types: Make all address conversion constexpr

Change-Id: If9d5e14e37e868c9a567c567ab8cb64913564a4f
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.cpp b/src/types.cpp
index ff49313..0d20d7d 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -4,119 +4,11 @@
 
 #include <charconv>
 
-namespace phosphor::network
-{
-
-void IfAddr::invalidPfx(uint8_t pfx)
+void phosphor::network::IfAddr::invalidPfx(uint8_t pfx)
 {
     throw std::invalid_argument(fmt::format("Invalid prefix {}", pfx));
 }
 
-namespace detail
-{
-
-std::string_view AddrBufMaker<ether_addr>::operator()(ether_addr val) noexcept
-{
-    for (char* ptr = buf.data() + 2; ptr < buf.end(); ptr += 3)
-    {
-        *ptr = ':';
-    }
-    for (size_t i = 0; i < 6; ++i)
-    {
-        char* tmp = buf.data() + i * 3;
-        uint8_t byte = val.ether_addr_octet[i];
-        if (byte < 16)
-        {
-            *(tmp++) = '0';
-        }
-        std::to_chars(tmp, buf.end(), byte, 16);
-    }
-    return {buf.begin(), buf.size()};
-}
-
-static char* makeInAddr(char* ptr, char* end, in_addr val) noexcept
-{
-    auto v = bswap(ntoh(val.s_addr));
-    for (size_t i = 0; i < 3; ++i)
-    {
-        const auto res = std::to_chars(ptr, end, v & 0xff, 10);
-        *res.ptr = '.';
-        ptr = res.ptr + 1;
-        v >>= 8;
-    }
-    return std::to_chars(ptr, end, v & 0xff, 10).ptr;
-}
-
-std::string_view AddrBufMaker<in_addr>::operator()(in_addr val) noexcept
-{
-    return {buf.data(), makeInAddr(buf.data(), buf.end(), val)};
-}
-
-std::string_view AddrBufMaker<in6_addr>::operator()(in6_addr val) noexcept
-{
-    // IPv4 in IPv6 Addr
-    if (val.s6_addr32[0] == 0 && val.s6_addr32[1] == 0 &&
-        val.s6_addr32[2] == hton(uint32_t(0xffff)))
-    {
-        constexpr auto prefix = std::string_view("::ffff:");
-        return {buf.data(),
-                makeInAddr(std::copy(prefix.begin(), prefix.end(), buf.begin()),
-                           buf.end(), {val.s6_addr32[3]})};
-    }
-
-    size_t skip_start = 0;
-    size_t skip_size = 0;
-    {
-        size_t new_start = 0;
-        size_t new_size = 0;
-        for (size_t i = 0; i < 9; ++i)
-        {
-            if (i < 8 && val.s6_addr16[i] == 0)
-            {
-                if (new_start + new_size == i)
-                {
-                    new_size++;
-                }
-                else
-                {
-                    new_start = i;
-                    new_size = 1;
-                }
-            }
-            else if (new_start + new_size == i && new_size > skip_size)
-            {
-                skip_start = new_start;
-                skip_size = new_size;
-            }
-        }
-    }
-    char* ptr = buf.begin();
-    for (size_t i = 0; i < 8; ++i)
-    {
-        if (i == skip_start && skip_size > 1)
-        {
-            if (i == 0)
-            {
-                *(ptr++) = ':';
-            }
-            *(ptr++) = ':';
-            i += skip_size - 1;
-            continue;
-        }
-        const auto res =
-            std::to_chars(ptr, buf.end(), ntoh(val.s6_addr16[i]), 16);
-        ptr = res.ptr;
-        if (i < 7)
-        {
-            *(ptr++) = ':';
-        }
-    }
-    return {buf.data(), ptr};
-}
-
-} // namespace detail
-} // namespace phosphor::network
-
 std::size_t std::hash<in_addr>::operator()(in_addr addr) const noexcept
 {
     return std::hash<decltype(addr.s_addr)>{}(addr.s_addr);
@@ -136,22 +28,24 @@
 
 std::string std::to_string(ether_addr value)
 {
-    return string(phosphor::network::detail::AddrBufMaker<ether_addr>{}(value));
+    return string(phosphor::network::detail::ToStrBuf<ether_addr>{}(value));
 }
 std::string std::to_string(in_addr value)
 {
-    return string(phosphor::network::detail::AddrBufMaker<in_addr>{}(value));
+    return string(phosphor::network::detail::ToStrBuf<in_addr>{}(value));
 }
 std::string std::to_string(in6_addr value)
 {
-    return string(phosphor::network::detail::AddrBufMaker<in6_addr>{}(value));
+    return string(phosphor::network::detail::ToStrBuf<in6_addr>{}(value));
 }
 std::string std::to_string(phosphor::network::InAddrAny value)
 {
-    return std::visit([](auto v) { return std::to_string(v); }, value);
+    phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
+    return string(tsb(value));
 }
 
 std::string std::to_string(phosphor::network::IfAddr value)
 {
-    return fmt::to_string(value);
+    phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
+    return string(tsb(value));
 }
diff --git a/src/types.hpp b/src/types.hpp
index 4bbff0d..4515211 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -462,6 +462,161 @@
     }
 };
 
