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 @@ -79,6 +79,93 @@ return IntegerToString(val); } +constexpr size_t floor_log_2(size_t num) { + size_t i = 0; + for (; num > 1; num /= 2) { + ++i; + } + return i; +} + +template class BaseIntegerToString { +public: + // We size the string buffer using an approximation algorithm: + // + // digits = ceil(bits in T / bits per digit) + // + // The value of bits per digit here is floor(log_2(base)). This gives us an + // upper bound on the size, where we always round up to the size of a power of + // 2 base. This is exact for all powers of two, but is rather loose for all + // other numbers. For this reason, it's better to use the default + // IntegerToString class if the base is known to be 10. + + static constexpr size_t BITS_PER_DIGIT = floor_log_2(Base); + static constexpr size_t BUFSIZE = + ((sizeof(T) * 8 + (BITS_PER_DIGIT - 1)) / BITS_PER_DIGIT) + + (cpp::is_signed() ? 1 : 0); + +private: + static_assert( + 2 <= Base <= 36, + "BaseIntegerToString can only have a base between 2 and 36 inclusive."); + static_assert(cpp::is_integral_v, + "BaseIntegerToString can only be used with integral types."); + + using UnsignedType = cpp::make_unsigned_t; + + char strbuf[BUFSIZE] = {'\0'}; + size_t len = 0; + + constexpr void convert(T val, size_t conv_base, bool lowercase) { + // We can convert to larger bases, since they take fewer digits, but smaller + // bases may not fit. + if (conv_base < Base) { + return; + } + + const char a = lowercase ? 'a' : 'A'; + + UnsignedType uval = val < 0 ? UnsignedType(-val) : UnsignedType(val); + + size_t buffptr = BUFSIZE; + if (uval == 0) { + strbuf[buffptr - 1] = '0'; + --buffptr; + } else { + for (; uval > 0; --buffptr, uval /= conv_base) { + UnsignedType digit = (uval % conv_base); + strbuf[buffptr - 1] = digit < 10 ? digit + '0' : digit + a - 10; + } + } + len = BUFSIZE - buffptr; + + if (val < 0) { + // This branch will be taken only for negative signed values. + ++len; + strbuf[BUFSIZE - len] = '-'; + } + } + +public: + constexpr explicit BaseIntegerToString() { ; } + constexpr explicit BaseIntegerToString(T val) { convert(val, Base, true); } + + constexpr explicit BaseIntegerToString(T val, size_t conv_base) { + convert(val, conv_base, true); + } + + constexpr explicit BaseIntegerToString(T val, size_t conv_base, + bool lowercase) { + convert(val, conv_base, lowercase); + } + + cpp::StringView str() const { + return cpp::StringView(strbuf + BUFSIZE - len, len); + } + + operator cpp::StringView() const { return str(); } +}; + } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H