net/addr/ip: Add compile time string conversion

This adds new ""_ip{4,6} operators that expand into their respective
types at compile time.

Change-Id: I1830863682dac4e8e78234d51f6e92aa7358837e
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/net/addr/ip.hpp b/include/stdplus/net/addr/ip.hpp
index d9f2aea..6987255 100644
--- a/include/stdplus/net/addr/ip.hpp
+++ b/include/stdplus/net/addr/ip.hpp
@@ -14,6 +14,7 @@
 
 namespace stdplus
 {
+
 namespace detail
 {
 struct In4AddrInner
@@ -405,6 +406,68 @@
     }
 };
 
+namespace detail
+{
+
+template <typename Addr, typename CharT, std::size_t N>
+struct CompileInAddr
+{
+    CharT str[N - 1];
+    Addr addr = {};
+    bool valid = true;
+
+    constexpr CompileInAddr(const CharT (&str)[N]) noexcept
+    {
+        std::copy(str, str + N - 1, this->str);
+        std::basic_string_view<CharT> sv{this->str, N - 1};
+        try
+        {
+            addr = fromStr<Addr>(sv);
+        }
+        catch (...)
+        {
+            valid = false;
+        }
+    }
+};
+
+template <typename CharT, std::size_t N>
+struct CompileIn4Addr : CompileInAddr<In4Addr, CharT, N>
+{
+    constexpr CompileIn4Addr(const CharT (&str)[N]) noexcept :
+        CompileInAddr<In4Addr, CharT, N>(str)
+    {}
+};
+
+template <typename CharT, std::size_t N>
+struct CompileIn6Addr : CompileInAddr<In6Addr, CharT, N>
+{
+    constexpr CompileIn6Addr(const CharT (&str)[N]) noexcept :
+        CompileInAddr<In6Addr, CharT, N>(str)
+    {}
+};
+
+} // namespace detail
+
+inline namespace in_addr_literals
+{
+
+template <detail::CompileIn4Addr Str>
+constexpr auto operator"" _ip4() noexcept
+{
+    static_assert(Str.valid, "stdplus::In4Addr");
+    return Str.addr;
+}
+
+template <detail::CompileIn6Addr Str>
+constexpr auto operator"" _ip6() noexcept
+{
+    static_assert(Str.valid, "stdplus::In6Addr");
+    return Str.addr;
+}
+
+} // namespace in_addr_literals
+
 } // namespace stdplus
 
 template <>
diff --git a/test/net/addr/ip.cpp b/test/net/addr/ip.cpp
index 5493b69..4256e42 100644
--- a/test/net/addr/ip.cpp
+++ b/test/net/addr/ip.cpp
@@ -34,6 +34,11 @@
 
     EXPECT_EQ((In4Addr{}), fromStr<In4Addr>("0.0.0.0"));
     EXPECT_EQ((In4Addr{192, 168, 1, 1}), fromStr<In4Addr>("192.168.001.1"));
+
+    constexpr bool tv = detail::CompileIn4Addr("192.0.0.0").valid;
+    EXPECT_TRUE(tv);
+    constexpr bool t = "192.0.0.0"_ip4 == (In4Addr{192});
+    EXPECT_TRUE(t);
 }
 
 TEST(ToStr, In4Addr)
@@ -82,6 +87,7 @@
                        255, 255, 255, 255, 255}),
               fs("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv));
     EXPECT_EQ((In6Addr{}), fs("0:0:0:0:0:0:0:0"sv));
+    EXPECT_EQ((In6Addr{0, 1}), fs("1::"sv));
     EXPECT_EQ((In6Addr{0, 0xff}), fs("ff::"sv));
     EXPECT_EQ((In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}),
               fs("::ff"sv));
@@ -100,6 +106,27 @@
               fs("ff::255.168.0.1"sv));
     EXPECT_EQ((In6Addr{0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 255, 168, 0, 1}),
               fs("0:1:2:3:4:5:255.168.0.1"sv));
+
+    constexpr bool tv = detail::CompileIn6Addr("1::").valid;
+    EXPECT_TRUE(tv);
+    constexpr bool t = "ff02::"_ip6 == In6Addr{0xff, 2};
+    EXPECT_TRUE(t);
+    EXPECT_EQ("1::"_ip6, (In6Addr{0, 1}));
+    EXPECT_EQ("100::"_ip6, (In6Addr{1}));
+    EXPECT_EQ("2::"_ip6, (In6Addr{0, 2}));
+    EXPECT_EQ("ff::"_ip6, (In6Addr{0, 0xff}));
+    EXPECT_EQ("::100"_ip6,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}));
+    EXPECT_EQ("::1"_ip6,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
+    EXPECT_EQ("::2"_ip6,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}));
+    EXPECT_EQ("::ff"_ip6,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}));
+    EXPECT_EQ("1::1"_ip6,
+              (In6Addr{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
+    EXPECT_EQ("5::b"_ip6,
+              (In6Addr{0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xb}));
 }
 
 TEST(ToStr, In6Addr)