net/addr/ip: Add InAnyAddr compile time strings

Change-Id: Ib4bf557b9991afd3087577190dd4d03eb2abca1f
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 6987255..b2c5350 100644
--- a/include/stdplus/net/addr/ip.hpp
+++ b/include/stdplus/net/addr/ip.hpp
@@ -447,6 +447,40 @@
     {}
 };
 
+template <typename CharT, std::size_t N>
+struct CompileInAnyAddr
+{
+    CharT str[N - 1];
+    union
+    {
+        stdplus::In4Addr addr4;
+        stdplus::In6Addr addr6;
+    } u;
+    bool v4 = true;
+    bool valid = true;
+
+    constexpr CompileInAnyAddr(const CharT (&str)[N]) noexcept :
+        u({.addr4 = {}})
+    {
+        std::copy(str, str + N - 1, this->str);
+        std::basic_string_view<CharT> sv{this->str, N - 1};
+        try
+        {
+            if (sv.find(':') == sv.npos)
+            {
+                u = {.addr4 = FromStr<In4Addr>{}(sv)};
+                return;
+            }
+            u = {.addr6 = FromStr<In6Addr>{}(sv)};
+            v4 = false;
+        }
+        catch (...)
+        {
+            valid = false;
+        }
+    }
+};
+
 } // namespace detail
 
 inline namespace in_addr_literals
@@ -466,6 +500,13 @@
     return Str.addr;
 }
 
+template <detail::CompileInAnyAddr Str>
+constexpr auto operator"" _ip() noexcept
+{
+    static_assert(Str.valid, "stdplus::InAnyAddr");
+    return Str.v4 ? InAnyAddr(Str.u.addr4) : InAnyAddr(Str.u.addr6);
+}
+
 } // namespace in_addr_literals
 
 } // namespace stdplus
diff --git a/test/net/addr/ip.cpp b/test/net/addr/ip.cpp
index 4256e42..acb88c1 100644
--- a/test/net/addr/ip.cpp
+++ b/test/net/addr/ip.cpp
@@ -188,6 +188,30 @@
     EXPECT_EQ(
         (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 0, 1}),
         fs("::ffff:192.168.0.1"sv));
+
+    constexpr bool tv = detail::CompileInAnyAddr("192.0.0.0").valid;
+    EXPECT_TRUE(tv);
+    constexpr bool tv2 = detail::CompileInAnyAddr("::").valid;
+    EXPECT_TRUE(tv2);
+    EXPECT_EQ((In4Addr{}), "0.0.0.0"_ip);
+    EXPECT_EQ((In6Addr{}), "::"_ip);
+    EXPECT_EQ("ff02::"_ip, (In6Addr{0xff, 2}));
+    EXPECT_EQ("1::"_ip, (In6Addr{0, 1}));
+    EXPECT_EQ("100::"_ip, (In6Addr{1}));
+    EXPECT_EQ("2::"_ip, (In6Addr{0, 2}));
+    EXPECT_EQ("ff::"_ip, (In6Addr{0, 0xff}));
+    EXPECT_EQ("::100"_ip,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}));
+    EXPECT_EQ("::1"_ip,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
+    EXPECT_EQ("::2"_ip,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}));
+    EXPECT_EQ("::ff"_ip,
+              (In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}));
+    EXPECT_EQ("1::1"_ip,
+              (In6Addr{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
+    EXPECT_EQ("5::b"_ip,
+              (In6Addr{0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xb}));
 }
 
 TEST(ToStr, InAnyAddr)