types: Add constexpr in_addr parser

Change-Id: Id24af0b9722b3ed68ab7dc5175c841e86b94ab64
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.hpp b/src/types.hpp
index 3c0b6d9..1a87faa 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -286,6 +286,29 @@
     }
 };
 
+template <>
+struct ToAddr<in_addr>
+{
+    constexpr in_addr operator()(std::string_view str) const
+    {
+        constexpr DecodeInt<uint8_t, 10> di;
+        uint32_t addr = {};
+        for (size_t i = 0; i < 3; ++i)
+        {
+            auto loc = str.find(".");
+            addr |= di(str.substr(0, loc));
+            addr <<= 8;
+            str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
+            if (str.empty())
+            {
+                throw std::invalid_argument("Missing addr data");
+            }
+        }
+        addr |= di(str);
+        return {hton(addr)};
+    }
+};
+
 namespace detail
 {
 
diff --git a/test/test_types.cpp b/test/test_types.cpp
index f9e49b3..fc4a998 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -120,6 +120,22 @@
               ToAddr<ether_addr>{}("FFEEDDccbbaa"));
 }
 
+TEST(ToAddr, InAddr)
+{
+    EXPECT_THROW(ToAddr<in_addr>{}(""), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("0.0.0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("0.0.0."), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}(".0.0.0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("0.0.0.0.0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("x.0.0.0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("ff.0.0.0"), std::invalid_argument);
+    EXPECT_THROW(ToAddr<in_addr>{}("256.0.0.0"), std::overflow_error);
+
+    EXPECT_EQ((in_addr{}), ToAddr<in_addr>{}("0.0.0.0"));
+    EXPECT_EQ((in_addr{htonl(0xc0a80101)}), ToAddr<in_addr>{}("192.168.001.1"));
+}
+
 namespace detail
 {