| //===-- Utilities to convert integral values to string ----------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #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/optional.h" |
| #include "src/__support/CPP/span.h" |
| #include "src/__support/CPP/string_view.h" |
| #include "src/__support/CPP/type_traits.h" |
| #include "src/__support/common.h" |
| |
| 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; |
| |
| return cpp::string_view(buffer.data() + buffer.size() - len, len); |
| } |
| |
| 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); |
| } |
| |
| LIBC_INLINE static constexpr size_t floor_log_2(size_t num) { |
| size_t i = 0; |
| for (; num > 1; num /= 2) { |
| ++i; |
| } |
| return i; |
| } |
| |
| 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); |
| } |
| |
| template <typename T> LIBC_INLINE static constexpr size_t dec_bufsize() { |
| return bufsize<10, T>(); |
| } |
| |
| template <typename T> LIBC_INLINE static constexpr size_t hex_bufsize() { |
| return bufsize<16, T>(); |
| } |
| |
| template <typename T> LIBC_INLINE static constexpr size_t oct_bufsize() { |
| return bufsize<8, T>(); |
| } |
| |
| template <typename T> LIBC_INLINE static constexpr size_t bin_bufsize() { |
| return bufsize<2, T>(); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| template <typename T, |
| cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_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); |
| } |
| return cpp::string_view(buffer.data(), buffer.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); |
| } |
| |
| 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); |
| } |
| }; |
| |
| } // namespace __llvm_libc |
| |
| #endif // LLVM_LIBC_SRC_SUPPORT_INTEGER_TO_STRING_H |