types: Better IPv6 RFC5952 support

This makes sure our text output better conforms to standards.

Change-Id: I6d9ed5490d11b5e59c3c1bccabe0520fddeb6b56
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.cpp b/src/types.cpp
index 37eb69e..ff49313 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -34,23 +34,36 @@
     return {buf.begin(), buf.size()};
 }
 
-std::string_view AddrBufMaker<in_addr>::operator()(in_addr val) noexcept
+static char* makeInAddr(char* ptr, char* end, in_addr val) noexcept
 {
     auto v = bswap(ntoh(val.s_addr));
-    char* ptr = buf.begin();
     for (size_t i = 0; i < 3; ++i)
     {
-        const auto res = std::to_chars(ptr, buf.end(), v & 0xff, 10);
+        const auto res = std::to_chars(ptr, end, v & 0xff, 10);
         *res.ptr = '.';
         ptr = res.ptr + 1;
         v >>= 8;
     }
-    const auto res = std::to_chars(ptr, buf.end(), v & 0xff, 10);
-    return {buf.data(), res.ptr};
+    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;
     {
@@ -80,7 +93,7 @@
     char* ptr = buf.begin();
     for (size_t i = 0; i < 8; ++i)
     {
-        if (i == skip_start && skip_size > 0)
+        if (i == skip_start && skip_size > 1)
         {
             if (i == 0)
             {
diff --git a/test/test_types.cpp b/test/test_types.cpp
index fc4a998..09a9666 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -172,6 +172,16 @@
     EXPECT_EQ("ff00::"sv, abm(in6_addr{0xff}));
     EXPECT_EQ("1:2:3:4:5:6:7:8"sv,
               abm(in6_addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8}));
+    // rfc5952 4.2.2
+    EXPECT_EQ("1:2:3:4:0:6:7:8"sv,
+              abm(in6_addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 6, 0, 7, 0, 8}));
+    // rfc5952 4.2.3
+    EXPECT_EQ("1::4:0:0:7:8"sv,
+              abm(in6_addr{0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 7, 0, 8}));
+    // rfc5952 5
+    EXPECT_EQ("::ffff:192.168.0.1"sv,
+              abm(in6_addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168,
+                           0, 1}));
 }
 
 TEST(BasicOps, AllAddrs)