[libc-commits] [libc] b555912 - [libc] Better IntegerToString API
Guillaume Chatelet via libc-commits
libc-commits at lists.llvm.org
Wed Aug 9 00:33:50 PDT 2023
Author: Guillaume Chatelet
Date: 2023-08-09T07:33:29Z
New Revision: b555912e705924630de1f65b77d0139a20b5338b
URL: https://github.com/llvm/llvm-project/commit/b555912e705924630de1f65b77d0139a20b5338b
DIFF: https://github.com/llvm/llvm-project/commit/b555912e705924630de1f65b77d0139a20b5338b.diff
LOG: [libc] Better IntegerToString API
This patch is an alternative to D155902. It provides the following benefits:
- No buffer manual allocation and error handling for the general case
- More flexible API : width specifier, sign and prefix handling
- Simpler code
The more flexible API removes the need for manually tweaking the buffer afterwards, and so prevents relying on implementation details of IntegerToString.
Reviewed By: michaelrj, jhuber6
Differential Revision: https://reviews.llvm.org/D156981
Added:
Modified:
libc/src/__support/CMakeLists.txt
libc/src/__support/CPP/string.h
libc/src/__support/CPP/stringstream.h
libc/src/__support/FPUtil/fpbits_str.h
libc/src/__support/StringUtil/error_to_string.cpp
libc/src/__support/StringUtil/signal_to_string.cpp
libc/src/__support/integer_to_string.h
libc/src/__support/libc_assert.h
libc/src/__support/threads/linux/thread.cpp
libc/src/stdio/printf_core/float_dec_converter.h
libc/src/stdio/printf_core/int_converter.h
libc/test/UnitTest/LibcTest.cpp
libc/test/UnitTest/TestLogger.cpp
libc/test/src/__support/integer_to_string_test.cpp
utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Removed:
################################################################################
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 02b444905cf1a1..74021093ae083f 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -90,10 +90,11 @@ add_header_library(
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
index 239fa18eac36d3..be7ed41ffcdce1 100644
--- a/libc/src/__support/CPP/string.h
+++ b/libc/src/__support/CPP/string.h
@@ -195,10 +195,8 @@ LIBC_INLINE string operator+(const char *lhs, const string &rhs) {
namespace internal {
template <typename T> string to_dec_string(T value) {
- char dec_buf[IntegerToString::dec_bufsize<T>()];
- 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<T> buffer(value);
+ return buffer.view();
}
} // namespace internal
diff --git a/libc/src/__support/CPP/stringstream.h b/libc/src/__support/CPP/stringstream.h
index 5cae89d41e7d5f..089483147e6a57 100644
--- 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 @@ class StringStream {
// Write the |val| as string.
template <typename T, enable_if_t<is_integral_v<T>, int> = 0>
StringStream &operator<<(T val) {
- char buffer[IntegerToString::dec_bufsize<T>()];
- auto int_to_str = IntegerToString::dec(val, buffer);
- if (int_to_str)
- return operator<<(*int_to_str);
- return *this;
+ const IntegerToString<T> buffer(val);
+ return *this << buffer.view();
}
template <typename T, enable_if_t<is_floating_point_v<T>, int> = 0>
diff --git a/libc/src/__support/FPUtil/fpbits_str.h b/libc/src/__support/FPUtil/fpbits_str.h
index 475b582d050821..0a1041e15221d7 100644
--- 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 <typename T>
+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<NNN...N> = S: N, E: 0xNNNN, M:0xNNN...N"
// 1. N is a hexadecimal digit.
@@ -33,36 +42,31 @@ template <typename T> LIBC_INLINE cpp::string str(fputil::FPBits<T> x) {
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<UIntType>()];
- zerofill(bitsbuf, sizeof(bitsbuf));
- IntegerToString::hex(x.bits, bitsbuf, false);
- s += cpp::string(bitsbuf, sizeof(bitsbuf));
+ const details::ZeroPaddedHexFmt<UIntType> 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<uint16_t>()];
- 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<uint16_t> exponent(x.get_unbiased_exponent());
+ s += exponent.view();
if constexpr (cpp::is_same_v<T, long double> &&
fputil::FloatProperties<long double>::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<UIntType>()] = {'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<UIntType> 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
index 96624920d8f0ca..692f8845558270 100644
--- a/libc/src/__support/StringUtil/error_to_string.cpp
+++ b/libc/src/__support/StringUtil/error_to_string.cpp
@@ -23,10 +23,9 @@ namespace internal {
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<int>();
// 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<int>::buffer_size()) *
+ sizeof(char);
}
// This is to hold error strings that have to be custom built. It may be
@@ -51,7 +50,7 @@ cpp::string_view build_error_string(int err_num, cpp::span<char> buffer) {
// 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<int>()))
+ (sizeof("Unknown error") + 1 + IntegerToString<int>::buffer_size()))
return const_cast<char *>("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
index c3610deb592d24..78b4d4dd853682 100644
--- a/libc/src/__support/StringUtil/signal_to_string.cpp
+++ b/libc/src/__support/StringUtil/signal_to_string.cpp
@@ -24,10 +24,9 @@ namespace internal {
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<int>();
// 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<int>::buffer_size()) *
+ sizeof(char);
}
// This is to hold signal strings that have to be custom built. It may be
@@ -54,7 +53,7 @@ cpp::string_view build_signal_string(int sig_num, cpp::span<char> buffer) {
// 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<int>()))
+ (base_str.size() + 1 + IntegerToString<int>::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
index 4140da27a39905..998ee6d6363ce8 100644
--- 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<int> 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<int>::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<int8_t>(-1) // "-1"
+// IntegerToString<int8_t, radix::Dec>(-1) // "-1"
+// IntegerToString<int8_t, radix::Bin>(-1) // "11111111"
+// IntegerToString<int8_t, radix::Oct>(-1) // "377"
+// IntegerToString<int8_t, radix::Hex>(-1) // "ff"
+//
+// Additionnally, the format can be changed by navigating the subtypes:
+// - WithPrefix : Adds "0b", "0", "0x" for binary, octal and hexadecimal
+// - WithWidth<XX> : 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<int8_t, radix::Dec::WithWidth<2>::WithSign>(0) : "+00"
+// IntegerToString<int8_t, radix::Dec::WithWidth<2>::WithSign>(-1) : "-01"
+// IntegerToString<uint8_t, radix::Hex::WithPrefix::Uppercase>(255) : "0xFF"
+// IntegerToString<uint8_t, radix::Hex::WithWidth<4>::Uppercase>(255) : "00FF"
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H
#define LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H
#include <stdint.h>
+#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<int>()];
-// auto str = IntegerToString::hex(
-// a, hexbuf, false /* generate upper case characters */);
-//
-// // Convert to decimal string:
-// char decbuf[IntegerToString::dec_bufsize<int>()];
-// auto str = IntegerToString::dec(a, decbuf);
-//
-// // Convert to octal string:
-// char octbuf[IntegerToString::oct_bufsize<int>(a)];
-// auto str = IntegerToString::dec(a, octbuf);
-//
-// // Convert to binary string:
-// char binbuf[IntegerToString::bin_bufsize<int>(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<char> &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<char>(digit < 10 ? digit + '0' : digit + a - 10);
- }
- }
- len = buffer.size() - buffptr;
+namespace details {
- return cpp::string_view(buffer.data() + buffer.size() - len, len);
- }
+template <uint8_t base, bool prefix = false, bool force_sign = false,
+ bool is_uppercase = false, size_t min_digits = 1>
+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<char> &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<BASE, true, FORCE_SIGN, IS_UPPERCASE, MIN_DIGITS>;
+ using WithSign = Fmt<BASE, PREFIX, true, IS_UPPERCASE, MIN_DIGITS>;
+ using Uppercase = Fmt<BASE, PREFIX, FORCE_SIGN, true, MIN_DIGITS>;
+ template <size_t value>
+ using WithWidth = Fmt<BASE, PREFIX, FORCE_SIGN, IS_UPPERCASE, value>;
- 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 <bool forward> class StringBufferWriterImpl {
+ cpp::span<char> 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 <uint8_t BASE, typename T>
- 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<T>() ? 1 : 0) +
- (BASE == 10 ? BUFSIZE_BASE10 : BUFSIZE_COMMON);
- }
+ StringBufferWriterImpl(const StringBufferWriterImpl &) = delete;
+ StringBufferWriterImpl(cpp::span<char> buffer) : buffer(buffer) {}
- template <typename T> 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 <typename T> 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 <typename T> LIBC_INLINE static constexpr size_t oct_bufsize() {
- return bufsize<8, T>();
+ LIBC_INLINE cpp::span<char> remainder_span() const {
+ return forward ? buffer.last(remainder_size())
+ : buffer.first(remainder_size());
}
- template <typename T> LIBC_INLINE static constexpr size_t bin_bufsize() {
- return bufsize<2, T>();
+ LIBC_INLINE cpp::span<char> buffer_span() const {
+ return forward ? buffer.first(size()) : buffer.last(size());
}
- template <uint8_t BASE, typename T,
- cpp::enable_if_t<2 <= BASE && BASE <= 36 && cpp::is_integral_v<T>,
- int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- convert(T val, cpp::span<char> buffer, bool lowercase = true) {
- if (buffer.size() < bufsize<BASE, T>())
- return cpp::optional<cpp::string_view>();
- if (cpp::is_signed_v<T>)
- 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 <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- dec(T val, cpp::span<char> buffer) {
- return convert<10>(val, buffer);
- }
+using StringBufferWriter = StringBufferWriterImpl<true>;
+using BackwardStringBufferWriter = StringBufferWriterImpl<false>;
+
+} // 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 <size_t radix> using Custom = details::Fmt<radix>;
- template <typename T, cpp::enable_if_t<cpp::is_integral_v<T> &&
- (sizeof(T) <= sizeof(uintmax_t)),
- int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- hex(T val, cpp::span<char> buffer, bool lowercase = true) {
- return convert<16>(val, buffer, lowercase);
+} // namespace radix
+
+// See file header for documentation.
+template <typename T, typename Fmt = radix::Dec> class IntegerToString {
+ static_assert(cpp::is_integral_v<T>);
+
+ 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 <typename T, cpp::enable_if_t<cpp::is_integral_v<T> &&
- (sizeof(T) > sizeof(uintmax_t)) &&
- sizeof(T) % sizeof(uintmax_t) == 0,
- int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- hex(T val, cpp::span<char> 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<uintmax_t>(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<T>);
+ using UNSIGNED_T = cpp::make_unsigned_t<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<T> || 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<T>::min()) {
+ return cpp::bit_cast<UNSIGNED_T>(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<UNSIGNED_T>(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<char, BUFFER_SIZE> 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 <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- oct(T val, cpp::span<char> buffer) {
- return convert<8>(val, buffer);
+ [[nodiscard]] LIBC_INLINE static cpp::optional<cpp::string_view>
+ format_to(cpp::span<char> 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 <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
- LIBC_INLINE static cpp::optional<cpp::string_view>
- bin(T val, cpp::span<char> 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
index eabcce8071f340..8c1f630fb4a403 100644
--- a/libc/src/__support/libc_assert.h
+++ b/libc/src/__support/libc_assert.h
@@ -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
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index 668f0f11e970ba..88efaece182a37 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -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.
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index a0c891fb866c5a..7927220da3fff8 100644
--- 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 __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>
@@ -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,
@@ -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.
@@ -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];
@@ -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
@@ -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
@@ -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.
@@ -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;
@@ -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
@@ -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.
@@ -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
@@ -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') {
@@ -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();
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index aa91e34486ba48..044721b5afde00 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -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;
@@ -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;
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index 3ffd0c3d18abd4..d9a9e50ca9a65b 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -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.
diff --git a/libc/test/UnitTest/TestLogger.cpp b/libc/test/UnitTest/TestLogger.cpp
index 0fa2153c6726b1..02be4bdf5de811 100644
--- a/libc/test/UnitTest/TestLogger.cpp
+++ b/libc/test/UnitTest/TestLogger.cpp
@@ -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);
}
diff --git a/libc/test/src/__support/integer_to_string_test.cpp b/libc/test/src/__support/integer_to_string_test.cpp
index 783a4d80826908..991582e896d2fe 100644
--- 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<uint8_t>()];
- 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<uint8_t>;
+ 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<int8_t>()];
- 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<int8_t>;
+ 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<uint16_t>()];
- 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<uint16_t>;
+ 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<int16_t>()];
- 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<int16_t>;
+ 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<uint32_t>()];
- 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<uint32_t>;
+ 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<int32_t>()];
- 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<int32_t>;
+ 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<uint64_t>()];
- 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<uint64_t>;
+ 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<int64_t>()];
- 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<int64_t>;
+ 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<uint64_t>()];
- 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<int64_t, Oct>;
+ 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<uint64_t>()];
- 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<uint64_t, Hex>;
+ EXPECT(type, 0, "0");
+ EXPECT(type, 0x12345, "12345");
+ EXPECT(type, 0x123456789abcdef, "123456789abcdef");
+ EXPECT(type, 0xffffffffffffffff, "ffffffffffffffff");
+ using TYPE = IntegerToString<uint64_t, Hex::Uppercase>;
+ EXPECT(TYPE, 0x123456789abcdef, "123456789ABCDEF");
}
TEST(LlvmLibcIntegerToStringTest, UINT64_Base_2) {
- char buf[IntegerToString::bin_bufsize<uint64_t>()];
- 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<uint64_t, Bin>;
+ 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<UInt128, Hex::WithWidth<32>>;
+ EXPECT(type, 0, "00000000000000000000000000000000");
+ EXPECT(type, 0x12345, "00000000000000000000000000012345");
+ EXPECT(type, static_cast<UInt128>(0x1234) << 112,
+ "12340000000000000000000000000000");
+ EXPECT(type, static_cast<UInt128>(0x1234) << 48,
+ "00000000000000001234000000000000");
+ EXPECT(type, static_cast<UInt128>(0x1234) << 52,
+ "00000000000000012340000000000000");
}
-TEST(LlvmLibcIntegerToStringTest, UINT128_Base_16) {
- char buf[IntegerToString::hex_bufsize<UInt128>()];
- EXPECT_EQ(*IntegerToString::hex(static_cast<UInt128>(0), buf),
- string_view("00000000000000000000000000000000"));
- EXPECT_EQ(*IntegerToString::hex(static_cast<UInt128>(0x12345), buf),
- string_view("00000000000000000000000000012345"));
- EXPECT_EQ((*IntegerToString::hex(static_cast<UInt128>(0x1234) << 112, buf)),
- string_view("12340000000000000000000000000000"));
- EXPECT_EQ((*IntegerToString::hex(static_cast<UInt128>(0x1234) << 48, buf)),
- string_view("00000000000000001234000000000000"));
- EXPECT_EQ((*IntegerToString::hex(static_cast<UInt128>(0x1234) << 52, buf)),
- string_view("00000000000000012340000000000000"));
+TEST(LlvmLibcIntegerToStringTest, UINT64_Base_36) {
+ using type = IntegerToString<uint64_t, Custom<36>>;
+ EXPECT(type, 0, "0");
+ EXPECT(type, 12345, "9ix");
+ EXPECT(type, 1047601316295595, "abcdefghij");
+ EXPECT(type, 2092218013456445, "klmnopqrst");
+ EXPECT(type, 0xffffffffffffffff, "3w5e11264sgsf");
+
+ using TYPE = IntegerToString<uint64_t, Custom<36>::Uppercase>;
+ EXPECT(TYPE, 1867590395, "UVWXYZ");
}
TEST(LlvmLibcIntegerToStringTest, UINT256_Base_16) {
using UInt256 = __llvm_libc::cpp::UInt<256>;
- char buf[IntegerToString::hex_bufsize<UInt256>()];
- EXPECT_EQ(
- *IntegerToString::hex(static_cast<UInt256>(0), buf),
- string_view(
- "0000000000000000000000000000000000000000000000000000000000000000"));
- EXPECT_EQ(
- *IntegerToString::hex(static_cast<UInt256>(0x12345), buf),
- string_view(
- "0000000000000000000000000000000000000000000000000000000000012345"));
- EXPECT_EQ(
- (*IntegerToString::hex(static_cast<UInt256>(0x1234) << 112, buf)),
- string_view(
- "0000000000000000000000000000000012340000000000000000000000000000"));
- EXPECT_EQ(
- (*IntegerToString::hex(static_cast<UInt256>(0x1234) << 116, buf)),
- string_view(
- "0000000000000000000000000000000123400000000000000000000000000000"));
- EXPECT_EQ(
- (*IntegerToString::hex(static_cast<UInt256>(0x1234) << 240, buf)),
- string_view(
- "1234000000000000000000000000000000000000000000000000000000000000"));
+ using type = IntegerToString<UInt256, Hex::WithWidth<64>>;
+ EXPECT(type, static_cast<UInt256>(0),
+ "0000000000000000000000000000000000000000000000000000000000000000");
+ EXPECT(type, static_cast<UInt256>(0x12345),
+ "0000000000000000000000000000000000000000000000000000000000012345");
+ EXPECT(type, static_cast<UInt256>(0x1234) << 112,
+ "0000000000000000000000000000000012340000000000000000000000000000");
+ EXPECT(type, static_cast<UInt256>(0x1234) << 116,
+ "0000000000000000000000000000000123400000000000000000000000000000");
+ EXPECT(type, static_cast<UInt256>(0x1234) << 240,
+ "1234000000000000000000000000000000000000000000000000000000000000");
+}
+
+TEST(LlvmLibcIntegerToStringTest, NegativeInterpretedAsPositive) {
+ using BIN = IntegerToString<int8_t, Bin>;
+ using OCT = IntegerToString<int8_t, Oct>;
+ using DEC = IntegerToString<int8_t, Dec>;
+ using HEX = IntegerToString<int8_t, Hex>;
+ 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<uint8_t, Bin::WithWidth<4>>;
+ using OCT = IntegerToString<uint8_t, Oct::WithWidth<4>>;
+ using DEC = IntegerToString<uint8_t, Dec::WithWidth<4>>;
+ using HEX = IntegerToString<uint8_t, Hex::WithWidth<4>>;
+ 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<uint8_t, Bin::WithPrefix>;
+ using OCT = IntegerToString<uint8_t, Oct::WithPrefix>;
+ using HEX = IntegerToString<uint8_t, Hex::WithPrefix>;
+ 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<uint64_t, Hex::Uppercase>;
+ EXPECT(HEX, 0xDEADC0DE, "DEADC0DE");
+}
+
+TEST(LlvmLibcIntegerToStringTest, Sign) {
+ // WithSign only compiles with DEC
+ using DEC = IntegerToString<int8_t, Dec::WithSign>;
+ 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<int, Dec::WithWidth<0>>::format_to(span<char>(), 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<int>::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<int>::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
index 8284e157a5b090..80d5deae9fc841 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -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",
More information about the libc-commits
mailing list