+template <typename T>
+struct ToStr
+{
+};
+
+template <>
+struct ToStr<char>
+{
+    static constexpr uint8_t buf_size = 1;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, char v) const noexcept
+    {
+        buf[0] = v;
+        return buf + 1;
+    }
+};
+
+template <>
+struct ToStr<ether_addr>
+{
+    // 6 octets * 2 hex chars + 5 separators
+    static constexpr uint8_t buf_size = 17;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, ether_addr v) const noexcept
+    {
+        for (char* ptr = buf + 2; ptr < buf + buf_size; ptr += 3)
+        {
+            *ptr = ':';
+        }
+        for (size_t i = 0; i < 6; ++i)
+        {
+            char* tmp = buf + i * 3;
+            uint8_t byte = v.ether_addr_octet[i];
+            EncodeInt<uint8_t, 16>{}(tmp, byte, 2);
+        }
+        return buf + buf_size;
+    }
+};
+
+template <>
+struct ToStr<in_addr>
+{
+    // 4 octets * 3 dec chars + 3 separators
+    static constexpr uint8_t buf_size = 15;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, in_addr v) const noexcept
+    {
+        auto n = bswap(ntoh(v.s_addr));
+        for (size_t i = 0; i < 3; ++i)
+        {
+            buf = ToStr<char>{}(EncodeInt<uint8_t, 10>{}(buf, n & 0xff), '.');
+            n >>= 8;
+        }
+        return EncodeInt<uint8_t, 10>{}(buf, n & 0xff);
+    }
+};
+
+template <>
+struct ToStr<in6_addr>
+{
+    // 8 hextets * 4 hex chars + 7 separators
+    static constexpr uint8_t buf_size = 39;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, in6_addr v) const noexcept
+    {
+        // IPv4 in IPv6 Addr
+        if (v.s6_addr32[0] == 0 && v.s6_addr32[1] == 0 &&
+            v.s6_addr32[2] == hton(uint32_t(0xffff)))
+        {
+            constexpr auto prefix = std::string_view("::ffff:");
+            return ToStr<in_addr>{}(
+                std::copy(prefix.begin(), prefix.end(), buf), {v.s6_addr32[3]});
+        }
+
+        size_t skip_start = 0;
+        size_t skip_size = 0;
+        {
+            size_t new_start = 0;
+            size_t new_size = 0;
+            for (size_t i = 0; i < 9; ++i)
+            {
+                if (i < 8 && v.s6_addr16[i] == 0)
+                {
+                    if (new_start + new_size == i)
+                    {
+                        new_size++;
+                    }
+                    else
+                    {
+                        new_start = i;
+                        new_size = 1;
+                    }
+                }
+                else if (new_start + new_size == i && new_size > skip_size)
+                {
+                    skip_start = new_start;
+                    skip_size = new_size;
+                }
+            }
+        }
+        for (size_t i = 0; i < 8; ++i)
+        {
+            if (i == skip_start && skip_size > 1)
+            {
+                if (i == 0)
+                {
+                    *(buf++) = ':';
+                }
+                *(buf++) = ':';
+                i += skip_size - 1;
+                continue;
+            }
+            buf = EncodeInt<uint16_t, 16>{}(buf, ntoh(v.s6_addr16[i]));
+            if (i < 7)
+            {
+                *(buf++) = ':';
+            }
+        }
+        return buf;
+    }
+};
+
+template <>
+struct ToStr<InAddrAny>
+{
+    // IPv6 is the bigger of the addrs
+    static constexpr uint8_t buf_size = ToStr<in6_addr>::buf_size;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, InAddrAny v) const noexcept
+    {
+        return std::visit([=](auto v) { return ToStr<decltype(v)>{}(buf, v); },
+                          v);
+    }
+};
+
+template <>
+struct ToStr<IfAddr>
+{
+    // InAddrAny + sep + 3 prefix chars
+    static constexpr uint8_t buf_size = ToStr<InAddrAny>::buf_size + 4;
+    using buf_type = std::array<char, buf_size>;
+
+    constexpr char* operator()(char* buf, IfAddr v) const noexcept
+    {
+        buf = ToStr<InAddrAny>{}(buf, v.getAddr());
+        buf = ToStr<char>{}(buf, '/');
+        return EncodeInt<uint8_t, 10>{}(buf, v.getPfx());
+    }
+};
+
 namespace detail
 {
 
@@ -496,42 +651,20 @@
 }
 
 template <typename T>
