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);
 }