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 @@ -190,8 +190,7 @@ 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; + cpp::string_view string_view = IntegerToString::dec(value, dec_buf); return string(string_view.data(), string_view.size()); } } // 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 @@ -60,9 +60,7 @@ 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; + return operator<<(int_to_str); } template , int> = 0> 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 @@ -169,8 +169,8 @@ (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) { + LIBC_INLINE static cpp::string_view 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); @@ -200,6 +200,45 @@ bin(T val, cpp::span buffer) { return convert<2>(val, buffer); } + + // Overloads for exactly sized char arrays. If the buffer is statically known + // then convert can return a string_view instead of an optional + // Uses a char reference as that represents known size and unknown data + template , + int> = 0> + LIBC_INLINE static cpp::string_view + convert(T val, char (&buffer)[bufsize()], bool lowercase = true) { + cpp::span span(buffer, buffer + sizeof(buffer)); + if (cpp::is_signed_v) + return convert_intmax(intmax_t(val), span, lowercase, BASE); + else + return convert_uintmax(uintmax_t(val), span, lowercase, BASE); + } + + template , int> = 0> + LIBC_INLINE static cpp::string_view dec(T val, + char (&buffer)[bufsize<10, T>()]) { + return convert<10>(val, buffer); + } + + template , int> = 0> + LIBC_INLINE static cpp::string_view hex(T val, + char (&buffer)[bufsize<16, T>()]) { + return convert<16>(val, buffer); + } + + template , int> = 0> + LIBC_INLINE static cpp::string_view oct(T val, + char (&buffer)[bufsize<8, T>()]) { + return convert<8>(val, buffer); + } + + template , int> = 0> + LIBC_INLINE static cpp::string_view bin(T val, + char (&buffer)[bufsize<2, T>()]) { + return convert<2>(val, buffer); + } }; } // namespace __llvm_libc 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 @@ -35,7 +35,7 @@ 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(line_number); __llvm_libc::write_to_stderr(": Assertion failed: '"); __llvm_libc::write_to_stderr(assertion); __llvm_libc::write_to_stderr("' in function: '"); 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 @@ -880,7 +880,7 @@ // 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); + cpp::string_view int_to_str = IntegerToString::dec(digits, buf); block_width = int_to_str.size(); } 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 @@ -48,8 +48,8 @@ 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)); + cpp::string_view view = IntegerToString::hex(Value, buf, false); + return "0x" + cpp::string(view.data(), view.size()); } // 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 @@ -51,8 +51,8 @@ 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)); + cpp::string_view view = IntegerToString::hex(t, buf, false); + return *this << "0x" << 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 @@ -19,7 +19,7 @@ using __llvm_libc::cpp::string_view; TEST(LlvmLibcIntegerToStringTest, UINT8) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize() + 1]; 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")); @@ -28,8 +28,18 @@ EXPECT_EQ(*IntegerToString::dec(uint8_t(-1), buf), string_view("255")); } +TEST(LlvmLibcIntegerToStringExactTest, 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")); +} + TEST(LlvmLibcIntegerToStringTest, INT8) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize() + 1]; 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")); @@ -40,8 +50,20 @@ EXPECT_EQ(*IntegerToString::dec(int8_t(INT8_MIN), buf), string_view("-128")); } +TEST(LlvmLibcIntegerToStringExactTest, 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")); +} + TEST(LlvmLibcIntegerToStringTest, UINT16) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -53,8 +75,21 @@ EXPECT_EQ(*IntegerToString::dec(uint16_t(-1), buf), string_view("65535")); } +TEST(LlvmLibcIntegerToStringExactTest, 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")); +} + TEST(LlvmLibcIntegerToStringTest, INT16) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -73,7 +108,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT32) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -95,7 +130,7 @@ } TEST(LlvmLibcIntegerToStringTest, INT32) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -133,7 +168,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT64) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -158,7 +193,7 @@ } TEST(LlvmLibcIntegerToStringTest, INT64) { - char buf[IntegerToString::dec_bufsize()]; + char buf[IntegerToString::dec_bufsize()+1]; 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")); @@ -200,7 +235,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_8) { - char buf[IntegerToString::oct_bufsize()]; + char buf[IntegerToString::oct_bufsize()+1]; EXPECT_EQ((*IntegerToString::oct(uint64_t(0), buf)), string_view("0")); EXPECT_EQ((*IntegerToString::oct(uint64_t(012345), buf)), string_view("12345")); @@ -211,7 +246,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_16) { - char buf[IntegerToString::hex_bufsize()]; + char buf[IntegerToString::hex_bufsize()+1]; 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)), @@ -223,7 +258,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_2) { - char buf[IntegerToString::bin_bufsize()]; + char buf[IntegerToString::bin_bufsize()+1]; EXPECT_EQ(*IntegerToString::bin(uint64_t(0), buf), string_view("0")); EXPECT_EQ(*IntegerToString::bin(uint64_t(0xf0c), buf), string_view("111100001100")); @@ -236,7 +271,7 @@ } TEST(LlvmLibcIntegerToStringTest, UINT64_Base_36) { - char buf[IntegerToString::bufsize<36, uint64_t>()]; + char buf[IntegerToString::bufsize<36, uint64_t>()+1]; EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(0), buf), string_view("0")); EXPECT_EQ(*IntegerToString::convert<36>(uint64_t(12345), buf), string_view("9ix")); @@ -251,40 +286,40 @@ } TEST(LlvmLibcIntegerToStringTest, UINT128_Base_16) { - char buf[IntegerToString::hex_bufsize()]; - EXPECT_EQ(*IntegerToString::hex(static_cast(0), buf), + char buf[IntegerToString::hex_bufsize()+1]; + EXPECT_EQ(IntegerToString::hex(static_cast(0), buf), string_view("00000000000000000000000000000000")); - EXPECT_EQ(*IntegerToString::hex(static_cast(0x12345), buf), + EXPECT_EQ(IntegerToString::hex(static_cast(0x12345), buf), string_view("00000000000000000000000000012345")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 112, buf)), + EXPECT_EQ((IntegerToString::hex(static_cast(0x1234) << 112, buf)), string_view("12340000000000000000000000000000")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 48, buf)), + EXPECT_EQ((IntegerToString::hex(static_cast(0x1234) << 48, buf)), string_view("00000000000000001234000000000000")); - EXPECT_EQ((*IntegerToString::hex(static_cast(0x1234) << 52, buf)), + EXPECT_EQ((IntegerToString::hex(static_cast(0x1234) << 52, buf)), string_view("00000000000000012340000000000000")); } TEST(LlvmLibcIntegerToStringTest, UINT256_Base_16) { using UInt256 = __llvm_libc::cpp::UInt<256>; - char buf[IntegerToString::hex_bufsize()]; + char buf[IntegerToString::hex_bufsize()+1]; EXPECT_EQ( - *IntegerToString::hex(static_cast(0), buf), + IntegerToString::hex(static_cast(0), buf), string_view( "0000000000000000000000000000000000000000000000000000000000000000")); EXPECT_EQ( - *IntegerToString::hex(static_cast(0x12345), buf), + IntegerToString::hex(static_cast(0x12345), buf), string_view( "0000000000000000000000000000000000000000000000000000000000012345")); EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 112, buf)), + (IntegerToString::hex(static_cast(0x1234) << 112, buf)), string_view( "0000000000000000000000000000000012340000000000000000000000000000")); EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 116, buf)), + (IntegerToString::hex(static_cast(0x1234) << 116, buf)), string_view( "0000000000000000000000000000000123400000000000000000000000000000")); EXPECT_EQ( - (*IntegerToString::hex(static_cast(0x1234) << 240, buf)), + (IntegerToString::hex(static_cast(0x1234) << 240, buf)), string_view( "1234000000000000000000000000000000000000000000000000000000000000")); }