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