#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/algorithm.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"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/big_int.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace details {
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;
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>;
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");
};
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:
StringBufferWriterImpl(const StringBufferWriterImpl &) = delete;
StringBufferWriterImpl(cpp::span<char> buffer) : buffer(buffer) {}
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; }
LIBC_INLINE StringBufferWriterImpl &push(char c) {
if (ok()) {
if (!full()) {
buffer[location()] = c;
++index;
} else {
out_of_range = true;
}
}
return *this;
}
LIBC_INLINE cpp::span<char> remainder_span() const {
return forward ? buffer.last(remainder_size())
: buffer.first(remainder_size());
}
LIBC_INLINE cpp::span<char> buffer_span() const {
return forward ? buffer.first(size()) : buffer.last(size());
}
LIBC_INLINE cpp::string_view buffer_view() const {
const auto s = buffer_span();
return {s.data(), s.size()};
}
};
using StringBufferWriter = StringBufferWriterImpl<true>;
using BackwardStringBufferWriter = StringBufferWriterImpl<false>;
}
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, typename Fmt = radix::Dec> class IntegerToString {
static_assert(cpp::is_integral_v<T> || is_big_int_v<T>);
LIBC_INLINE static constexpr size_t compute_buffer_size() {
constexpr auto MAX_DIGITS = []() -> size_t {
if constexpr (Fmt::BASE == 10)
return ((sizeof(T) * 5) + 1) / 2;
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 size_t DIGIT_SIZE = cpp::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;
}
static constexpr size_t BUFFER_SIZE = compute_buffer_size();
static_assert(BUFFER_SIZE > 0);
struct IntegerWriter {
static_assert(cpp::is_integral_v<T> || is_big_int_v<T>);
using UNSIGNED_T = make_integral_or_big_int_unsigned_t<T>;
LIBC_INLINE static char digit_char(uint8_t digit) {
if (digit < 10)
return '0' + static_cast<char>(digit);
return (Fmt::IS_UPPERCASE ? 'A' : 'a') + static_cast<char>(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(static_cast<uint8_t>(value % Fmt::BASE));
sink.push(digit_char(digit));
}
}
LIBC_INLINE static UNSIGNED_T abs(T value) {
if (cpp::is_unsigned_v<T> || value >= 0)
return value;
if (value == cpp::numeric_limits<T>::min()) {
return cpp::bit_cast<UNSIGNED_T>(value);
} else {
return -value;
}
}
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(static_cast<UNSIGNED_T>(value), sink);
}
while (sink.ok() && sink.size() < Fmt::MIN_DIGITS)
sink.push('0');
if constexpr (Fmt::BASE == 10) {
if (value < 0)
sink.push('-');
else if (Fmt::FORCE_SIGN)
sink.push('+');
}
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');
}
}
}
};
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();
}
[[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;
}
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());
}
};
}
#endif