types: Make all address conversion constexpr
Change-Id: If9d5e14e37e868c9a567c567ab8cb64913564a4f
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/types.cpp b/src/types.cpp
index ff49313..0d20d7d 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -4,119 +4,11 @@
#include <charconv>
-namespace phosphor::network
-{
-
-void IfAddr::invalidPfx(uint8_t pfx)
+void phosphor::network::IfAddr::invalidPfx(uint8_t pfx)
{
throw std::invalid_argument(fmt::format("Invalid prefix {}", pfx));
}
-namespace detail
-{
-
-std::string_view AddrBufMaker<ether_addr>::operator()(ether_addr val) noexcept
-{
- for (char* ptr = buf.data() + 2; ptr < buf.end(); ptr += 3)
- {
- *ptr = ':';
- }
- for (size_t i = 0; i < 6; ++i)
- {
- char* tmp = buf.data() + i * 3;
- uint8_t byte = val.ether_addr_octet[i];
- if (byte < 16)
- {
- *(tmp++) = '0';
- }
- std::to_chars(tmp, buf.end(), byte, 16);
- }
- return {buf.begin(), buf.size()};
-}
-
-static char* makeInAddr(char* ptr, char* end, in_addr val) noexcept
-{
- auto v = bswap(ntoh(val.s_addr));
- for (size_t i = 0; i < 3; ++i)
- {
- const auto res = std::to_chars(ptr, end, v & 0xff, 10);
- *res.ptr = '.';
- ptr = res.ptr + 1;
- v >>= 8;
- }
- 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;
- {
- size_t new_start = 0;
- size_t new_size = 0;
- for (size_t i = 0; i < 9; ++i)
- {
- if (i < 8 && val.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;
- }
- }
- }
- char* ptr = buf.begin();
- for (size_t i = 0; i < 8; ++i)
- {
- if (i == skip_start && skip_size > 1)
- {
- if (i == 0)
- {
- *(ptr++) = ':';
- }
- *(ptr++) = ':';
- i += skip_size - 1;
- continue;
- }
- const auto res =
- std::to_chars(ptr, buf.end(), ntoh(val.s6_addr16[i]), 16);
- ptr = res.ptr;
- if (i < 7)
- {
- *(ptr++) = ':';
- }
- }
- return {buf.data(), ptr};
-}
-
-} // namespace detail
-} // namespace phosphor::network
-
std::size_t std::hash<in_addr>::operator()(in_addr addr) const noexcept
{
return std::hash<decltype(addr.s_addr)>{}(addr.s_addr);
@@ -136,22 +28,24 @@
std::string std::to_string(ether_addr value)
{
- return string(phosphor::network::detail::AddrBufMaker<ether_addr>{}(value));
+ return string(phosphor::network::detail::ToStrBuf<ether_addr>{}(value));
}
std::string std::to_string(in_addr value)
{
- return string(phosphor::network::detail::AddrBufMaker<in_addr>{}(value));
+ return string(phosphor::network::detail::ToStrBuf<in_addr>{}(value));
}
std::string std::to_string(in6_addr value)
{
- return string(phosphor::network::detail::AddrBufMaker<in6_addr>{}(value));
+ return string(phosphor::network::detail::ToStrBuf<in6_addr>{}(value));
}
std::string std::to_string(phosphor::network::InAddrAny value)
{
- return std::visit([](auto v) { return std::to_string(v); }, value);
+ phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
+ return string(tsb(value));
}
std::string std::to_string(phosphor::network::IfAddr value)
{
- return fmt::to_string(value);
+ phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
+ return string(tsb(value));
}
diff --git a/src/types.hpp b/src/types.hpp
index 4bbff0d..4515211 100644
--- a/src/types.hpp
+++ b/src/types.hpp
@@ -462,6 +462,161 @@
}
};
+template <typename T>
+struct ToStr
+{
+};
+
+template <>
+struct ToStr<char>
+{
+ static constexpr uint8_t buf_size = 1;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, char v) const noexcept
+ {
+ buf[0] = v;
+ return buf + 1;
+ }
+};
+
+template <>
+struct ToStr<ether_addr>
+{
+ // 6 octets * 2 hex chars + 5 separators
+ static constexpr uint8_t buf_size = 17;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, ether_addr v) const noexcept
+ {
+ for (char* ptr = buf + 2; ptr < buf + buf_size; ptr += 3)
+ {
+ *ptr = ':';
+ }
+ for (size_t i = 0; i < 6; ++i)
+ {
+ char* tmp = buf + i * 3;
+ uint8_t byte = v.ether_addr_octet[i];
+ EncodeInt<uint8_t, 16>{}(tmp, byte, 2);
+ }
+ return buf + buf_size;
+ }
+};
+
+template <>
+struct ToStr<in_addr>
+{
+ // 4 octets * 3 dec chars + 3 separators
+ static constexpr uint8_t buf_size = 15;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, in_addr v) const noexcept
+ {
+ auto n = bswap(ntoh(v.s_addr));
+ for (size_t i = 0; i < 3; ++i)
+ {
+ buf = ToStr<char>{}(EncodeInt<uint8_t, 10>{}(buf, n & 0xff), '.');
+ n >>= 8;
+ }
+ return EncodeInt<uint8_t, 10>{}(buf, n & 0xff);
+ }
+};
+
+template <>
+struct ToStr<in6_addr>
+{
+ // 8 hextets * 4 hex chars + 7 separators
+ static constexpr uint8_t buf_size = 39;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, in6_addr v) const noexcept
+ {
+ // IPv4 in IPv6 Addr
+ if (v.s6_addr32[0] == 0 && v.s6_addr32[1] == 0 &&
+ v.s6_addr32[2] == hton(uint32_t(0xffff)))
+ {
+ constexpr auto prefix = std::string_view("::ffff:");
+ return ToStr<in_addr>{}(
+ std::copy(prefix.begin(), prefix.end(), buf), {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 = EncodeInt<uint16_t, 16>{}(buf, ntoh(v.s6_addr16[i]));
+ if (i < 7)
+ {
+ *(buf++) = ':';
+ }
+ }
+ return buf;
+ }
+};
+
+template <>
+struct ToStr<InAddrAny>
+{
+ // IPv6 is the bigger of the addrs
+ static constexpr uint8_t buf_size = ToStr<in6_addr>::buf_size;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, InAddrAny v) const noexcept
+ {
+ return std::visit([=](auto v) { return ToStr<decltype(v)>{}(buf, v); },
+ v);
+ }
+};
+
+template <>
+struct ToStr<IfAddr>
+{
+ // InAddrAny + sep + 3 prefix chars
+ static constexpr uint8_t buf_size = ToStr<InAddrAny>::buf_size + 4;
+ using buf_type = std::array<char, buf_size>;
+
+ constexpr char* operator()(char* buf, IfAddr v) const noexcept
+ {
+ buf = ToStr<InAddrAny>{}(buf, v.getAddr());
+ buf = ToStr<char>{}(buf, '/');
+ return EncodeInt<uint8_t, 10>{}(buf, v.getPfx());
+ }
+};
+
namespace detail
{
@@ -496,42 +651,20 @@
}
template <typename T>
-struct AddrBufMaker
-{
-};
-
-template <>
-struct AddrBufMaker<ether_addr>
+struct ToStrBuf
{
public:
- std::string_view operator()(ether_addr val) noexcept;
+ constexpr std::string_view operator()(T v) noexcept
+ {
+ return {buf.data(), ToStr<T>{}(buf.data(), v)};
+ }
private:
- std::array<char, /*octet*/ 2 * /*octets*/ 6 + /*seps*/ 5> buf;
+ typename ToStr<T>::buf_type buf;
};
-template <>
-struct AddrBufMaker<in_addr>
-{
- public:
- std::string_view operator()(in_addr val) noexcept;
-
- private:
- std::array<char, /*octet*/ 3 * /*octets*/ 4 + /*seps*/ 3> buf;
-};
-
-template <>
-struct AddrBufMaker<in6_addr>
-{
- public:
- std::string_view operator()(in6_addr val) noexcept;
-
- private:
- std::array<char, /*hextet*/ 4 * /*hextets*/ 8 + /*seps*/ 7> buf;
-};
-
-template <typename BufMaker>
-struct FormatFromBuf
+template <typename T>
+struct Format
{
private:
fmt::formatter<std::string_view> formatter;
@@ -546,7 +679,7 @@
template <typename FormatContext>
auto format(auto v, FormatContext& ctx) const
{
- return formatter.format(BufMaker{}(v), ctx);
+ return formatter.format(ToStrBuf<T>{}(v), ctx);
}
};
} // namespace detail
@@ -583,70 +716,26 @@
namespace fmt
{
template <>
-struct formatter<ether_addr>
- : phosphor::network::detail::FormatFromBuf<
- phosphor::network::detail::AddrBufMaker<ether_addr>>
+struct formatter<ether_addr> : phosphor::network::detail::Format<ether_addr>
{
};
template <>
-struct formatter<in_addr>
- : phosphor::network::detail::FormatFromBuf<
- phosphor::network::detail::AddrBufMaker<in_addr>>
+struct formatter<in_addr> : phosphor::network::detail::Format<in_addr>
{
};
template <>
-struct formatter<in6_addr>
- : phosphor::network::detail::FormatFromBuf<
- phosphor::network::detail::AddrBufMaker<in6_addr>>
+struct formatter<in6_addr> : phosphor::network::detail::Format<in6_addr>
{
};
template <>
struct formatter<phosphor::network::InAddrAny>
+ : phosphor::network::detail::Format<phosphor::network::InAddrAny>
{
- private:
- fmt::formatter<std::string_view> formatter;
-
- public:
- template <typename ParseContext>
- constexpr auto parse(ParseContext& ctx)
- {
- return ctx.begin();
- }
-
- template <typename FormatContext>
- auto format(auto v, FormatContext& ctx) const
- {
- return std::visit(
- [&](auto v) {
- auto abm =
- phosphor::network::detail::AddrBufMaker<decltype(v)>{};
- return formatter.format(abm(v), ctx);
- },
- v);
- }
};
template <>
struct formatter<phosphor::network::IfAddr>
+ : phosphor::network::detail::Format<phosphor::network::IfAddr>
{
- private:
- fmt::formatter<phosphor::network::InAddrAny> addrF;
- fmt::formatter<char> strF;
- fmt::formatter<uint8_t> numF;
-
- public:
- template <typename ParseContext>
- constexpr auto parse(ParseContext& ctx)
- {
- return ctx.begin();
- }
-
- template <typename FormatContext>
- auto format(auto v, FormatContext& ctx) const
- {
- addrF.format(v.getAddr(), ctx);
- strF.format('/', ctx);
- return numF.format(v.getPfx(), ctx);
- }
};
} // namespace fmt
@@ -685,30 +774,27 @@
auto& operator<<(auto& os, ether_addr v)
{
- return os << phosphor::network::detail::AddrBufMaker<ether_addr>{}(v);
+ return os << phosphor::network::detail::ToStrBuf<ether_addr>{}(v);
}
auto& operator<<(auto& os, in_addr v)
{
- return os << phosphor::network::detail::AddrBufMaker<in_addr>{}(v);
+ return os << phosphor::network::detail::ToStrBuf<in_addr>{}(v);
}
auto& operator<<(auto& os, in6_addr v)
{
- return os << phosphor::network::detail::AddrBufMaker<in6_addr>{}(v);
+ return os << phosphor::network::detail::ToStrBuf<in6_addr>{}(v);
}
auto& operator<<(auto& os, phosphor::network::InAddrAny v)
{
- return os << std::visit(
- [](auto v) {
- return phosphor::network::detail::AddrBufMaker<
- decltype(v)>{}(v);
- },
- v);
+ phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
+ return os << tsb(v);
}
auto& operator<<(auto& os, phosphor::network::IfAddr v)
{
- return os << v.getAddr() << "/" << std::dec << int{v.getPfx()};
+ phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
+ return os << tsb(v);
}
diff --git a/test/test_types.cpp b/test/test_types.cpp
index c733ec2..ae5c96d 100644
--- a/test/test_types.cpp
+++ b/test/test_types.cpp
@@ -185,6 +185,9 @@
EXPECT_THROW(ta("ffff0::0"), std::overflow_error);
EXPECT_EQ((in6_addr{}), ta("::"));
+ EXPECT_EQ((in6_addr{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255}),
+ ta("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv));
EXPECT_EQ((in6_addr{}), ta("0:0:0:0:0:0:0:0"));
EXPECT_EQ((in6_addr{0, 0xff}), ta("ff::"));
EXPECT_EQ((in6_addr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}),
@@ -233,7 +236,7 @@
TEST(BufMaker, EthAddr)
{
- AddrBufMaker<ether_addr> abm;
+ ToStrBuf<ether_addr> abm;
EXPECT_EQ("11:22:33:44:55:66"sv,
abm(ether_addr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}));
EXPECT_EQ("01:02:03:04:05:67"sv,
@@ -244,7 +247,7 @@
TEST(BufMaker, InAddr)
{
- AddrBufMaker<in_addr> abm;
+ ToStrBuf<in_addr> abm;
EXPECT_EQ("255.255.255.255"sv, abm(in_addr{0xffffffff}));
EXPECT_EQ("1.15.3.4"sv, abm(in_addr{htonl(0x010f0304)}));
EXPECT_EQ("0.0.0.0"sv, abm(in_addr{}));
@@ -252,7 +255,7 @@
TEST(BufMaker, In6Addr)
{
- AddrBufMaker<in6_addr> abm;
+ ToStrBuf<in6_addr> abm;
EXPECT_EQ("::"sv, abm(in6_addr{}));
EXPECT_EQ("ff::"sv, abm(in6_addr{0, 0xff}));
EXPECT_EQ("::ff"sv,
@@ -264,6 +267,9 @@
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}));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"sv,
+ abm(in6_addr{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"sv,
abm(in6_addr{0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 6, 0, 7, 0, 8}));
@@ -321,7 +327,7 @@
auto start = std::chrono::steady_clock::now();
for (size_t i = 0; i < 10000000; ++i)
{
- AddrBufMaker<in6_addr>{}(in6_addr{1});
+ ToStrBuf<in6_addr>{}(in6_addr{1});
}
fmt::print("Duration: {}\n", std::chrono::steady_clock::now() - start);
// Make sure this test isn't enabled