428 changes: 272 additions & 156 deletions libc/src/__support/integer_to_string.h

Large diffs are not rendered by default.

20 changes: 9 additions & 11 deletions libc/src/__support/libc_assert.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,15 @@ namespace __llvm_libc {
LIBC_INLINE void report_assertion_failure(const char *assertion,
const char *filename, unsigned line,
const char *funcname) {
char line_str[IntegerToString::dec_bufsize<unsigned>()];
// dec returns an optional, will always be valid for this size buffer
auto line_number = IntegerToString::dec(line, line_str);
__llvm_libc::write_to_stderr(filename);
__llvm_libc::write_to_stderr(":");
__llvm_libc::write_to_stderr(*line_number);
__llvm_libc::write_to_stderr(": Assertion failed: '");
__llvm_libc::write_to_stderr(assertion);
__llvm_libc::write_to_stderr("' in function: '");
__llvm_libc::write_to_stderr(funcname);
__llvm_libc::write_to_stderr("'\n");
const IntegerToString<unsigned> line_buffer(line);
write_to_stderr(filename);
write_to_stderr(":");
write_to_stderr(line_buffer.view());
write_to_stderr(": Assertion failed: '");
write_to_stderr(assertion);
write_to_stderr("' in function: '");
write_to_stderr(funcname);
write_to_stderr("'\n");
}

} // namespace __llvm_libc
Expand Down
2 changes: 1 addition & 1 deletion libc/src/__support/threads/linux/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ bool Thread::operator==(const Thread &thread) const {
static constexpr cpp::string_view THREAD_NAME_PATH_PREFIX("/proc/self/task/");
static constexpr size_t THREAD_NAME_PATH_SIZE =
THREAD_NAME_PATH_PREFIX.size() +
IntegerToString::dec_bufsize<int>() + // Size of tid
IntegerToString<int>::buffer_size() + // Size of tid
1 + // For '/' character
5; // For the file name "comm" and the nullterminator.

Expand Down
100 changes: 28 additions & 72 deletions libc/src/stdio/printf_core/float_dec_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ namespace __llvm_libc {
namespace printf_core {

using MantissaInt = fputil::FPBits<long double>::UIntType;
using DecimalString = IntegerToString<intmax_t>;
using ExponentString =
IntegerToString<intmax_t, radix::Dec::WithWidth<2>::WithSign>;

// Returns true if value is divisible by 2^p.
template <typename T>
Expand Down Expand Up @@ -193,39 +196,11 @@ class FloatWriter {
return 0;
}

cpp::string_view exp_str(int exponent, cpp::span<char> exp_buffer) {

// -exponent will never overflow because all long double types we support
// have at most 15 bits of mantissa and the C standard defines an int as
// being at least 16 bits.
static_assert(fputil::FloatProperties<long double>::EXPONENT_WIDTH <
(sizeof(int) * 8));

int positive_exponent = exponent < 0 ? -exponent : exponent;
char exp_sign = exponent < 0 ? '-' : '+';
auto const int_to_str =
*IntegerToString::dec(positive_exponent, exp_buffer);

// IntegerToString writes the digits from right to left so there will be
// space to the left of int_to_str.
size_t digits_in_exp = int_to_str.size();
size_t index = exp_buffer.size() - digits_in_exp - 1;

// Ensure that at least two digits were written. IntegerToString always
// writes at least 1 digit (it writes "0" when the input number is 0).
if (digits_in_exp < 2) {
exp_buffer[index] = '0';
--index;
}

// Since the exp_buffer has to be sized to handle an intmax_t, it has space
// for a sign. In this case we're handling the sign on our own since we also
// want plus signs for positive numbers.
exp_buffer[index] = exp_sign;

return cpp::string_view(exp_buffer.data() + index,
exp_buffer.size() - index);
}
// -exponent will never overflow because all long double types we support
// have at most 15 bits of mantissa and the C standard defines an int as
// being at least 16 bits.
static_assert(fputil::FloatProperties<long double>::EXPONENT_WIDTH <
(sizeof(int) * 8));

public:
FloatWriter(Writer *init_writer, bool init_has_decimal_point,
Expand All @@ -239,8 +214,8 @@ class FloatWriter {
}

void write_first_block(BlockInt block, bool exp_format = false) {
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str = *IntegerToString::dec(block, buf);
const DecimalString buf(block);
const cpp::string_view int_to_str = buf.view();
size_t digits_buffered = int_to_str.size();
// Block Buffer is guaranteed to not overflow since block cannot have more
// than BLOCK_SIZE digits.
Expand Down Expand Up @@ -268,9 +243,8 @@ class FloatWriter {
// Now buffer the current block. We add 1 + MAX_BLOCK to force the
// leading zeroes, and drop the leading one. This is probably inefficient,
// but it works. See https://xkcd.com/2021/
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str =
*IntegerToString::dec(block + (MAX_BLOCK + 1), buf);
const DecimalString buf(block + (MAX_BLOCK + 1));
const cpp::string_view int_to_str = buf.view();
// TODO: Replace with memcpy
for (size_t count = 0; count < BLOCK_SIZE; ++count) {
block_buffer[count] = int_to_str[count + 1];
Expand All @@ -285,8 +259,8 @@ class FloatWriter {
RoundDirection round) {
char end_buff[BLOCK_SIZE];

char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str = *IntegerToString::dec(block + (MAX_BLOCK + 1), buf);
const DecimalString buf(block + (MAX_BLOCK + 1));
const cpp::string_view int_to_str = buf.view();

// copy the last block_digits characters into the start of end_buff.
// TODO: Replace with memcpy
Expand Down Expand Up @@ -372,9 +346,8 @@ class FloatWriter {
char end_buff[BLOCK_SIZE];

{
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str =
*IntegerToString::dec(block + (MAX_BLOCK + 1), buf);
const DecimalString buf(block + (MAX_BLOCK + 1));
const cpp::string_view int_to_str = buf.view();

// copy the last block_digits characters into the start of end_buff.
// TODO: Replace with memcpy
Expand Down Expand Up @@ -424,8 +397,8 @@ class FloatWriter {
// but we do increment the exponent.
++exponent;

char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str = exp_str(exponent, buf);
const ExponentString buf(exponent);
const cpp::string_view int_to_str = buf.view();

// TODO: also change this to calculate the width of the number more
// efficiently.
Expand Down Expand Up @@ -479,11 +452,9 @@ class FloatWriter {
buffered_digits = block_digits;
RET_IF_RESULT_NEGATIVE(flush_buffer());

char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto const int_to_str = exp_str(exponent, buf);

RET_IF_RESULT_NEGATIVE(writer->write(exp_char));
RET_IF_RESULT_NEGATIVE(writer->write(int_to_str));
const ExponentString buf(exponent);
RET_IF_RESULT_NEGATIVE(writer->write(buf.view()));

total_digits_written = total_digits;

Expand Down Expand Up @@ -707,17 +678,12 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
cur_block = 0;
}

// TODO: Find a better way to calculate the number of digits in the
// initial block and exponent.
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
size_t block_width = int_to_str.size();
const size_t block_width = IntegerToString<intmax_t>(digits).size();

final_exponent = (cur_block * BLOCK_SIZE) + static_cast<int>(block_width - 1);
int positive_exponent = final_exponent < 0 ? -final_exponent : final_exponent;

int_to_str = *IntegerToString::dec(positive_exponent, buf);
size_t exponent_width = int_to_str.size();
size_t exponent_width = IntegerToString<intmax_t>(positive_exponent).size();

// Calculate the total number of digits in the number.
// 1 - the digit before the decimal point
Expand Down Expand Up @@ -752,10 +718,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,

// if the last block is also the first block, then ignore leading zeroes.
if (digits_written == 0) {
// TODO: Find a better way to calculate the number of digits in a block.
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
last_block_size = int_to_str.size();
last_block_size = IntegerToString<intmax_t>(digits).size();
}

// This is the last block.
Expand Down Expand Up @@ -881,14 +844,7 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
return convert_float_decimal_typed<T>(writer, new_conv, float_bits);
}

size_t block_width = 0;
{
// TODO: Find a better way to calculate the number of digits in the
// initial block and exponent.
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
block_width = int_to_str.size();
}
const size_t block_width = IntegerToString<intmax_t>(digits).size();

size_t digits_checked = 0;
// TODO: look into unifying trailing_zeroes and trailing_nines. The number can
Expand All @@ -900,8 +856,8 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,

// If the first block is not also the last block
if (block_width <= exp_precision + 1) {
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
const DecimalString buf(digits);
const cpp::string_view int_to_str = buf.view();

for (size_t i = 0; i < block_width; ++i) {
if (int_to_str[i] == '9') {
Expand Down Expand Up @@ -962,8 +918,8 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,

size_t last_block_size = BLOCK_SIZE;

char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
const DecimalString buf(digits);
const cpp::string_view int_to_str = buf.view();

size_t implicit_leading_zeroes = BLOCK_SIZE - int_to_str.size();

Expand Down
31 changes: 26 additions & 5 deletions libc/src/stdio/printf_core/int_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,38 @@ namespace printf_core {
LIBC_INLINE constexpr char to_lower(char a) { return a | 32; }
LIBC_INLINE constexpr bool is_lower(char a) { return (a & 32) > 0; }

namespace details {

using HexFmt = IntegerToString<uintmax_t, radix::Hex>;
using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>;
using OctFmt = IntegerToString<uintmax_t, radix::Oct>;
using DecFmt = IntegerToString<uintmax_t>;

LIBC_INLINE constexpr size_t num_buf_size() {
constexpr auto max = [](size_t a, size_t b) -> size_t {
return (a < b) ? b : a;
};
return max(HexFmt::buffer_size(),
max(HexFmtUppercase::buffer_size(),
max(OctFmt::buffer_size(), DecFmt::buffer_size())));
}

LIBC_INLINE cpp::optional<cpp::string_view>
num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
if (to_lower(conv_name) == 'x') {
return IntegerToString::hex(num, bufref, is_lower(conv_name));
if (is_lower(conv_name))
return HexFmt::format_to(bufref, num);
else
return HexFmtUppercase::format_to(bufref, num);
} else if (conv_name == 'o') {
return IntegerToString::oct(num, bufref);
return OctFmt::format_to(bufref, num);
} else {
return IntegerToString::dec(num, bufref);
return DecFmt::format_to(bufref, num);
}
}

} // namespace details

LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
static constexpr size_t BITS_IN_BYTE = 8;
static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
Expand Down Expand Up @@ -66,8 +87,8 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {

num = apply_length_modifier(num, to_conv.length_modifier);

char buf[IntegerToString::oct_bufsize<intmax_t>()];
auto str = num_to_strview(num, buf, to_conv.conv_name);
cpp::array<char, details::num_buf_size()> buf;
auto str = details::num_to_strview(num, buf, to_conv.conv_name);
if (!str)
return INT_CONVERSION_ERROR;

Expand Down
5 changes: 2 additions & 3 deletions libc/test/UnitTest/LibcTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ cpp::enable_if_t<cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t)),
cpp::string>
describeValue(T Value) {
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
char buf[IntegerToString::hex_bufsize<T>()];
IntegerToString::hex(Value, buf, false);
return "0x" + cpp::string(buf, sizeof(buf));
const IntegerToString<T, radix::Hex::WithPrefix> buffer(Value);
return buffer.view();
}

// When the value is of a standard integral type, just display it as normal.
Expand Down
5 changes: 2 additions & 3 deletions libc/test/UnitTest/TestLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ template <typename T> TestLogger &TestLogger::operator<<(T t) {
if constexpr (cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
sizeof(T) > sizeof(uint64_t)) {
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
char buf[IntegerToString::hex_bufsize<T>()];
IntegerToString::hex(t, buf, false);
return *this << "0x" << cpp::string_view(buf, sizeof(buf));
const IntegerToString<T, radix::Hex::WithPrefix> buffer(t);
return *this << buffer.view();
} else {
return *this << cpp::to_string(t);
}
Expand Down
475 changes: 245 additions & 230 deletions libc/test/src/__support/integer_to_string_test.cpp

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ libc_support_library(
hdrs = ["src/__support/integer_to_string.h"],
deps = [
":__support_common",
":__support_cpp_bit",
":__support_cpp_limits",
":__support_cpp_optional",
":__support_cpp_span",
":__support_cpp_string_view",
Expand Down