blob: 59225b6ebd6d034e3d2e2f77026254de7f38d293 [file] [log] [blame]
// 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 "partition_alloc/partition_alloc_base/strings/cstring_builder.h"
#include "build/build_config.h"
#include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
#include "partition_alloc/partition_alloc_base/strings/safe_sprintf.h"
#if !BUILDFLAG(IS_WIN)
#include <unistd.h>
#endif
#include <cmath>
#include <cstring>
#include <limits>
#if BUILDFLAG(PA_DCHECK_IS_ON)
#include "partition_alloc/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);
*ptr_ = '\0';
return buffer_;
}
void CStringBuilder::PutFloatingPoint(double value, unsigned num_digits10) {
switch (std::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