numeric/str: Add constexpr int encode
This makes it possible to quickly convert numbers to strings in
constexpr contexts.
Change-Id: I5a460feacf97c80761d02a3b07f5b22080401b45
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index 979478e..56f9c8e 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -13,6 +13,7 @@
'stdplus/net/addr/ip.hpp',
'stdplus/net/addr/subnet.hpp',
'stdplus/numeric/endian.hpp',
+ 'stdplus/numeric/str.hpp',
'stdplus/pinned.hpp',
'stdplus/raw.hpp',
'stdplus/signal.hpp',
diff --git a/include/stdplus/numeric/str.hpp b/include/stdplus/numeric/str.hpp
new file mode 100644
index 0000000..ee80905
--- /dev/null
+++ b/include/stdplus/numeric/str.hpp
@@ -0,0 +1,111 @@
+#pragma once
+#include <stdplus/str/conv.hpp>
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <concepts>
+#include <cstdint>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+namespace stdplus
+{
+
+namespace detail
+{
+
+inline constexpr auto maxBase = 36;
+
+inline constexpr auto singleIntTable = []() {
+ std::array<char, maxBase> ret;
+ for (int8_t i = 0; i < 10; ++i)
+ {
+ ret[i] = i + '0';
+ }
+ for (int8_t i = 0; i < 26; ++i)
+ {
+ ret[i + 10] = i + 'a';
+ }
+ static_assert(maxBase == 36);
+ return ret;
+}();
+
+template <uint8_t base, typename T, typename CharT>
+constexpr CharT* uintToStr(CharT* buf, T v, uint8_t min_width) noexcept
+{
+ static_assert(std::is_unsigned_v<T>);
+ uint8_t i = 0;
+ do
+ {
+ if constexpr (std::popcount(base) == 1)
+ {
+ constexpr auto shift = std::countr_zero(base);
+ constexpr auto mask = (1 << shift) - 1;
+ buf[i] = detail::singleIntTable[v & mask];
+ v >>= shift;
+ }
+ else
+ {
+ buf[i] = detail::singleIntTable[v % base];
+ v /= base;
+ }
+ i += 1;
+ } while (v > 0);
+ auto end = buf + std::max(i, min_width);
+ std::fill(buf + i, end, '0');
+ std::reverse(buf, end);
+ return end;
+}
+
+template <uint8_t base, std::integral T, typename CharT>
+constexpr CharT* intToStr(CharT* buf, T v, uint8_t min_width) noexcept
+{
+ if constexpr (std::is_signed_v<T>)
+ {
+ if (v < 0)
+ {
+ *(buf++) = '-';
+ v = -v;
+ }
+ }
+ return uintToStr<base>(buf, std::make_unsigned_t<T>(v), min_width);
+}
+
+} // namespace detail
+
+template <uint8_t base, std::integral T>
+struct IntToStr
+{
+ static_assert(base > 1 && base <= detail::maxBase);
+
+ static inline constexpr size_t buf_size = []() {
+ T v = std::numeric_limits<T>::max();
+ uint8_t i = 0;
+ for (; v != 0; ++i)
+ {
+ v /= base;
+ }
+ return i + std::is_signed_v<T>;
+ }();
+
+ template <typename CharT>
+ constexpr CharT* operator()(CharT* buf, T v,
+ uint8_t min_width = 0) const noexcept
+ {
+ using ptr_t =
+ std::conditional_t<std::is_signed_v<T>, intptr_t, uintptr_t>;
+ return detail::intToStr<
+ base, std::conditional_t<sizeof(T) <= sizeof(ptr_t), ptr_t, T>>(
+ buf, v, min_width);
+ }
+};
+
+template <std::integral T>
+struct ToStr<T> : IntToStr<10, T>
+{
+ using type = T;
+};
+
+} // namespace stdplus