types: Add constexpr int encoder
Change-Id: I4a3117f442c5a829faffbb5897eb2a0ea013f63a
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.hpp b/src/types.hpp
index 73d2778..4bbff0d 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -198,7 +198,19 @@
}
return ret;
}();
-}
+inline constexpr auto intLookup = []() {
+ std::array<char, 36> 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';
+ }
+ return ret;
+}();
+} // namespace detail
template <typename T, uint8_t base>
struct DecodeInt
@@ -249,6 +261,59 @@
}
};
+template <typename T, uint8_t base>
+struct EncodeInt
+{
+ static_assert(base > 1 && base <= 36);
+ static_assert(std::is_unsigned_v<T>);
+
+ static constexpr uint8_t buf_size = []() {
+ T v = std::numeric_limits<T>::max();
+ uint8_t i = 0;
+ for (; v != 0; ++i)
+ {
+ v /= base;
+ }
+ return i;
+ }();
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr uint8_t reverseFill(char* buf, T v) const noexcept
+ {
+ uint8_t i = 0;
+ do
+ {
+ if constexpr (std::popcount(base) == 1)
+ {
+ buf[i++] = detail::intLookup[v & 0xf];
+ v >>= 4;
+ }
+ else
+ {
+ buf[i++] = detail::intLookup[v % base];
+ v /= base;
+ }
+ } while (v > 0);
+ return i;
+ }
+
+ constexpr char* operator()(char* buf, T v) const noexcept
+ {
+ uint8_t i = reverseFill(buf, v);
+ std::reverse(buf, buf + i);
+ return buf + i;
+ }
+
+ constexpr char* operator()(char* buf, T v, uint8_t min_width) const noexcept
+ {
+ uint8_t i = reverseFill(buf, v);
+ auto end = buf + std::max(i, min_width);
+ std::fill(buf + i, end, '0');
+ std::reverse(buf, end);
+ return end;
+ }
+};
+
template <typename T>
struct ToAddr
{
diff --git a/test/test_types.cpp b/test/test_types.cpp
index 5c2fa78..c733ec2 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -74,6 +74,31 @@
EXPECT_THROW(d("100"), std::overflow_error);
}
+TEST(EncodeInt, uint8_10)
+{
+ EncodeInt<uint8_t, 10> e;
+ static_assert(e.buf_size == 3);
+ char buf[e.buf_size];
+ EXPECT_EQ("0", std::string_view(buf, e(buf, 0)));
+ EXPECT_EQ("42", std::string_view(buf, e(buf, 42)));
+ EXPECT_EQ("255", std::string_view(buf, e(buf, 255)));
+ EXPECT_EQ("000", std::string_view(buf, e(buf, 0, 3)));
+ EXPECT_EQ("255", std::string_view(buf, e(buf, 255, 3)));
+}
+
+TEST(EncodeInt, uint8_16)
+{
+ EncodeInt<uint8_t, 16> e;
+ static_assert(e.buf_size == 2);
+ char buf[e.buf_size];
+ EXPECT_EQ("0", std::string_view(buf, e(buf, 0)));
+ EXPECT_EQ("2a", std::string_view(buf, e(buf, 42)));
+ EXPECT_EQ("ff", std::string_view(buf, e(buf, 255)));
+ EXPECT_EQ("00", std::string_view(buf, e(buf, 0, 2)));
+ EXPECT_EQ("02", std::string_view(buf, e(buf, 2, 2)));
+ EXPECT_EQ("ff", std::string_view(buf, e(buf, 255, 2)));
+}
+
TEST(EqualOperator, InAddrAny)
{
EXPECT_EQ(InAddrAny(in6_addr{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,