net/addr/subnet: Add ToStr conversion
Change-Id: Id06045fd638fa53eb5725eadd63cace148fc53f3
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/net/addr/subnet.hpp b/include/stdplus/net/addr/subnet.hpp
index e9a174c..7f898c5 100644
--- a/include/stdplus/net/addr/subnet.hpp
+++ b/include/stdplus/net/addr/subnet.hpp
@@ -1,5 +1,9 @@
+#include <fmt/core.h>
+
#include <stdplus/net/addr/ip.hpp>
#include <stdplus/numeric/endian.hpp>
+#include <stdplus/numeric/str.hpp>
+#include <stdplus/str/conv.hpp>
#include <limits>
#include <type_traits>
@@ -67,9 +71,13 @@
void invalidSubnetPfx(std::size_t pfx);
-template <typename Addr, typename Pfx>
+template <typename Addr_, typename Pfx_>
class Subnet46
{
+ public:
+ using Addr = Addr_;
+ using Pfx = Pfx_;
+
private:
static constexpr inline std::size_t maxPfx = sizeof(Addr) * 8;
static_assert(std::is_unsigned_v<Pfx> && std::is_integral_v<Pfx>);
@@ -115,24 +123,28 @@
} // namespace detail
-using Subnet4 = detail::Subnet46<In4Addr, uint8_t>;
-using Subnet6 = detail::Subnet46<In6Addr, uint8_t>;
+using Subnet4 = detail::Subnet46<In4Addr, std::uint8_t>;
+using Subnet6 = detail::Subnet46<In6Addr, std::uint8_t>;
class SubnetAny
{
+ public:
+ using Addr = InAnyAddr;
+ using Pfx = std::uint8_t;
+
private:
- InAnyAddr addr;
- uint8_t pfx;
+ Addr addr;
+ Pfx pfx;
public:
- constexpr SubnetAny(auto addr, uint8_t pfx) : addr(addr), pfx(pfx)
+ constexpr SubnetAny(auto addr, Pfx pfx) : addr(addr), pfx(pfx)
{
if (detail::addrBits(addr) < pfx)
{
detail::invalidSubnetPfx(pfx);
}
}
- constexpr SubnetAny(InAnyAddr addr, uint8_t pfx) : addr(addr), pfx(pfx)
+ constexpr SubnetAny(InAnyAddr addr, Pfx pfx) : addr(addr), pfx(pfx)
{
if (std::visit([](auto v) { return detail::addrBits(v); }, addr) < pfx)
{
@@ -192,4 +204,46 @@
}
};
+namespace detail
+{
+
+template <typename Subnet>
+struct SubnetToStr
+{
+ using type = Subnet;
+ using ToDec = IntToStr<10, typename Subnet::Pfx>;
+ // Addr + sep + 3 prefix chars
+ static constexpr std::size_t buf_size =
+ ToStr<typename Subnet::Addr>::buf_size + 1 + ToDec::buf_size;
+
+ template <typename CharT>
+ constexpr CharT* operator()(CharT* buf, Subnet v) const noexcept
+ {
+ buf = ToStr<typename Subnet::Addr>{}(buf, v.getAddr());
+ (buf++)[0] = '/';
+ return ToDec{}(buf, v.getPfx());
+ }
+};
+
+} // namespace detail
+
+template <typename Addr, typename Pfx>
+struct ToStr<detail::Subnet46<Addr, Pfx>> :
+ detail::SubnetToStr<detail::Subnet46<Addr, Pfx>>
+{};
+
+template <>
+struct ToStr<SubnetAny> : detail::SubnetToStr<SubnetAny>
+{};
+
} // namespace stdplus
+
+template <typename Addr, typename Pfx, typename CharT>
+struct fmt::formatter<stdplus::detail::Subnet46<Addr, Pfx>, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::detail::Subnet46<Addr, Pfx>>, CharT>
+{};
+
+template <typename CharT>
+struct fmt::formatter<stdplus::SubnetAny, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::SubnetAny>, CharT>
+{};
diff --git a/src/net/addr/subnet.cpp b/src/net/addr/subnet.cpp
index 36d6049..d7cb24f 100644
--- a/src/net/addr/subnet.cpp
+++ b/src/net/addr/subnet.cpp
@@ -15,4 +15,9 @@
template class Subnet46<In4Addr, uint8_t>;
template class Subnet46<In6Addr, uint8_t>;
+template char* SubnetToStr<Subnet4>::operator()(char*, Subnet4) const noexcept;
+template char* SubnetToStr<Subnet6>::operator()(char*, Subnet6) const noexcept;
+template char* SubnetToStr<SubnetAny>::operator()(char*,
+ SubnetAny) const noexcept;
+
} // namespace stdplus::detail
diff --git a/test/net/addr/subnet.cpp b/test/net/addr/subnet.cpp
index 561507c..5024928 100644
--- a/test/net/addr/subnet.cpp
+++ b/test/net/addr/subnet.cpp
@@ -51,6 +51,15 @@
EXPECT_TRUE(Subnet4(addr4Full, 0).contains(In4Addr{}));
}
+TEST(Subnet4, ToStr)
+{
+ ToStrHandle<ToStr<Subnet4>> tsh;
+ EXPECT_EQ("0.0.0.0/16", tsh(Subnet4({}, 16)));
+ EXPECT_EQ("255.0.255.255/28", tsh(Subnet4(In4Addr{255, 0, 255, 255}, 28)));
+ EXPECT_EQ("a 1.2.3.4/32 b",
+ fmt::format("a {} b", Subnet4(In4Addr{1, 2, 3, 4}, 32)));
+}
+
TEST(Subnet6, Basic)
{
EXPECT_NO_THROW(Subnet6(in6_addr{0xff}, 128));
@@ -102,6 +111,15 @@
EXPECT_TRUE(Subnet6(addr6Full, 0).contains(In6Addr{}));
}
+TEST(Subnet6, ToStr)
+{
+ ToStrHandle<ToStr<Subnet6>> tsh;
+ EXPECT_EQ("::/0", tsh(Subnet6({}, 0)));
+ EXPECT_EQ("ff00::/128", tsh(Subnet6(In6Addr{0xff}, 128)));
+ EXPECT_EQ("a 102:304::/32 b",
+ fmt::format("a {} b", Subnet6(In6Addr{1, 2, 3, 4}, 32)));
+}
+
TEST(SubnetAny, Basic)
{
EXPECT_NO_THROW(SubnetAny(in_addr{0xffffffff}, 32));
@@ -157,4 +175,15 @@
EXPECT_FALSE(SubnetAny(addr4Full, 32).contains(InAnyAddr{In4Addr{}}));
}
+TEST(SubnetAny, ToStr)
+{
+ ToStrHandle<ToStr<SubnetAny>> tsh;
+ EXPECT_EQ("0.0.0.0/16", tsh(SubnetAny(In4Addr{}, 16)));
+ EXPECT_EQ("ff00::/128", tsh(SubnetAny(In6Addr{0xff}, 128)));
+ EXPECT_EQ("a 102:304::/32 b",
+ fmt::format("a {} b", SubnetAny(In6Addr{1, 2, 3, 4}, 32)));
+ EXPECT_EQ("a 1.2.3.4/32 b",
+ fmt::format("a {} b", SubnetAny(In4Addr{1, 2, 3, 4}, 32)));
+}
+
} // namespace stdplus