1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "partition_alloc/partition_alloc_base/strings/cstring_builder.h"
6
7 #include "build/build_config.h"
8 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
9 #include "partition_alloc/partition_alloc_base/strings/safe_sprintf.h"
10
11 #if !BUILDFLAG(IS_WIN)
12 #include <unistd.h>
13 #endif
14
15 #include <cmath>
16 #include <cstring>
17 #include <limits>
18
19 #if BUILDFLAG(PA_DCHECK_IS_ON)
20 #include "partition_alloc/partition_alloc_base/check.h"
21 #define PA_RAW_DCHECK PA_RAW_CHECK
22 #else
23 #define PA_RAW_DCHECK(x) \
24 do { \
25 if (x) { \
26 } \
27 } while (0)
28 #endif
29
30 namespace partition_alloc::internal::base::strings {
31
32 namespace {
33
34 constexpr size_t kNumDigits10 = 5u;
35
Pow10(unsigned exp)36 constexpr uint64_t Pow10(unsigned exp) {
37 uint64_t ret = 1;
38 for (unsigned i = 0; i < exp; ++i) {
39 ret *= 10U;
40 }
41 return ret;
42 }
43
Log10(uint64_t value)44 constexpr uint64_t Log10(uint64_t value) {
45 uint64_t ret = 0;
46 while (value != 0u) {
47 value = value / 10u;
48 ++ret;
49 }
50 return ret;
51 }
52
GetDigits10(unsigned num_digits10)53 constexpr uint64_t GetDigits10(unsigned num_digits10) {
54 return Pow10(num_digits10);
55 }
56
57 } // namespace
58
59 template <typename T>
PutInteger(T value)60 void CStringBuilder::PutInteger(T value) {
61 // We need an array of chars whose size is:
62 // - floor(log10(max value)) + 1 chars for the give value, and
63 // - 1 char for '-' (if negative)
64 // - 1 char for '\0'
65 char buffer[Log10(std::numeric_limits<T>::max()) + 3];
66 ssize_t n = base::strings::SafeSPrintf(buffer, "%d", value);
67 PA_RAW_DCHECK(n >= 0);
68 PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer));
69 PutText(buffer, n);
70 }
71
operator <<(char ch)72 CStringBuilder& CStringBuilder::operator<<(char ch) {
73 PutText(&ch, 1);
74 return *this;
75 }
76
operator <<(const char * text)77 CStringBuilder& CStringBuilder::operator<<(const char* text) {
78 PutText(text);
79 return *this;
80 }
81
operator <<(float value)82 CStringBuilder& CStringBuilder::operator<<(float value) {
83 PutFloatingPoint(value, kNumDigits10);
84 return *this;
85 }
86
operator <<(double value)87 CStringBuilder& CStringBuilder::operator<<(double value) {
88 PutFloatingPoint(value, kNumDigits10);
89 return *this;
90 }
91
operator <<(int value)92 CStringBuilder& CStringBuilder::operator<<(int value) {
93 PutInteger(value);
94 return *this;
95 }
96
operator <<(unsigned int value)97 CStringBuilder& CStringBuilder::operator<<(unsigned int value) {
98 PutInteger(value);
99 return *this;
100 }
101
operator <<(long value)102 CStringBuilder& CStringBuilder::operator<<(long value) {
103 PutInteger(value);
104 return *this;
105 }
106
operator <<(unsigned long value)107 CStringBuilder& CStringBuilder::operator<<(unsigned long value) {
108 PutInteger(value);
109 return *this;
110 }
111
operator <<(long long value)112 CStringBuilder& CStringBuilder::operator<<(long long value) {
113 PutInteger(value);
114 return *this;
115 }
116
operator <<(unsigned long long value)117 CStringBuilder& CStringBuilder::operator<<(unsigned long long value) {
118 PutInteger(value);
119 return *this;
120 }
121
operator <<(const void * value)122 CStringBuilder& CStringBuilder::operator<<(const void* value) {
123 if (!value) {
124 PutText("(nil)");
125 } else {
126 // We need an array of chars whose size is:
127 // - 2 chars per 1 byte(00-FF), totally sizeof(const void*) * 2 chars,
128 // - 2 chars for "0x",
129 // - 1 char for '\0',
130 char buffer[sizeof(const void*) * 2 + 2 + 1];
131 ssize_t n = base::strings::SafeSPrintf(buffer, "%p", value);
132 PA_RAW_DCHECK(n > 0);
133 PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer));
134 PutText(buffer, n);
135 }
136 return *this;
137 }
138
operator <<(std::nullptr_t)139 CStringBuilder& CStringBuilder::operator<<(std::nullptr_t) {
140 PutText("nullptr");
141 return *this;
142 }
143
c_str()144 const char* CStringBuilder::c_str() {
145 PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize);
146 *ptr_ = '\0';
147 return buffer_;
148 }
149
PutFloatingPoint(double value,unsigned num_digits10)150 void CStringBuilder::PutFloatingPoint(double value, unsigned num_digits10) {
151 switch (std::fpclassify(value)) {
152 case FP_INFINITE:
153 PutText(value < 0 ? "-inf" : "inf");
154 break;
155 case FP_NAN:
156 PutText("NaN");
157 break;
158 case FP_ZERO:
159 PutText("0");
160 break;
161 case FP_SUBNORMAL:
162 // Denormalized values are not supported.
163 PutNormalFloatingPoint(value > 0 ? std::numeric_limits<double>::min()
164 : -std::numeric_limits<double>::min(),
165 num_digits10);
166 break;
167 case FP_NORMAL:
168 default:
169 PutNormalFloatingPoint(value, num_digits10);
170 break;
171 }
172 }
173
PutNormalFloatingPoint(double value,unsigned num_digits10)174 void CStringBuilder::PutNormalFloatingPoint(double value,
175 unsigned num_digits10) {
176 if (value < 0) {
177 PutText("-", 1);
178 value = -value;
179 }
180
181 int exponent = floor(log10(value));
182 double significand = value / pow(10, exponent);
183
184 char buffer[64];
185 ssize_t n = base::strings::SafeSPrintf(
186 buffer, "%d", lrint(significand * GetDigits10(num_digits10)));
187 PA_RAW_DCHECK(n > 0);
188 PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer));
189 PutText(buffer, 1);
190 if (n > 1) {
191 PutText(".", 1);
192 PutText(buffer + 1, n - 1);
193 }
194 if (exponent != 0) {
195 n = base::strings::SafeSPrintf(buffer, "e%s%d", exponent > 0 ? "+" : "",
196 exponent);
197 PA_RAW_DCHECK(n > 0);
198 PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer));
199 PutText(buffer, n);
200 }
201 }
202
PutText(const char * text)203 void CStringBuilder::PutText(const char* text) {
204 PutText(text, strlen(text));
205 }
206
PutText(const char * text,size_t length)207 void CStringBuilder::PutText(const char* text, size_t length) {
208 PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize);
209 while (ptr_ < buffer_ + kBufferSize - 1 && length > 0 && *text != '\0') {
210 *ptr_++ = *text++;
211 --length;
212 }
213 }
214
215 } // namespace partition_alloc::internal::base::strings
216