-struct AddrBufMaker
-{
-};
-
-template <>
-struct AddrBufMaker<ether_addr>
+struct ToStrBuf
 {
   public:
-    std::string_view operator()(ether_addr val) noexcept;
+    constexpr std::string_view operator()(T v) noexcept
+    {
+        return {buf.data(), ToStr<T>{}(buf.data(), v)};
+    }
 
   private:
-    std::array<char, /*octet*/ 2 * /*octets*/ 6 + /*seps*/ 5> buf;
+    typename ToStr<T>::buf_type buf;
 };
 
-template <>
-struct AddrBufMaker<in_addr>
-{
-  public:
-    std::string_view operator()(in_addr val) noexcept;
-
-  private:
-    std::array<char, /*octet*/ 3 * /*octets*/ 4 + /*seps*/ 3> buf;
-};
-
-template <>
-struct AddrBufMaker<in6_addr>
-{
-  public:
-    std::string_view operator()(in6_addr val) noexcept;
-
-  private:
-    std::array<char, /*hextet*/ 4 * /*hextets*/ 8 + /*seps*/ 7> buf;
-};
-
-template <typename BufMaker>
-struct FormatFromBuf
+template <typename T>
+struct Format
 {
   private:
     fmt::formatter<std::string_view> formatter;
@@ -546,7 +679,7 @@
     template <typename FormatContext>
     auto format(auto v, FormatContext& ctx) const
     {
-        return formatter.format(BufMaker{}(v), ctx);
+        return formatter.format(ToStrBuf<T>{}(v), ctx);
     }
 };
 } // namespace detail
@@ -583,70 +716,26 @@
 namespace fmt
 {
 template <>
-struct formatter<ether_addr>
-    : phosphor::network::detail::FormatFromBuf<
-          phosphor::network::detail::AddrBufMaker<ether_addr>>
+struct formatter<ether_addr> : phosphor::network::detail::Format<ether_addr>
 {
 };
 template <>
-struct formatter<in_addr>
-    : phosphor::network::detail::FormatFromBuf<
-          phosphor::network::detail::AddrBufMaker<in_addr>>
+struct formatter<in_addr> : phosphor::network::detail::Format<in_addr>
 {
 };
 template <>
-struct formatter<in6_addr>
-    : phosphor::network::detail::FormatFromBuf<
-          phosphor::network::detail::AddrBufMaker<in6_addr>>
+struct formatter<in6_addr> : phosphor::network::detail::Format<in6_addr>
 {
 };
 template <>
 struct formatter<phosphor::network::InAddrAny>
+    : phosphor::network::detail::Format<phosphor::network::InAddrAny>
 {
-  private:
-    fmt::formatter<std::string_view> formatter;
-
-  public:
-    template <typename ParseContext>
-    constexpr auto parse(ParseContext& ctx)
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext>
-    auto format(auto v, FormatContext& ctx) const
-    {
-        return std::visit(
-            [&](auto v) {
-                auto abm =
-                    phosphor::network::detail::AddrBufMaker<decltype(v)>{};
-                return formatter.format(abm(v), ctx);
-            },
-            v);
-    }
 };
 template <>
 struct formatter<phosphor::network::IfAddr>
+    : phosphor::network::detail::Format<phosphor::network::IfAddr>
 {
-  private:
-    fmt::formatter<phosphor::network::InAddrAny> addrF;
-    fmt::formatter<char> strF;
-    fmt::formatter<uint8_t> numF;
-
-  public:
-    template <typename ParseContext>
-    constexpr auto parse(ParseContext& ctx)
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext>
-    auto format(auto v, FormatContext& ctx) const
-    {
-        addrF.format(v.getAddr(), ctx);
-        strF.format('/', ctx);
-        return numF.format(v.getPfx(), ctx);
-    }
 };
 } // namespace fmt
 
