| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/allocator/partition_allocator/partition_alloc_base/strings/cstring_builder.h" |
| |
| #include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" |
| #include "base/allocator/partition_allocator/partition_alloc_base/strings/safe_sprintf.h" |
| #include "build/build_config.h" |
| |
| #if !BUILDFLAG(IS_WIN) |
| #include <unistd.h> |
| #endif |
| |
| #include <cmath> |
| #include <cstring> |
| |
| #if BUILDFLAG(PA_DCHECK_IS_ON) |
| #include "base/allocator/partition_allocator/partition_alloc_base/check.h" |
| #define PA_RAW_DCHECK PA_RAW_CHECK |
| #else |
| #define PA_RAW_DCHECK(x) \ |
| do { \ |
| if (x) { \ |
| } \ |
| } while (0) |
| #endif |
| |
| namespace partition_alloc::internal::base::strings { |
| |
| namespace { |
| |
| constexpr size_t kNumDigits10 = 5u; |
| |
| constexpr uint64_t Pow10(unsigned exp) { |
| uint64_t ret = 1; |
| for (unsigned i = 0; i < exp; ++i) { |
| ret *= 10U; |
| } |
| return ret; |
| } |
| |
| constexpr uint64_t Log10(uint64_t value) { |
| uint64_t ret = 0; |
| while (value != 0u) { |
| value = value / 10u; |
| ++ret; |
| } |
| return ret; |
| } |
| |
| constexpr uint64_t GetDigits10(unsigned num_digits10) { |
| return Pow10(num_digits10); |
| } |
| |
| } // namespace |
| |
| template <typename T> |
| void CStringBuilder::PutInteger(T value) { |
| // We need an array of chars whose size is: |
| // - floor(log10(max value)) + 1 chars for the give value, and |
| // - 1 char for '-' (if negative) |
| // - 1 char for '\0' |
| char buffer[Log10(std::numeric_limits<T>::max()) + 3]; |
| ssize_t n = base::strings::SafeSPrintf(buffer, "%d", value); |
| PA_RAW_DCHECK(n >= 0); |
| PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer)); |
| PutText(buffer, n); |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(char ch) { |
| PutText(&ch, 1); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(const char* text) { |
| PutText(text); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(float value) { |
| PutFloatingPoint(value, kNumDigits10); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(double value) { |
| PutFloatingPoint(value, kNumDigits10); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(int value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(unsigned int value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(long value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(unsigned long value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(long long value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(unsigned long long value) { |
| PutInteger(value); |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(const void* value) { |
| if (!value) { |
| PutText("(nil)"); |
| } else { |
| // We need an array of chars whose size is: |
| // - 2 chars per 1 byte(00-FF), totally sizeof(const void*) * 2 chars, |
| // - 2 chars for "0x", |
| // - 1 char for '\0', |
| char buffer[sizeof(const void*) * 2 + 2 + 1]; |
| ssize_t n = base::strings::SafeSPrintf(buffer, "%p", value); |
| PA_RAW_DCHECK(n > 0); |
| PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer)); |
| PutText(buffer, n); |
| } |
| return *this; |
| } |
| |
| CStringBuilder& CStringBuilder::operator<<(std::nullptr_t) { |
| PutText("nullptr"); |
| return *this; |
| } |
| |
| const char* CStringBuilder::c_str() { |
| PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize); |
| if (*ptr_ != '\0') { |
| *ptr_ = '\0'; |
| } |
| return buffer_; |
| } |
| |
| void CStringBuilder::PutFloatingPoint(double value, unsigned num_digits10) { |
| switch (fpclassify(value)) { |
| case FP_INFINITE: |
| PutText(value < 0 ? "-inf" : "inf"); |
| break; |
| case FP_NAN: |
| PutText("NaN"); |
| break; |
| case FP_ZERO: |
| PutText("0"); |
| break; |
| case FP_SUBNORMAL: |
| // Denormalized values are not supported. |
| PutNormalFloatingPoint(value > 0 ? std::numeric_limits<double>::min() |
| : -std::numeric_limits<double>::min(), |
| num_digits10); |
| break; |
| case FP_NORMAL: |
| default: |
| PutNormalFloatingPoint(value, num_digits10); |
| break; |
| } |
| } |
| |
| void CStringBuilder::PutNormalFloatingPoint(double value, |
| unsigned num_digits10) { |
| if (value < 0) { |
| PutText("-", 1); |
| value = -value; |
| } |
| |
| int exponent = floor(log10(value)); |
| double significand = value / pow(10, exponent); |
| |
| char buffer[64]; |
| ssize_t n = base::strings::SafeSPrintf( |
| buffer, "%d", lrint(significand * GetDigits10(num_digits10))); |
| PA_RAW_DCHECK(n > 0); |
| PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer)); |
| PutText(buffer, 1); |
| if (n > 1) { |
| PutText(".", 1); |
| PutText(buffer + 1, n - 1); |
| } |
| if (exponent != 0) { |
| n = base::strings::SafeSPrintf(buffer, "e%s%d", exponent > 0 ? "+" : "", |
| exponent); |
| PA_RAW_DCHECK(n > 0); |
| PA_RAW_DCHECK(static_cast<size_t>(n) < sizeof(buffer)); |
| PutText(buffer, n); |
| } |
| } |
| |
| void CStringBuilder::PutText(const char* text) { |
| PutText(text, strlen(text)); |
| } |
| |
| void CStringBuilder::PutText(const char* text, size_t length) { |
| PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize); |
| while (ptr_ < buffer_ + kBufferSize - 1 && length > 0 && *text != '\0') { |
| *ptr_++ = *text++; |
| --length; |
| } |
| } |
| |
| } // namespace partition_alloc::internal::base::strings |