diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -90,10 +90,11 @@ HDRS integer_to_string.h DEPENDS + libc.src.__support.common + libc.src.__support.CPP.limits libc.src.__support.CPP.span libc.src.__support.CPP.string_view libc.src.__support.CPP.type_traits - libc.src.__support.common ) diff --git a/libc/src/__support/CPP/string.h b/libc/src/__support/CPP/string.h --- a/libc/src/__support/CPP/string.h +++ b/libc/src/__support/CPP/string.h @@ -195,10 +195,8 @@ namespace internal { template string to_dec_string(T value) { - char dec_buf[IntegerToString::dec_bufsize()]; - auto maybe_string_view = IntegerToString::dec(value, dec_buf); - const auto &string_view = *maybe_string_view; - return string(string_view.data(), string_view.size()); + const IntegerToString buffer(value); + return buffer.view(); } } // namespace internal diff --git a/libc/src/__support/CPP/stringstream.h b/libc/src/__support/CPP/stringstream.h --- a/libc/src/__support/CPP/stringstream.h +++ b/libc/src/__support/CPP/stringstream.h @@ -9,8 +9,8 @@ #ifndef LLVM_LIBC_SRC_SUPPORT_CPP_STRINGSTREAM_H #define LLVM_LIBC_SRC_SUPPORT_CPP_STRINGSTREAM_H -#include "string_view.h" #include "span.h" +#include "string_view.h" #include "type_traits.h" #include "src/__support/integer_to_string.h" @@ -58,11 +58,8 @@ // Write the |val| as string. template , int> = 0> StringStream &operator<<(T val) { - char buffer[IntegerToString::dec_bufsize()]; - auto int_to_str = IntegerToString::dec(val, buffer); - if (int_to_str) - return operator<<(*int_to_str); - return *this; + const IntegerToString buffer(val); + return *this << buffer.view(); } template , int> = 0> diff --git a/libc/src/__support/FPUtil/fpbits_str.h b/libc/src/__support/FPUtil/fpbits_str.h --- a/libc/src/__support/FPUtil/fpbits_str.h +++ b/libc/src/__support/FPUtil/fpbits_str.h @@ -18,6 +18,15 @@ namespace __llvm_libc { +namespace details { + +// Format T as uppercase hexadecimal number with leading zeros. +template +using ZeroPaddedHexFmt = IntegerToString< + T, typename radix::Hex::WithWidth<(sizeof(T) * 2)>::WithPrefix::Uppercase>; + +} // namespace details + // Converts the bits to a string in the following format: // "0x = S: N, E: 0xNNNN, M:0xNNN...N" // 1. N is a hexadecimal digit. @@ -33,36 +42,31 @@ if (x.is_inf()) return x.get_sign() ? "(-Infinity)" : "(+Infinity)"; - auto zerofill = [](char *arr, size_t n) { - for (size_t i = 0; i < n; ++i) - arr[i] = '0'; - }; + const auto sign_char = [](bool sign) -> char { return sign ? '1' : '0'; }; + + cpp::string s; - cpp::string s("0x"); - char bitsbuf[IntegerToString::hex_bufsize()]; - zerofill(bitsbuf, sizeof(bitsbuf)); - IntegerToString::hex(x.bits, bitsbuf, false); - s += cpp::string(bitsbuf, sizeof(bitsbuf)); + const details::ZeroPaddedHexFmt bits(x.bits); + s += bits.view(); - s += " = ("; - s += cpp::string("S: ") + (x.get_sign() ? "1" : "0"); + s += " = (S: "; + s += sign_char(x.get_sign()); - char expbuf[IntegerToString::hex_bufsize()]; - zerofill(expbuf, sizeof(expbuf)); - IntegerToString::hex(x.get_unbiased_exponent(), expbuf, false); - s += cpp::string(", E: 0x") + cpp::string(expbuf, sizeof(expbuf)); + s += ", E: "; + const details::ZeroPaddedHexFmt exponent(x.get_unbiased_exponent()); + s += exponent.view(); if constexpr (cpp::is_same_v && fputil::FloatProperties::MANTISSA_WIDTH == 63) { - s += cpp::string(", I: ") + (x.get_implicit_bit() ? "1" : "0"); + s += ", I: "; + s += sign_char(x.get_implicit_bit()); } - char mantbuf[IntegerToString::hex_bufsize()] = {'0'}; - zerofill(mantbuf, sizeof(mantbuf)); - IntegerToString::hex(x.get_mantissa(), mantbuf, false); - s += cpp::string(", M: 0x") + cpp::string(mantbuf, sizeof(mantbuf)); + s += ", M: "; + const details::ZeroPaddedHexFmt mantissa(x.get_mantissa()); + s += mantissa.view(); - s += ")"; + s += ')'; return s; } diff --git a/libc/src/__support/StringUtil/error_to_string.cpp b/libc/src/__support/StringUtil/error_to_string.cpp --- a/libc/src/__support/StringUtil/error_to_string.cpp +++ b/libc/src/__support/StringUtil/error_to_string.cpp @@ -23,10 +23,9 @@ constexpr size_t max_buff_size() { constexpr size_t unknown_str_len = sizeof("Unknown error"); - constexpr size_t max_num_len = - __llvm_libc::IntegerToString::dec_bufsize(); // the buffer should be able to hold "Unknown error" + ' ' + num_str - return (unknown_str_len + 1 + max_num_len) * sizeof(char); + return (unknown_str_len + 1 + IntegerToString::buffer_size()) * + sizeof(char); } // This is to hold error strings that have to be custom built. It may be @@ -51,7 +50,7 @@ // if the buffer can't hold "Unknown error" + ' ' + num_str, then just // return "Unknown error". if (buffer.size() < - (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize())) + (sizeof("Unknown error") + 1 + IntegerToString::buffer_size())) return const_cast("Unknown error"); cpp::StringStream buffer_stream( diff --git a/libc/src/__support/StringUtil/signal_to_string.cpp b/libc/src/__support/StringUtil/signal_to_string.cpp --- a/libc/src/__support/StringUtil/signal_to_string.cpp +++ b/libc/src/__support/StringUtil/signal_to_string.cpp @@ -24,10 +24,9 @@ constexpr size_t max_buff_size() { constexpr size_t base_str_len = sizeof("Real-time signal"); - constexpr size_t max_num_len = - __llvm_libc::IntegerToString::dec_bufsize(); // the buffer should be able to hold "Real-time signal" + ' ' + num_str - return (base_str_len + 1 + max_num_len) * sizeof(char); + return (base_str_len + 1 + IntegerToString::buffer_size()) * + sizeof(char); } // This is to hold signal strings that have to be custom built. It may be @@ -54,7 +53,7 @@ // if the buffer can't hold "Unknown signal" + ' ' + num_str, then just // return "Unknown signal". if (buffer.size() < - (base_str.size() + 1 + IntegerToString::dec_bufsize())) + (base_str.size() + 1 + IntegerToString::buffer_size())) return base_str; cpp::StringStream buffer_stream( diff --git a/libc/src/__support/integer_to_string.h b/libc/src/__support/integer_to_string.h --- a/libc/src/__support/integer_to_string.h +++ b/libc/src/__support/integer_to_string.h @@ -5,12 +5,63 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// Converts an integer to a string. +// +// By default, the string is written as decimal to an internal buffer and +// accessed via the 'view' method. +// +// IntegerToString buffer(42); +// cpp::string_view view = buffer.view(); +// +// The buffer is allocated on the stack and its size is so that the conversion +// always succeeds. +// +// It is also possible to write the data to a preallocated buffer, but this may +// fail. +// +// char buffer[8]; +// if (auto maybe_view = IntegerToString::write_to_span(buffer, 42)) { +// cpp::string_view view = *maybe_view; +// } +// +// The first template parameter is the type of the integer. +// The second template parameter defines how the integer is formatted. +// Available default are 'radix::Bin', 'radix::Oct', 'radix::Dec' and +// 'radix::Hex'. +// +// For 'radix::Bin', 'radix::Oct' and 'radix::Hex' the value is always +// interpreted as a positive type but 'radix::Dec' will honor negative values. +// e.g., +// +// IntegerToString(-1) // "-1" +// IntegerToString(-1) // "-1" +// IntegerToString(-1) // "11111111" +// IntegerToString(-1) // "377" +// IntegerToString(-1) // "ff" +// +// Additionnally, the format can be changed by navigating the subtypes: +// - WithPrefix : Adds "0b", "0", "0x" for binary, octal and hexadecimal +// - WithWidth : Pad string to XX characters filling leading digits with 0 +// - Uppercase : Use uppercase letters (only for HexString) +// - WithSign : Prepend '+' for positive values (only for DecString) +// +// Examples +// -------- +// IntegerToString::WithSign>(0) : "+00" +// IntegerToString::WithSign>(-1) : "-01" +// IntegerToString(255) : "0xFF" +// IntegerToString::Uppercase>(255) : "00FF" +//===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H #define LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H #include +#include "src/__support/CPP/array.h" +#include "src/__support/CPP/bit.h" +#include "src/__support/CPP/limits.h" #include "src/__support/CPP/optional.h" #include "src/__support/CPP/span.h" #include "src/__support/CPP/string_view.h" @@ -19,186 +70,251 @@ namespace __llvm_libc { -// Convert integer values to their string representation. -// -// Example usage: -// int a = 1234567; -// -// // Convert to hexadecimal string: -// char hexbuf[IntegerToString::hex_bufsize()]; -// auto str = IntegerToString::hex( -// a, hexbuf, false /* generate upper case characters */); -// -// // Convert to decimal string: -// char decbuf[IntegerToString::dec_bufsize()]; -// auto str = IntegerToString::dec(a, decbuf); -// -// // Convert to octal string: -// char octbuf[IntegerToString::oct_bufsize(a)]; -// auto str = IntegerToString::dec(a, octbuf); -// -// // Convert to binary string: -// char binbuf[IntegerToString::bin_bufsize(a)]; -// auto str = IntegerToString::bin(a, binbuf); -// -// // Convert to base 30 string: -// char b30buf[IntegerToString::bufsize<30, int>(a)]; -// auto str = IntegerToString::convert<30>(a, b30buf); -class IntegerToString { - LIBC_INLINE static cpp::string_view convert_uintmax(uintmax_t uval, - cpp::span &buffer, - bool lowercase, - const uint8_t conv_base) { - const char a = lowercase ? 'a' : 'A'; - - size_t len = 0; - - size_t buffptr = buffer.size(); - if (uval == 0) { - buffer[buffptr - 1] = '0'; - --buffptr; - } else { - for (; uval > 0; --buffptr, uval /= conv_base) { - uintmax_t digit = (uval % conv_base); - buffer[buffptr - 1] = static_cast(digit < 10 ? digit + '0' : digit + a - 10); - } - } - len = buffer.size() - buffptr; +namespace details { - return cpp::string_view(buffer.data() + buffer.size() - len, len); - } +template +struct Fmt { + static constexpr uint8_t BASE = base; + static constexpr size_t MIN_DIGITS = min_digits; + static constexpr bool IS_UPPERCASE = is_uppercase; + static constexpr bool PREFIX = prefix; + static constexpr char FORCE_SIGN = force_sign; - LIBC_INLINE static cpp::string_view convert_intmax(intmax_t val, - cpp::span &buffer, - bool lowercase, - const uint8_t conv_base) { - if (val >= 0) - return convert_uintmax(uintmax_t(val), buffer, lowercase, conv_base); - uintmax_t uval = uintmax_t(-val); - auto str_view = convert_uintmax(uval, buffer, lowercase, conv_base); - size_t len = str_view.size(); - ++len; - buffer[buffer.size() - len] = '-'; - return cpp::string_view(buffer.data() + buffer.size() - len, len); - } + using WithPrefix = Fmt; + using WithSign = Fmt; + using Uppercase = Fmt; + template + using WithWidth = Fmt; - LIBC_INLINE static constexpr size_t floor_log_2(size_t num) { - size_t i = 0; - for (; num > 1; num /= 2) { - ++i; - } - return i; + // Invariants + static constexpr uint8_t NUMERICAL_DIGITS = 10; + static constexpr uint8_t ALPHA_DIGITS = 26; + static constexpr uint8_t MAX_DIGIT = NUMERICAL_DIGITS + ALPHA_DIGITS; + static_assert(BASE > 1 && BASE <= MAX_DIGIT); + static_assert(!IS_UPPERCASE || BASE > 10, "Uppercase is only for radix > 10"); + static_assert(!FORCE_SIGN || BASE == 10, "WithSign is only for radix == 10"); + static_assert(!PREFIX || (BASE == 2 || BASE == 8 || BASE == 16), + "WithPrefix is only for radix == 2, 8 or 16"); +}; + +// Move this to a separate header since it might be useful elsewhere. +template class StringBufferWriterImpl { + cpp::span buffer; + size_t index = 0; + bool out_of_range = false; + + LIBC_INLINE size_t location() const { + return forward ? index : buffer.size() - 1 - index; } public: - // We size the string buffer for base 10 using an approximation algorithm: - // - // size = ceil(sizeof(T) * 5 / 2) - // - // If sizeof(T) is 1, then size is 3 (actually need 3) - // If sizeof(T) is 2, then size is 5 (actually need 5) - // If sizeof(T) is 4, then size is 10 (actually need 10) - // If sizeof(T) is 8, then size is 20 (actually need 20) - // If sizeof(T) is 16, then size is 40 (actually need 39) - // - // NOTE: The ceil operation is actually implemented as - // floor(((sizeof(T) * 5) + 1)/2) - // where floor operation is just integer division. - // - // This estimation grows slightly faster than the actual value, but the - // overhead is small enough to tolerate. In the actual formula below, we - // add an additional byte to accommodate the '-' sign in case of signed - // integers. - // For other bases, we approximate by rounding down to the nearest power of - // two base, since the space needed is easy to calculate and it won't - // overestimate by too much. - template - LIBC_INLINE static constexpr size_t bufsize() { - constexpr size_t BITS_PER_DIGIT = floor_log_2(BASE); - constexpr size_t BUFSIZE_COMMON = - ((sizeof(T) * 8 + (BITS_PER_DIGIT - 1)) / BITS_PER_DIGIT); - constexpr size_t BUFSIZE_BASE10 = (sizeof(T) * 5 + 1) / 2; - return (cpp::is_signed() ? 1 : 0) + - (BASE == 10 ? BUFSIZE_BASE10 : BUFSIZE_COMMON); - } + StringBufferWriterImpl(const StringBufferWriterImpl &) = delete; + StringBufferWriterImpl(cpp::span buffer) : buffer(buffer) {} - template LIBC_INLINE static constexpr size_t dec_bufsize() { - return bufsize<10, T>(); - } + LIBC_INLINE size_t size() const { return index; } + LIBC_INLINE size_t remainder_size() const { return buffer.size() - size(); } + LIBC_INLINE bool empty() const { return size() == 0; } + LIBC_INLINE bool full() const { return size() == buffer.size(); } + LIBC_INLINE bool ok() const { return !out_of_range; } - template LIBC_INLINE static constexpr size_t hex_bufsize() { - return bufsize<16, T>(); + LIBC_INLINE StringBufferWriterImpl &push(char c) { + if (ok()) { + if (!full()) { + buffer[location()] = c; + ++index; + } else { + out_of_range = true; + } + } + return *this; } - template LIBC_INLINE static constexpr size_t oct_bufsize() { - return bufsize<8, T>(); + LIBC_INLINE cpp::span remainder_span() const { + return forward ? buffer.last(remainder_size()) + : buffer.first(remainder_size()); } - template LIBC_INLINE static constexpr size_t bin_bufsize() { - return bufsize<2, T>(); + LIBC_INLINE cpp::span buffer_span() const { + return forward ? buffer.first(size()) : buffer.last(size()); } - template , - int> = 0> - LIBC_INLINE static cpp::optional - convert(T val, cpp::span buffer, bool lowercase = true) { - if (buffer.size() < bufsize()) - return cpp::optional(); - if (cpp::is_signed_v) - return convert_intmax(intmax_t(val), buffer, lowercase, BASE); - else - return convert_uintmax(uintmax_t(val), buffer, lowercase, BASE); + LIBC_INLINE cpp::string_view buffer_view() const { + const auto s = buffer_span(); + return {s.data(), s.size()}; } +}; - template , int> = 0> - LIBC_INLINE static cpp::optional - dec(T val, cpp::span buffer) { - return convert<10>(val, buffer); - } +using StringBufferWriter = StringBufferWriterImpl; +using BackwardStringBufferWriter = StringBufferWriterImpl; + +} // namespace details + +namespace radix { + +using Bin = details::Fmt<2>; +using Oct = details::Fmt<8>; +using Dec = details::Fmt<10>; +using Hex = details::Fmt<16>; +template using Custom = details::Fmt; - template && - (sizeof(T) <= sizeof(uintmax_t)), - int> = 0> - LIBC_INLINE static cpp::optional - hex(T val, cpp::span buffer, bool lowercase = true) { - return convert<16>(val, buffer, lowercase); +} // namespace radix + +// See file header for documentation. +template class IntegerToString { + static_assert(cpp::is_integral_v); + + LIBC_INLINE static constexpr size_t compute_buffer_size() { + constexpr auto max_digits = []() -> size_t { + // We size the string buffer for base 10 using an approximation algorithm: + // + // size = ceil(sizeof(T) * 5 / 2) + // + // If sizeof(T) is 1, then size is 3 (actually need 3) + // If sizeof(T) is 2, then size is 5 (actually need 5) + // If sizeof(T) is 4, then size is 10 (actually need 10) + // If sizeof(T) is 8, then size is 20 (actually need 20) + // If sizeof(T) is 16, then size is 40 (actually need 39) + // + // NOTE: The ceil operation is actually implemented as + // floor(((sizeof(T) * 5) + 1) / 2) + // where floor operation is just integer division. + // + // This estimation grows slightly faster than the actual value, but the + // overhead is small enough to tolerate. + if constexpr (Fmt::BASE == 10) + return ((sizeof(T) * 5) + 1) / 2; + // For other bases, we approximate by rounding down to the nearest power + // of two base, since the space needed is easy to calculate and it won't + // overestimate by too much. + constexpr auto floor_log_2 = [](size_t num) -> size_t { + size_t i = 0; + for (; num > 1; num /= 2) + ++i; + return i; + }; + constexpr size_t BITS_PER_DIGIT = floor_log_2(Fmt::BASE); + return ((sizeof(T) * 8 + (BITS_PER_DIGIT - 1)) / BITS_PER_DIGIT); + }; + constexpr auto max = [](size_t a, size_t b) -> size_t { + return a > b ? a : b; + }; + constexpr size_t digit_size = max(max_digits(), Fmt::MIN_DIGITS); + constexpr size_t sign_size = Fmt::BASE == 10 ? 1 : 0; + constexpr size_t prefix_size = Fmt::PREFIX ? 2 : 0; + return digit_size + sign_size + prefix_size; } - template && - (sizeof(T) > sizeof(uintmax_t)) && - sizeof(T) % sizeof(uintmax_t) == 0, - int> = 0> - LIBC_INLINE static cpp::optional - hex(T val, cpp::span buffer, bool lowercase = true) { - // We will assume the buffer is exactly sized, which will be the case if - // it was sized using the bufsize method. - constexpr size_t BLOCKS = sizeof(T) / sizeof(uintmax_t); - constexpr size_t UINTMAX_BUFSIZE = bufsize<16, uintmax_t>(); - // We will zero out the buffer. This specialization is not used to - // implement a public function so zeroing out byte-by-byte does not - // have any affect on runtime or user expectations. - for (size_t i = 0; i < buffer.size(); ++i) - buffer[i] = '0'; - for (size_t i = 0; i < BLOCKS; ++i, val >>= (sizeof(uintmax_t) * 8)) { - uintmax_t block_val = static_cast(val); - hex(block_val, - buffer.subspan((BLOCKS - i - 1) * UINTMAX_BUFSIZE, UINTMAX_BUFSIZE), - lowercase); + static constexpr size_t BUFFER_SIZE = compute_buffer_size(); + static_assert(BUFFER_SIZE > 0); + + // An internal stateless structure that handles the number formatting logic. + struct IntegerWriter { + static_assert(cpp::is_integral_v); + using UNSIGNED_T = cpp::make_unsigned_t; + + LIBC_INLINE static char digit_char(uint8_t digit) { + if (digit < 10) + return '0' + digit; + return (Fmt::IS_UPPERCASE ? 'A' : 'a') + (digit - 10); + } + + LIBC_INLINE static void + write_unsigned_number(UNSIGNED_T value, + details::BackwardStringBufferWriter &sink) { + for (; sink.ok() && value != 0; value /= Fmt::BASE) { + const uint8_t digit(value % Fmt::BASE); + sink.push(digit_char(digit)); + } + } + + // Returns the absolute value of 'value' as 'UNSIGNED_T'. + LIBC_INLINE static UNSIGNED_T abs(T value) { + if (cpp::is_unsigned_v || value >= 0) + return value; // already of the right sign. + + // Signed integers are asymmetric (e.g., int8_t ∈ [-128, 127]). + // Thus negating the type's minimum value would overflow. + // From C++20 on, signed types are guaranteed to be represented as 2's + // complement. We take advantage of this representation and negate the + // value by using the exact same bit representation, e.g., + // binary : 0b1000'0000 + // int8_t : -128 + // uint8_t: 128 + + // Note: the compiler can completely optimize out the two branches and + // replace them by a simple negate instruction. + // https://godbolt.org/z/hE7zahT9W + if (value == cpp::numeric_limits::min()) { + return cpp::bit_cast(value); + } else { + return -value; // legal and representable both as T and UNSIGNED_T.` + } + } + + LIBC_INLINE static void write(T value, + details::BackwardStringBufferWriter &sink) { + if constexpr (Fmt::BASE == 10) { + write_unsigned_number(abs(value), sink); + } else { + write_unsigned_number(cpp::bit_cast(value), sink); + } + // width + while (sink.ok() && sink.size() < Fmt::MIN_DIGITS) + sink.push('0'); + // sign + if constexpr (Fmt::BASE == 10) { + if (value < 0) + sink.push('-'); + else if (Fmt::FORCE_SIGN) + sink.push('+'); + } + // prefix + if constexpr (Fmt::PREFIX) { + if constexpr (Fmt::BASE == 2) { + sink.push('b'); + sink.push('0'); + } + if constexpr (Fmt::BASE == 16) { + sink.push('x'); + sink.push('0'); + } + if constexpr (Fmt::BASE == 8) { + const cpp::string_view written = sink.buffer_view(); + if (written.empty() || written.front() != '0') + sink.push('0'); + } + } } - return cpp::string_view(buffer.data(), buffer.size()); + }; + + cpp::array array; + size_t written = 0; + +public: + IntegerToString(const IntegerToString &) = delete; + IntegerToString(T value) { + details::BackwardStringBufferWriter writer(array); + IntegerWriter::write(value, writer); + written = writer.size(); } - template , int> = 0> - LIBC_INLINE static cpp::optional - oct(T val, cpp::span buffer) { - return convert<8>(val, buffer); + [[nodiscard]] LIBC_INLINE static cpp::optional + format_to(cpp::span buffer, T value) { + details::BackwardStringBufferWriter writer(buffer); + IntegerWriter::write(value, writer); + if (writer.ok()) + return cpp::string_view(buffer.data() + buffer.size() - writer.size(), + writer.size()); + return cpp::nullopt; } - template , int> = 0> - LIBC_INLINE static cpp::optional - bin(T val, cpp::span buffer) { - return convert<2>(val, buffer); + LIBC_INLINE static constexpr size_t buffer_size() { return BUFFER_SIZE; } + + LIBC_INLINE size_t size() const { return written; } + LIBC_INLINE cpp::string_view view() && = delete; + LIBC_INLINE cpp::string_view view() const & { + return cpp::string_view(array.data() + array.size() - size(), size()); } }; diff --git a/libc/src/__support/libc_assert.h b/libc/src/__support/libc_assert.h --- a/libc/src/__support/libc_assert.h +++ b/libc/src/__support/libc_assert.h @@ -32,17 +32,15 @@ LIBC_INLINE void report_assertion_failure(const char *assertion, const char *filename, unsigned line, const char *funcname) { - char line_str[IntegerToString::dec_bufsize()]; - // 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 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 diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp --- a/libc/src/__support/threads/linux/thread.cpp +++ b/libc/src/__support/threads/linux/thread.cpp @@ -391,7 +391,7 @@ 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() + // Size of tid + IntegerToString::buffer_size() + // Size of tid 1 + // For '/' character 5; // For the file name "comm" and the nullterminator. diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h --- a/libc/src/stdio/printf_core/float_dec_converter.h +++ b/libc/src/stdio/printf_core/float_dec_converter.h @@ -32,6 +32,9 @@ namespace printf_core { using MantissaInt = fputil::FPBits::UIntType; +using DecimalString = IntegerToString; +using ExponentString = + IntegerToString::WithSign>; // Returns true if value is divisible by 2^p. template @@ -193,39 +196,11 @@ return 0; } - cpp::string_view exp_str(int exponent, cpp::span 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::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::EXPONENT_WIDTH < + (sizeof(int) * 8)); public: FloatWriter(Writer *init_writer, bool init_has_decimal_point, @@ -239,8 +214,8 @@ } void write_first_block(BlockInt block, bool exp_format = false) { - char buf[IntegerToString::dec_bufsize()]; - 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. @@ -268,9 +243,8 @@ // 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()]; - 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]; @@ -285,8 +259,8 @@ RoundDirection round) { char end_buff[BLOCK_SIZE]; - char buf[IntegerToString::dec_bufsize()]; - 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 @@ -372,9 +346,8 @@ char end_buff[BLOCK_SIZE]; { - char buf[IntegerToString::dec_bufsize()]; - 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 @@ -424,8 +397,8 @@ // but we do increment the exponent. ++exponent; - char buf[IntegerToString::dec_bufsize()]; - 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. @@ -479,11 +452,9 @@ buffered_digits = block_digits; RET_IF_RESULT_NEGATIVE(flush_buffer()); - char buf[IntegerToString::dec_bufsize()]; - 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; @@ -707,17 +678,12 @@ 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()]; - auto int_to_str = *IntegerToString::dec(digits, buf); - size_t block_width = int_to_str.size(); + const size_t block_width = IntegerToString(digits).size(); final_exponent = (cur_block * BLOCK_SIZE) + static_cast(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(positive_exponent).size(); // Calculate the total number of digits in the number. // 1 - the digit before the decimal point @@ -752,10 +718,7 @@ // 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()]; - auto int_to_str = *IntegerToString::dec(digits, buf); - last_block_size = int_to_str.size(); + last_block_size = IntegerToString(digits).size(); } // This is the last block. @@ -881,14 +844,7 @@ return convert_float_decimal_typed(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()]; - auto int_to_str = *IntegerToString::dec(digits, buf); - block_width = int_to_str.size(); - } + const size_t block_width = IntegerToString(digits).size(); size_t digits_checked = 0; // TODO: look into unifying trailing_zeroes and trailing_nines. The number can @@ -900,8 +856,8 @@ // If the first block is not also the last block if (block_width <= exp_precision + 1) { - char buf[IntegerToString::dec_bufsize()]; - 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') { @@ -962,8 +918,8 @@ size_t last_block_size = BLOCK_SIZE; - char buf[IntegerToString::dec_bufsize()]; - 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(); diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h --- a/libc/src/stdio/printf_core/int_converter.h +++ b/libc/src/stdio/printf_core/int_converter.h @@ -28,17 +28,38 @@ 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; +using HexFmtUppercase = IntegerToString; +using OctFmt = IntegerToString; +using DecFmt = IntegerToString; + +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 num_to_strview(uintmax_t num, cpp::span 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; @@ -66,8 +87,8 @@ num = apply_length_modifier(num, to_conv.length_modifier); - char buf[IntegerToString::oct_bufsize()]; - auto str = num_to_strview(num, buf, to_conv.conv_name); + cpp::array buf; + auto str = details::num_to_strview(num, buf, to_conv.conv_name); if (!str) return INT_CONVERSION_ERROR; diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp --- a/libc/test/UnitTest/LibcTest.cpp +++ b/libc/test/UnitTest/LibcTest.cpp @@ -47,9 +47,8 @@ cpp::string> describeValue(T Value) { static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt"); - char buf[IntegerToString::hex_bufsize()]; - IntegerToString::hex(Value, buf, false); - return "0x" + cpp::string(buf, sizeof(buf)); + const IntegerToString buffer(Value); + return buffer.view(); } // When the value is of a standard integral type, just display it as normal. diff --git a/libc/test/UnitTest/TestLogger.cpp b/libc/test/UnitTest/TestLogger.cpp --- a/libc/test/UnitTest/TestLogger.cpp +++ b/libc/test/UnitTest/TestLogger.cpp @@ -50,9 +50,8 @@ if constexpr (cpp::is_integral_v && cpp::is_unsigned_v && sizeof(T) > sizeof(uint64_t)) { static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt"); - char buf[IntegerToString::hex_bufsize()]; - IntegerToString::hex(t, buf, false); - return *this << "0x" << cpp::string_view(buf, sizeof(buf)); + const IntegerToString buffer(t); + return *this << buffer.view(); } else { return *this << cpp::to_string(t); } diff --git a/libc/test/src/__support/integer_to_string_test.cpp b/libc/test/src/__support/integer_to_string_test.cpp --- a/libc/test/src/__support/integer_to_string_test.cpp +++ b/libc/test/src/__support/integer_to_string_test.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/span.h" #include "src/__support/CPP/string_view.h" #include "src/__support/UInt.h" #include "src/__support/UInt128.h" @@ -16,275 +17,289 @@ #include "limits.h" using __llvm_libc::IntegerToString; +using __llvm_libc::cpp::span; using __llvm_libc::cpp::string_view; +using __llvm_libc::radix::Bin; +using __llvm_libc::radix::Custom; +using __llvm_libc::radix::Dec; +using __llvm_libc::radix::Hex; +using __llvm_libc::radix::Oct; + +#define EXPECT(type, value, string_value) \ + { \ + const type buffer(value); \ + EXPECT_EQ(buffer.view(), string_view(string_value)); \ + } TEST(LlvmLibcIntegerToStringTest, UINT8) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(uint8_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(uint8_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(uint8_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(uint8_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(uint8_t(UINT8_MAX), buf), string_view("255")); - EXPECT_EQ(*IntegerToString::dec(uint8_t(-1), buf), string_view("255")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, UINT8_MAX, "255"); + EXPECT(type, -1, "255"); } TEST(LlvmLibcIntegerToStringTest, INT8) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(int8_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(int8_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(int8_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(int8_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(int8_t(-12), buf), string_view("-12")); - EXPECT_EQ(*IntegerToString::dec(int8_t(-123), buf), string_view("-123")); - EXPECT_EQ(*IntegerToString::dec(int8_t(INT8_MAX), buf), string_view("127")); - EXPECT_EQ(*IntegerToString::dec(int8_t(INT8_MIN), buf), string_view("-128")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, -12, "-12"); + EXPECT(type, -123, "-123"); + EXPECT(type, INT8_MAX, "127"); + EXPECT(type, INT8_MIN, "-128"); } TEST(LlvmLibcIntegerToStringTest, UINT16) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(uint16_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(UINT16_MAX), buf), - string_view("65535")); - EXPECT_EQ(*IntegerToString::dec(uint16_t(-1), buf), string_view("65535")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, UINT16_MAX, "65535"); + EXPECT(type, -1, "65535"); } TEST(LlvmLibcIntegerToStringTest, INT16) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(int16_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(int16_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(int16_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(int16_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(int16_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(int16_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(int16_t(-1), buf), string_view("-1")); - EXPECT_EQ(*IntegerToString::dec(int16_t(-12), buf), string_view("-12")); - EXPECT_EQ(*IntegerToString::dec(int16_t(-123), buf), string_view("-123")); - EXPECT_EQ(*IntegerToString::dec(int16_t(-1234), buf), string_view("-1234")); - EXPECT_EQ(*IntegerToString::dec(int16_t(-12345), buf), string_view("-12345")); - EXPECT_EQ(*IntegerToString::dec(int16_t(INT16_MAX), buf), - string_view("32767")); - EXPECT_EQ(*IntegerToString::dec(int16_t(INT16_MIN), buf), - string_view("-32768")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, -1, "-1"); + EXPECT(type, -12, "-12"); + EXPECT(type, -123, "-123"); + EXPECT(type, -1234, "-1234"); + EXPECT(type, -12345, "-12345"); + EXPECT(type, INT16_MAX, "32767"); + EXPECT(type, INT16_MIN, "-32768"); } TEST(LlvmLibcIntegerToStringTest, UINT32) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(uint32_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(123456), buf), string_view("123456")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(1234567), buf), - string_view("1234567")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(12345678), buf), - string_view("12345678")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(123456789), buf), - string_view("123456789")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(1234567890), buf), - string_view("1234567890")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(UINT32_MAX), buf), - string_view("4294967295")); - EXPECT_EQ(*IntegerToString::dec(uint32_t(-1), buf), string_view("4294967295")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, 123456, "123456"); + EXPECT(type, 1234567, "1234567"); + EXPECT(type, 12345678, "12345678"); + EXPECT(type, 123456789, "123456789"); + EXPECT(type, 1234567890, "1234567890"); + EXPECT(type, UINT32_MAX, "4294967295"); + EXPECT(type, -1, "4294967295"); } TEST(LlvmLibcIntegerToStringTest, INT32) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(int32_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(int32_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(int32_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(int32_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(int32_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(int32_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(int32_t(123456), buf), string_view("123456")); - EXPECT_EQ(*IntegerToString::dec(int32_t(1234567), buf), - string_view("1234567")); - EXPECT_EQ(*IntegerToString::dec(int32_t(12345678), buf), - string_view("12345678")); - EXPECT_EQ(*IntegerToString::dec(int32_t(123456789), buf), - string_view("123456789")); - EXPECT_EQ(*IntegerToString::dec(int32_t(1234567890), buf), - string_view("1234567890")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-1), buf), string_view("-1")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-12), buf), string_view("-12")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-123), buf), string_view("-123")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-1234), buf), string_view("-1234")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-12345), buf), string_view("-12345")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-123456), buf), - string_view("-123456")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-1234567), buf), - string_view("-1234567")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-12345678), buf), - string_view("-12345678")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-123456789), buf), - string_view("-123456789")); - EXPECT_EQ(*IntegerToString::dec(int32_t(-1234567890), buf), - string_view("-1234567890")); - EXPECT_EQ(*IntegerToString::dec(int32_t(INT32_MAX), buf), - string_view("2147483647")); - EXPECT_EQ(*IntegerToString::dec(int32_t(INT32_MIN), buf), - string_view("-2147483648")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, 123456, "123456"); + EXPECT(type, 1234567, "1234567"); + EXPECT(type, 12345678, "12345678"); + EXPECT(type, 123456789, "123456789"); + EXPECT(type, 1234567890, "1234567890"); + EXPECT(type, -1, "-1"); + EXPECT(type, -12, "-12"); + EXPECT(type, -123, "-123"); + EXPECT(type, -1234, "-1234"); + EXPECT(type, -12345, "-12345"); + EXPECT(type, -123456, "-123456"); + EXPECT(type, -1234567, "-1234567"); + EXPECT(type, -12345678, "-12345678"); + EXPECT(type, -123456789, "-123456789"); + EXPECT(type, -1234567890, "-1234567890"); + EXPECT(type, INT32_MAX, "2147483647"); + EXPECT(type, INT32_MIN, "-2147483648"); } TEST(LlvmLibcIntegerToStringTest, UINT64) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(uint64_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(123456), buf), string_view("123456")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(1234567), buf), - string_view("1234567")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(12345678), buf), - string_view("12345678")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(123456789), buf), - string_view("123456789")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(1234567890), buf), - string_view("1234567890")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(1234567890123456789), buf), - string_view("1234567890123456789")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(UINT64_MAX), buf), - string_view("18446744073709551615")); - EXPECT_EQ(*IntegerToString::dec(uint64_t(-1), buf), - string_view("18446744073709551615")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, 123456, "123456"); + EXPECT(type, 1234567, "1234567"); + EXPECT(type, 12345678, "12345678"); + EXPECT(type, 123456789, "123456789"); + EXPECT(type, 1234567890, "1234567890"); + EXPECT(type, 1234567890123456789, "1234567890123456789"); + EXPECT(type, UINT64_MAX, "18446744073709551615"); + EXPECT(type, -1, "18446744073709551615"); } TEST(LlvmLibcIntegerToStringTest, INT64) { - char buf[IntegerToString::dec_bufsize()]; - EXPECT_EQ(*IntegerToString::dec(int64_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::dec(int64_t(1), buf), string_view("1")); - EXPECT_EQ(*IntegerToString::dec(int64_t(12), buf), string_view("12")); - EXPECT_EQ(*IntegerToString::dec(int64_t(123), buf), string_view("123")); - EXPECT_EQ(*IntegerToString::dec(int64_t(1234), buf), string_view("1234")); - EXPECT_EQ(*IntegerToString::dec(int64_t(12345), buf), string_view("12345")); - EXPECT_EQ(*IntegerToString::dec(int64_t(123456), buf), string_view("123456")); - EXPECT_EQ(*IntegerToString::dec(int64_t(1234567), buf), - string_view("1234567")); - EXPECT_EQ(*IntegerToString::dec(int64_t(12345678), buf), - string_view("12345678")); - EXPECT_EQ(*IntegerToString::dec(int64_t(123456789), buf), - string_view("123456789")); - EXPECT_EQ(*IntegerToString::dec(int64_t(1234567890), buf), - string_view("1234567890")); - EXPECT_EQ(*IntegerToString::dec(int64_t(1234567890123456789), buf), - string_view("1234567890123456789")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-1), buf), string_view("-1")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-12), buf), string_view("-12")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-123), buf), string_view("-123")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-1234), buf), string_view("-1234")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-12345), buf), string_view("-12345")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-123456), buf), - string_view("-123456")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-1234567), buf), - string_view("-1234567")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-12345678), buf), - string_view("-12345678")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-123456789), buf), - string_view("-123456789")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-1234567890), buf), - string_view("-1234567890")); - EXPECT_EQ(*IntegerToString::dec(int64_t(-1234567890123456789), buf), - string_view("-1234567890123456789")); - EXPECT_EQ(*IntegerToString::dec(int64_t(INT64_MAX), buf), - string_view("9223372036854775807")); - EXPECT_EQ(*IntegerToString::dec(int64_t(INT64_MIN), buf), - string_view("-9223372036854775808")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 1, "1"); + EXPECT(type, 12, "12"); + EXPECT(type, 123, "123"); + EXPECT(type, 1234, "1234"); + EXPECT(type, 12345, "12345"); + EXPECT(type, 123456, "123456"); + EXPECT(type, 1234567, "1234567"); + EXPECT(type, 12345678, "12345678"); + EXPECT(type, 123456789, "123456789"); + EXPECT(type, 1234567890, "1234567890"); + EXPECT(type, 1234567890123456789, "1234567890123456789"); + EXPECT(type, -1, "-1"); + EXPECT(type, -12, "-12"); + EXPECT(type, -123, "-123"); + EXPECT(type, -1234, "-1234"); + EXPECT(type, -12345, "-12345"); + EXPECT(type, -123456, "-123456"); + EXPECT(type, -1234567, "-1234567"); + EXPECT(type, -12345678, "-12345678"); + EXPECT(type, -123456789, "-123456789"); + EXPECT(type, -1234567890, "-1234567890"); + EXPECT(type, -1234567890123456789, "-1234567890123456789"); + EXPECT(type, INT64_MAX, "9223372036854775807"); + EXPECT(type, INT64_MIN, "-9223372036854775808"); } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_8) { - char buf[IntegerToString::oct_bufsize()]; - EXPECT_EQ((*IntegerToString::oct(uint64_t(0), buf)), string_view("0")); - EXPECT_EQ((*IntegerToString::oct(uint64_t(012345), buf)), - string_view("12345")); - EXPECT_EQ((*IntegerToString::oct(uint64_t(0123456701234567012345), buf)), - string_view("123456701234567012345")); - EXPECT_EQ((*IntegerToString::oct(uint64_t(01777777777777777777777), buf)), - string_view("1777777777777777777777")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 012345, "12345"); + EXPECT(type, 0123456701234567012345, "123456701234567012345"); + EXPECT(type, 01777777777777777777777, "1777777777777777777777"); } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_16) { - char buf[IntegerToString::hex_bufsize()]; - EXPECT_EQ(*IntegerToString::hex(uint64_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::hex(uint64_t(0x12345), buf), string_view("12345")); - EXPECT_EQ((*IntegerToString::hex(uint64_t(0x123456789abcdef), buf)), - string_view("123456789abcdef")); - EXPECT_EQ(*IntegerToString::hex(uint64_t(0x123456789abcdef), buf, false), - string_view("123456789ABCDEF")); - EXPECT_EQ(*IntegerToString::hex(uint64_t(0xffffffffffffffff), buf), - string_view("ffffffffffffffff")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 0x12345, "12345"); + EXPECT(type, 0x123456789abcdef, "123456789abcdef"); + EXPECT(type, 0xffffffffffffffff, "ffffffffffffffff"); + using TYPE = IntegerToString; + EXPECT(TYPE, 0x123456789abcdef, "123456789ABCDEF"); } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_2) { - char buf[IntegerToString::bin_bufsize()]; - EXPECT_EQ(*IntegerToString::bin(uint64_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::bin(uint64_t(0xf0c), buf), - string_view("111100001100")); - EXPECT_EQ(*IntegerToString::bin(uint64_t(0x123abc), buf), - string_view("100100011101010111100")); - EXPECT_EQ( - *IntegerToString::bin(uint64_t(0xffffffffffffffff), buf), - string_view( - "1111111111111111111111111111111111111111111111111111111111111111")); + using type = IntegerToString; + EXPECT(type, 0, "0"); + EXPECT(type, 0b111100001100, "111100001100"); + EXPECT(type, 0b100100011101010111100, "100100011101010111100"); + EXPECT(type, 0xffffffffffffffff, + "1111111111111111111111111111111111111111111111111111111111111111"); } -TEST(LlvmLibcIntegerToStringTest, UINT64_Base_36) { - char buf[IntegerToString::bufsize<36, uint64_t>()]; - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(0), buf), string_view("0")); - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(12345), buf), - string_view("9ix")); - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(1047601316295595), buf), - string_view("abcdefghij")); - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(2092218013456445), buf), - string_view("klmnopqrst")); - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(1867590395), buf, false), - string_view("UVWXYZ")); - EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(0xffffffffffffffff), buf), - string_view("3w5e11264sgsf")); +TEST(LlvmLibcIntegerToStringTest, UINT128_Base_16) { + using type = IntegerToString>; + EXPECT(type, 0, "00000000000000000000000000000000"); + EXPECT(type, 0x12345, "00000000000000000000000000012345"); + EXPECT(type, static_cast(0x1234) << 112, + "12340000000000000000000000000000"); + EXPECT(type, static_cast(0x1234) << 48, + "00000000000000001234000000000000"); + EXPECT(type, static_cast(0x1234) << 52, + "00000000000000012340000000000000"); } -TEST(LlvmLibcIntegerToStringTest, UINT128_Base_16) { - char buf[IntegerToString::hex_bufsize()]; - EXPECT_EQ(*IntegerToString::hex(static_cast(0), buf), - string_view("00000000000000000000000000000000")); - EXPECT_EQ(*IntegerToString::hex(static_cast(0x12345), buf), - string_view("00000000000000000000000000012345")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 112, buf)), - string_view("12340000000000000000000000000000")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 48, buf)), - string_view("00000000000000001234000000000000")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 52, buf)), - string_view("00000000000000012340000000000000")); +TEST(LlvmLibcIntegerToStringTest, UINT64_Base_36) { + using type = IntegerToString>; + EXPECT(type, 0, "0"); + EXPECT(type, 12345, "9ix"); + EXPECT(type, 1047601316295595, "abcdefghij"); + EXPECT(type, 2092218013456445, "klmnopqrst"); + EXPECT(type, 0xffffffffffffffff, "3w5e11264sgsf"); + + using TYPE = IntegerToString::Uppercase>; + EXPECT(TYPE, 1867590395, "UVWXYZ"); } TEST(LlvmLibcIntegerToStringTest, UINT256_Base_16) { using UInt256 = __llvm_libc::cpp::UInt<256>; - char buf[IntegerToString::hex_bufsize()]; - EXPECT_EQ( - *IntegerToString::hex(static_cast(0), buf), - string_view( - "0000000000000000000000000000000000000000000000000000000000000000")); - EXPECT_EQ( - *IntegerToString::hex(static_cast(0x12345), buf), - string_view( - "0000000000000000000000000000000000000000000000000000000000012345")); - EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 112, buf)), - string_view( - "0000000000000000000000000000000012340000000000000000000000000000")); - EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 116, buf)), - string_view( - "0000000000000000000000000000000123400000000000000000000000000000")); - EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 240, buf)), - string_view( - "1234000000000000000000000000000000000000000000000000000000000000")); + using type = IntegerToString>; + EXPECT(type, static_cast(0), + "0000000000000000000000000000000000000000000000000000000000000000"); + EXPECT(type, static_cast(0x12345), + "0000000000000000000000000000000000000000000000000000000000012345"); + EXPECT(type, static_cast(0x1234) << 112, + "0000000000000000000000000000000012340000000000000000000000000000"); + EXPECT(type, static_cast(0x1234) << 116, + "0000000000000000000000000000000123400000000000000000000000000000"); + EXPECT(type, static_cast(0x1234) << 240, + "1234000000000000000000000000000000000000000000000000000000000000"); +} + +TEST(LlvmLibcIntegerToStringTest, NegativeInterpretedAsPositive) { + using BIN = IntegerToString; + using OCT = IntegerToString; + using DEC = IntegerToString; + using HEX = IntegerToString; + EXPECT(BIN, -1, "11111111"); + EXPECT(OCT, -1, "377"); + EXPECT(DEC, -1, "-1"); // Only DEC format negative values + EXPECT(HEX, -1, "ff"); +} + +TEST(LlvmLibcIntegerToStringTest, Width) { + using BIN = IntegerToString>; + using OCT = IntegerToString>; + using DEC = IntegerToString>; + using HEX = IntegerToString>; + EXPECT(BIN, 1, "0001"); + EXPECT(HEX, 1, "0001"); + EXPECT(OCT, 1, "0001"); + EXPECT(DEC, 1, "0001"); +} + +TEST(LlvmLibcIntegerToStringTest, Prefix) { + // WithPrefix is not supported for Decimal + using BIN = IntegerToString; + using OCT = IntegerToString; + using HEX = IntegerToString; + EXPECT(BIN, 1, "0b1"); + EXPECT(HEX, 1, "0x1"); + EXPECT(OCT, 1, "01"); + EXPECT(OCT, 0, "0"); // Zero is not prefixed for octal +} + +TEST(LlvmLibcIntegerToStringTest, Uppercase) { + using HEX = IntegerToString; + EXPECT(HEX, 0xDEADC0DE, "DEADC0DE"); +} + +TEST(LlvmLibcIntegerToStringTest, Sign) { + // WithSign only compiles with DEC + using DEC = IntegerToString; + EXPECT(DEC, -1, "-1"); + EXPECT(DEC, 0, "+0"); + EXPECT(DEC, 1, "+1"); +} + +TEST(LlvmLibcIntegerToStringTest, BufferOverrun) { + { // Writing '0' in an empty buffer requiring zero digits : works + const auto view = + IntegerToString>::format_to(span(), 0); + ASSERT_TRUE(view.has_value()); + ASSERT_EQ(*view, string_view("")); + } + char buffer[1]; + { // Writing '1' in a buffer of one char : works + const auto view = IntegerToString::format_to(buffer, 1); + ASSERT_TRUE(view.has_value()); + ASSERT_EQ(*view, string_view("1")); + } + { // Writing '11' in a buffer of one char : fails + const auto view = IntegerToString::format_to(buffer, 11); + ASSERT_FALSE(view.has_value()); + } } diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -413,6 +413,8 @@ 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",