types: Make all address conversion constexpr

Change-Id: If9d5e14e37e868c9a567c567ab8cb64913564a4f
Signed-off-by: William A. Kennington III <wak@google.com>
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);
 }