@@ -685,30 +774,27 @@
 
 auto& operator<<(auto& os, ether_addr v)
 {
-    return os << phosphor::network::detail::AddrBufMaker<ether_addr>{}(v);
+    return os << phosphor::network::detail::ToStrBuf<ether_addr>{}(v);
 }
 
 auto& operator<<(auto& os, in_addr v)
 {
-    return os << phosphor::network::detail::AddrBufMaker<in_addr>{}(v);
+    return os << phosphor::network::detail::ToStrBuf<in_addr>{}(v);
 }
 
 auto& operator<<(auto& os, in6_addr v)
 {
-    return os << phosphor::network::detail::AddrBufMaker<in6_addr>{}(v);
+    return os << phosphor::network::detail::ToStrBuf<in6_addr>{}(v);
 }
 
 auto& operator<<(auto& os, phosphor::network::InAddrAny v)
 {
-    return os << std::visit(
-               [](auto v) {
-                   return phosphor::network::detail::AddrBufMaker<
-                       decltype(v)>{}(v);
-               },
-               v);
+    phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
+    return os << tsb(v);
 }
 
 auto& operator<<(auto& os, phosphor::network::IfAddr v)
 {
-    return os << v.getAddr() << "/" << std::dec << int{v.getPfx()};
+    phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
+    return os << tsb(v);
 }
diff --git a/test/test_types.cpp b/test/test_types.cpp
index c733ec2..ae5c96d 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -185,6 +185,9 @@
     EXPECT_THROW(ta("ffff0::0"), std::overflow_error);
 
     EXPECT_EQ((in6_addr{}), ta("::"));
+    EXPECT_EQ((in6_addr{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+                        255, 255, 255, 255, 255}),
+              ta("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv));
     EXPECT_EQ((in6_addr{}), ta("0:0:0:0:0:0:0:0"));
     EXPECT_EQ((in6_addr{0, 0xff}), ta("ff::"));
     EXPECT_EQ((in6_addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}),
@@ -233,7 +236,7 @@
 
 TEST(BufMaker, EthAddr)
 {
-    AddrBufMaker<ether_addr> abm;
+    ToStrBuf<ether_addr> abm;
     EXPECT_EQ("11:22:33:44:55:66"sv,
               abm(ether_addr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}));
     EXPECT_EQ("01:02:03:04:05:67"sv,
@@ -244,7 +247,7 @@
 
 TEST(BufMaker, InAddr)
 {
-    AddrBufMaker<in_addr> abm;
+    ToStrBuf<in_addr> abm;
     EXPECT_EQ("255.255.255.255"sv, abm(in_addr{0xffffffff}));
     EXPECT_EQ("1.15.3.4"sv, abm(in_addr{htonl(0x010f0304)}));
     EXPECT_EQ("0.0.0.0"sv, abm(in_addr{}));
@@ -252,7 +255,7 @@
 
 TEST(BufMaker, In6Addr)
 {
-    AddrBufMaker<in6_addr> abm;
+    ToStrBuf<in6_addr> abm;
     EXPECT_EQ("::"sv, abm(in6_addr{}));
     EXPECT_EQ("ff::"sv, abm(in6_addr{0, 0xff}));
     EXPECT_EQ("::ff"sv,
@@ -264,6 +267,9 @@
     EXPECT_EQ("ff00::"sv, abm(in6_addr{0xff}));
     EXPECT_EQ("1:2:3:4:5:6:7:8"sv,
               abm(in6_addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8}));
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv,
+              abm(in6_addr{255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+                           255, 255, 255, 255, 255, 255}));
     // rfc5952 4.2.2
     EXPECT_EQ("1:2:3:4:0:6:7:8"sv,
               abm(in6_addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 6, 0, 7, 0, 8}));
@@ -321,7 +327,7 @@
     auto start = std::chrono::steady_clock::now();
     for (size_t i = 0; i < 10000000; ++i)
     {
-        AddrBufMaker<in6_addr>{}(in6_addr{1});
+        ToStrBuf<in6_addr>{}(in6_addr{1});
     }
     fmt::print("Duration: {}\n", std::chrono::steady_clock::now() - start);
     // Make sure this test isn't enabled