Skip to content

Commit

Permalink
[PA] Add CStringBuilder to replace std::ostringstream used by LogMess…
Browse files Browse the repository at this point in the history
…age.

Since std::ostringstream allocates and deallocates memory from heap, c.f. https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:buildtools/third_party/libc++/trunk/src/ios.cpp

std::ostringstream is not available inside memory allocation. Instead
add CStringBuilder (not resize, fixed buffer size) for LogMessage.

Change-Id: I8051978487acc5fc9b976d6085909b43f81d9d0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4744311
Reviewed-by: Yuki Shiino <[email protected]>
Commit-Queue: Takashi Sakamoto <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1179481}
  • Loading branch information
tasak authored and Chromium LUCI CQ committed Aug 4, 2023
1 parent e5eb131 commit 07a88f3
Show file tree
Hide file tree
Showing 5 changed files with 421 additions and 0 deletions.
1 change: 1 addition & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3802,6 +3802,7 @@ test("base_unittests") {
"allocator/partition_allocator/partition_alloc_base/logging_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/rand_util_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/strings/cstring_builder_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/strings/safe_sprintf_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/strings/stringprintf_pa_unittest.cc",
"allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.cc",
Expand Down
2 changes: 2 additions & 0 deletions base/allocator/partition_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ source_set("allocator_base") {
"partition_alloc_base/rand_util.cc",
"partition_alloc_base/rand_util.h",
"partition_alloc_base/scoped_clear_last_error.h",
"partition_alloc_base/strings/cstring_builder.cc",
"partition_alloc_base/strings/cstring_builder.h",
"partition_alloc_base/strings/safe_sprintf.cc",
"partition_alloc_base/strings/safe_sprintf.h",
"partition_alloc_base/strings/stringprintf.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_

#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
#include "build/build_config.h"

#include <cstddef>

#if !BUILDFLAG(IS_WIN)
#include <unistd.h>
#endif

namespace partition_alloc::internal::base::strings {

// Similar to std::ostringstream, but creates a C string, i.e. nul-terminated
// char-type string, instead of std::string. To use inside memory allocation,
// this method must not allocate any memory with malloc, aligned_malloc,
// calloc, and so on.
class PA_COMPONENT_EXPORT(PARTITION_ALLOC) CStringBuilder {
public:
static constexpr size_t kBufferSize = 1024u;

CStringBuilder() : ptr_(buffer_) {}

CStringBuilder& operator<<(char ch);
CStringBuilder& operator<<(const char* text);
CStringBuilder& operator<<(float value);
CStringBuilder& operator<<(double value);
CStringBuilder& operator<<(int value);
CStringBuilder& operator<<(unsigned int value);
CStringBuilder& operator<<(long value);
CStringBuilder& operator<<(unsigned long value);
CStringBuilder& operator<<(long long value);
CStringBuilder& operator<<(unsigned long long value);
CStringBuilder& operator<<(const void* value);
CStringBuilder& operator<<(std::nullptr_t);
const char* c_str();

private:
template <typename T>
void PutInteger(T value);
void PutFloatingPoint(double value, unsigned num_digits10);
void PutNormalFloatingPoint(double value, unsigned num_digits10);
void PutText(const char* text);
void PutText(const char* text, size_t length);

char buffer_[kBufferSize];
char* ptr_;
};

} // namespace partition_alloc::internal::base::strings

#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_
Loading

0 comments on commit 07a88f3

Please sign in to comment.