net/addr/ip: Add IPv6 ToStr conversion
Change-Id: Icab45bc29732eb9dc6cf42532ec74fe1c48afa91
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 b312536..d42605c 100644
--- a/include/stdplus/net/addr/ip.hpp
+++ b/include/stdplus/net/addr/ip.hpp
@@ -181,6 +181,75 @@
}
};
+template <>
+struct ToStr<In6Addr>
+{
+ using type = In6Addr;
+ using ToHex = IntToStr<16, uint16_t>;
+ // 8 hextets * 4 hex chars + 7 separators
+ static constexpr std::size_t buf_size = 35 + ToHex::buf_size;
+
+ template <typename CharT>
+ constexpr CharT* operator()(CharT* buf, In6Addr v) const noexcept
+ {
+ // IPv4 in IPv6 Addr
+ if (v.s6_addr32[0] == 0 && v.s6_addr32[1] == 0 && v.s6_addr16[4] == 0 &&
+ v.s6_addr16[5] == 0xffff)
+ {
+ constexpr auto prefix = std::string_view("::ffff:");
+ return ToStr<In4Addr>{}(
+ std::copy(prefix.begin(), prefix.end(), buf),
+ in_addr{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 = ToHex{}(buf, ntoh(v.s6_addr16[i]));
+ if (i < 7)
+ {
+ *(buf++) = ':';
+ }
+ }
+ return buf;
+ }
+};
+
namespace detail
{
using InAnyAddrV = std::variant<In4Addr, In6Addr>;
@@ -232,3 +301,8 @@
struct fmt::formatter<stdplus::In4Addr, CharT> :
stdplus::Format<stdplus::ToStr<stdplus::In4Addr>, CharT>
{};
+
+template <typename CharT>
+struct fmt::formatter<stdplus::In6Addr, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::In6Addr>, CharT>
+{};
diff --git a/src/net/addr/ip.cpp b/src/net/addr/ip.cpp
index 16e328f..e411054 100644
--- a/src/net/addr/ip.cpp
+++ b/src/net/addr/ip.cpp
@@ -5,4 +5,5 @@
template In4Addr FromStr<In4Addr>::operator()(std::string_view) const;
template char* ToStr<In4Addr>::operator()(char*, In4Addr) const noexcept;
template In6Addr FromStr<In6Addr>::operator()(std::string_view) const;
+template char* ToStr<In6Addr>::operator()(char*, In6Addr) const noexcept;
} // namespace stdplus
diff --git a/test/net/addr/ip.cpp b/test/net/addr/ip.cpp
index 3e02404..7296a1d 100644
--- a/test/net/addr/ip.cpp
+++ b/test/net/addr/ip.cpp
@@ -102,6 +102,36 @@
fs("0:1:2:3:4:5:255.168.0.1"sv));
}
+TEST(ToStr, In6Addr)
+{
+ ToStrHandle<ToStr<In6Addr>> tsh;
+ EXPECT_EQ("::", tsh(In6Addr{}));
+ EXPECT_EQ("ff::", tsh(In6Addr{0, 0xff}));
+ EXPECT_EQ("::ff",
+ tsh(In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}));
+ EXPECT_EQ("0:0:ff::ff", tsh(In6Addr{0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0xff}));
+ EXPECT_EQ("::100:0:ff",
+ tsh(In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0xff}));
+ EXPECT_EQ("ff00::", tsh(In6Addr{0xff}));
+ EXPECT_EQ("1:2:3:4:5:6:7:8",
+ tsh(In6Addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8}));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ tsh(In6Addr{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255}));
+ // rfc5952 4.2.2
+ EXPECT_EQ("1:2:3:4:0:6:7:8",
+ tsh(In6Addr{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",
+ tsh(In6Addr{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", tsh(In6Addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xff, 0xff, 192, 168, 0, 1}));
+
+ EXPECT_EQ("a ff00:: b", fmt::format("a {} b", In6Addr{0xff}));
+}
+
TEST(EqualOperator, InAnyAddr)
{
EXPECT_EQ(InAnyAddr(In6Addr{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,