| #pragma once |
| #include <netinet/in.h> |
| |
| #include <stdplus/hash.hpp> |
| #include <stdplus/numeric/endian.hpp> |
| #include <stdplus/numeric/str.hpp> |
| #include <stdplus/str/conv.hpp> |
| #include <stdplus/variant.hpp> |
| |
| #include <algorithm> |
| #include <array> |
| #include <stdexcept> |
| #include <variant> |
| |
| namespace stdplus |
| { |
| |
| namespace detail |
| { |
| struct In4AddrInner |
| { |
| union |
| { |
| in_addr a; |
| uint32_t s4_addr32; |
| uint8_t s4_addr[4]; |
| }; |
| }; |
| static_assert(sizeof(In4AddrInner) == sizeof(in_addr)); |
| } // namespace detail |
| |
| struct In4Addr : detail::In4AddrInner |
| { |
| constexpr In4Addr() noexcept : In4AddrInner({.s4_addr = {}}) {} |
| constexpr In4Addr(in_addr a) noexcept : In4Addr() |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| this->a = a; |
| return; |
| } |
| auto arr = std::bit_cast<std::array<std::uint8_t, 4>>(a); |
| std::copy(arr.begin(), arr.end(), s4_addr); |
| } |
| explicit constexpr In4Addr(std::initializer_list<uint8_t> a) noexcept : |
| In4Addr() |
| { |
| std::copy(a.begin(), a.end(), s4_addr); |
| } |
| |
| constexpr std::uint8_t byte(std::size_t i) const noexcept |
| { |
| return s4_addr[i]; |
| } |
| |
| constexpr void byte(std::size_t i, std::uint8_t v) noexcept |
| { |
| s4_addr[i] = v; |
| } |
| |
| constexpr std::uint32_t word() const noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| return s4_addr32; |
| } |
| return std::bit_cast<std::uint32_t>(s4_addr); |
| } |
| |
| constexpr void word(std::uint32_t v) noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| s4_addr32 = v; |
| return; |
| } |
| auto arr = std::bit_cast<std::array<std::uint8_t, 4>>(v); |
| std::copy(arr.begin(), arr.end(), s4_addr); |
| } |
| |
| constexpr operator in_addr() const noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| return a; |
| } |
| return std::bit_cast<in_addr>(s4_addr); |
| } |
| |
| constexpr bool operator==(in_addr rhs) const noexcept |
| { |
| return *this == In4Addr{rhs}; |
| } |
| |
| constexpr bool operator==(In4Addr rhs) const noexcept |
| { |
| return word() == rhs.word(); |
| } |
| }; |
| |
| template <> |
| struct FromStr<In4Addr> |
| { |
| template <typename CharT> |
| constexpr In4Addr operator()(std::basic_string_view<CharT> sv) const |
| { |
| constexpr StrToInt<10, uint8_t> sti; |
| uint32_t addr = {}; |
| for (size_t i = 0; i < 3; ++i) |
| { |
| auto loc = sv.find("."); |
| addr |= sti(sv.substr(0, loc)); |
| addr <<= 8; |
| sv.remove_prefix(loc == sv.npos ? sv.size() : loc + 1); |
| if (sv.empty()) |
| { |
| throw std::invalid_argument("Missing addr data"); |
| } |
| } |
| addr |= sti(sv); |
| return in_addr{hton(addr)}; |
| } |
| }; |
| |
| template <> |
| struct ToStr<In4Addr> |
| { |
| using type = In4Addr; |
| using ToOct = IntToStr<10, uint8_t>; |
| // 4 octets * 3 dec chars + 3 separators |
| static constexpr std::size_t buf_size = 12 + ToOct::buf_size; |
| |
| template <typename CharT> |
| constexpr CharT* operator()(CharT* buf, In4Addr v) const noexcept |
| { |
| auto n = bswap(ntoh(v.word())); |
| for (size_t i = 0; i < 3; ++i) |
| { |
| buf = ToOct{}(buf, n & 0xff); |
| (buf++)[0] = '.'; |
| n >>= 8; |
| } |
| return ToOct{}(buf, n & 0xff); |
| } |
| }; |
| |
| struct In6Addr : in6_addr |
| { |
| constexpr In6Addr() noexcept : in6_addr() {} |
| constexpr In6Addr(in6_addr a) noexcept : in6_addr(a) |
| { |
| if (std::is_constant_evaluated()) |
| { |
| std::copy(a.s6_addr, a.s6_addr + std::size(s6_addr), s6_addr); |
| } |
| } |
| explicit constexpr In6Addr(std::initializer_list<uint8_t> a) noexcept : |
| In6Addr() |
| { |
| std::copy(a.begin(), a.end(), s6_addr); |
| } |
| |
| constexpr std::uint8_t byte(std::size_t i) const noexcept |
| { |
| return s6_addr[i]; |
| } |
| |
| constexpr void byte(std::size_t i, std::uint8_t v) noexcept |
| { |
| s6_addr[i] = v; |
| } |
| |
| constexpr std::uint16_t hextet(std::size_t i) const noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| return s6_addr16[i]; |
| } |
| std::uint8_t bytes[sizeof(std::uint16_t)]; |
| const auto start = s6_addr + i * std::size(bytes); |
| std::copy(start, start + std::size(bytes), bytes); |
| return std::bit_cast<std::uint16_t>(bytes); |
| } |
| |
| constexpr void hextet(std::size_t i, std::uint16_t v) noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| s6_addr16[i] = v; |
| return; |
| } |
| auto bytes = std::bit_cast<std::array<std::uint8_t, sizeof(v)>>(v); |
| std::copy(bytes.begin(), bytes.end(), s6_addr + i * bytes.size()); |
| } |
| |
| constexpr std::uint32_t word(std::size_t i) const noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| return s6_addr32[i]; |
| } |
| std::uint8_t bytes[sizeof(std::uint32_t)]; |
| const auto start = s6_addr + i * std::size(bytes); |
| std::copy(start, start + std::size(bytes), bytes); |
| return std::bit_cast<std::uint32_t>(bytes); |
| } |
| |
| constexpr void word(std::size_t i, std::uint32_t v) noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| s6_addr32[i] = v; |
| return; |
| } |
| auto bytes = std::bit_cast<std::array<std::uint8_t, sizeof(v)>>(v); |
| std::copy(bytes.begin(), bytes.end(), s6_addr + i * bytes.size()); |
| } |
| |
| constexpr bool operator==(in6_addr rhs) const noexcept |
| { |
| if (!std::is_constant_evaluated()) |
| { |
| return std::equal(s6_addr32, s6_addr32 + 4, rhs.s6_addr32); |
| } |
| return std::equal(s6_addr, s6_addr + 16, rhs.s6_addr); |
| } |
| |
| constexpr bool operator==(In6Addr rhs) const noexcept |
| { |
| return *this == static_cast<in6_addr&>(rhs); |
| } |
| }; |
| |
| template <> |
| struct FromStr<In6Addr> |
| { |
| template <typename CharT> |
| constexpr In6Addr operator()(std::basic_string_view<CharT> sv) const |
| { |
| constexpr StrToInt<16, uint16_t> sti; |
| constexpr FromStr<In4Addr> fsip4; |
| In6Addr ret = {}; |
| size_t i = 0; |
| while (i < 8) |
| { |
| auto loc = sv.find(':'); |
| if (i == 6 && loc == sv.npos) |
| { |
| ret.word(3, fsip4(sv).word()); |
| return ret; |
| } |
| if (loc != 0 && !sv.empty()) |
| { |
| ret.hextet(i++, hton(sti(sv.substr(0, loc)))); |
| } |
| if (i < 8 && sv.size() > loc + 1 && sv[loc + 1] == ':') |
| { |
| sv.remove_prefix(loc + 2); |
| break; |
| } |
| else if (sv.empty()) |
| { |
| throw std::invalid_argument("IPv6 Data"); |
| } |
| sv.remove_prefix(loc == sv.npos ? sv.size() : loc + 1); |
| } |
| if (sv.starts_with(':')) |
| { |
| throw std::invalid_argument("Extra separator"); |
| } |
| size_t j = 7; |
| if (!sv.empty() && i < 6 && sv.find('.') != sv.npos) |
| { |
| auto loc = sv.rfind(':'); |
| ret.word(3, fsip4(sv.substr(loc == sv.npos ? 0 : loc + 1)).word()); |
| sv.remove_suffix(loc == sv.npos ? sv.size() : sv.size() - loc); |
| j -= 2; |
| } |
| while (!sv.empty() && j > i) |
| { |
| auto loc = sv.rfind(':'); |
| ret.hextet(j--, hton(sti(sv.substr(loc == sv.npos ? 0 : loc + 1)))); |
| sv.remove_suffix(loc == sv.npos ? sv.size() : sv.size() - loc); |
| } |
| if (!sv.empty()) |
| { |
| throw std::invalid_argument("Too much data"); |
| } |
| return ret; |
| } |
| }; |
| |
| 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.word(0) == 0 && v.word(1) == 0 && v.hextet(4) == 0 && |
| v.hextet(5) == 0xffff) |
| { |
| constexpr auto prefix = std::string_view("::ffff:"); |
| return ToStr<In4Addr>{}( |
| std::copy(prefix.begin(), prefix.end(), buf), |
| in_addr{v.word(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.hextet(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.hextet(i))); |
| if (i < 7) |
| { |
| *(buf++) = ':'; |
| } |
| } |
| return buf; |
| } |
| }; |
| |
| namespace detail |
| { |
| using InAnyAddrV = std::variant<In4Addr, In6Addr>; |
| } |
| |
| struct InAnyAddr : detail::InAnyAddrV |
| { |
| constexpr InAnyAddr(in_addr a) noexcept : detail::InAnyAddrV(In4Addr{a}) {} |
| constexpr InAnyAddr(In4Addr a) noexcept : detail::InAnyAddrV(a) {} |
| constexpr InAnyAddr(in6_addr a) noexcept : detail::InAnyAddrV(In6Addr{a}) {} |
| constexpr InAnyAddr(In6Addr a) noexcept : detail::InAnyAddrV(a) {} |
| |
| constexpr bool operator==(auto rhs) const noexcept |
| { |
| return variantEqFuzzy(*this, rhs); |
| } |
| }; |
| |
| template <> |
| struct FromStr<InAnyAddr> |
| { |
| template <typename CharT> |
| constexpr InAnyAddr operator()(std::basic_string_view<CharT> sv) const |
| { |
| if (sv.find(':') == sv.npos) |
| { |
| return FromStr<In4Addr>{}(sv); |
| } |
| return FromStr<In6Addr>{}(sv); |
| } |
| }; |
| |
| template <> |
| struct ToStr<InAnyAddr> |
| { |
| using type = InAnyAddr; |
| static constexpr std::size_t buf_size = std::max(ToStr<In4Addr>::buf_size, |
| ToStr<In6Addr>::buf_size); |
| |
| template <typename CharT> |
| constexpr CharT* operator()(CharT* buf, InAnyAddr v) const noexcept |
| { |
| return std::visit([=](auto v) { return ToStr<decltype(v)>{}(buf, v); }, |
| v); |
| } |
| }; |
| |
| namespace detail |
| { |
| |
| template <typename Addr, typename CharT, std::size_t N> |
| struct CompileInAddr |
| { |
| CharT str[N - 1]; |
| Addr addr = {}; |
| bool valid = true; |
| |
| constexpr CompileInAddr(const CharT (&str)[N]) noexcept |
| { |
| std::copy(str, str + N - 1, this->str); |
| std::basic_string_view<CharT> sv{this->str, N - 1}; |
| try |
| { |
| addr = fromStr<Addr>(sv); |
| } |
| catch (...) |
| { |
| valid = false; |
| } |
| } |
| }; |
| |
| template <typename CharT, std::size_t N> |
| struct CompileIn4Addr : CompileInAddr<In4Addr, CharT, N> |
| { |
| constexpr CompileIn4Addr(const CharT (&str)[N]) noexcept : |
| CompileInAddr<In4Addr, CharT, N>(str) |
| {} |
| }; |
| |
| template <typename CharT, std::size_t N> |
| struct CompileIn6Addr : CompileInAddr<In6Addr, CharT, N> |
| { |
| constexpr CompileIn6Addr(const CharT (&str)[N]) noexcept : |
| CompileInAddr<In6Addr, CharT, N>(str) |
| {} |
| }; |
| |
| } // namespace detail |
| |
| inline namespace in_addr_literals |
| { |
| |
| template <detail::CompileIn4Addr Str> |
| constexpr auto operator"" _ip4() noexcept |
| { |
| static_assert(Str.valid, "stdplus::In4Addr"); |
| return Str.addr; |
| } |
| |
| template <detail::CompileIn6Addr Str> |
| constexpr auto operator"" _ip6() noexcept |
| { |
| static_assert(Str.valid, "stdplus::In6Addr"); |
| return Str.addr; |
| } |
| |
| } // namespace in_addr_literals |
| |
| } // namespace stdplus |
| |
| template <> |
| struct std::hash<stdplus::In4Addr> |
| { |
| constexpr std::size_t operator()(stdplus::In4Addr addr) const noexcept |
| { |
| return stdplus::hashMulti(addr.word()); |
| } |
| }; |
| |
| template <> |
| struct std::hash<stdplus::In6Addr> |
| { |
| constexpr std::size_t operator()(stdplus::In6Addr addr) const noexcept |
| { |
| if (std::is_constant_evaluated()) |
| { |
| return stdplus::hashMulti(addr.s6_addr); |
| } |
| return stdplus::hashMulti(addr.s6_addr32); |
| } |
| }; |
| |
| template <> |
| struct std::hash<stdplus::InAnyAddr> |
| { |
| inline std::size_t operator()(stdplus::InAnyAddr a) const noexcept |
| { |
| return std::hash<stdplus::detail::InAnyAddrV>{}(a); |
| } |
| }; |
| |
| template <typename CharT> |
| 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> |
| {}; |
| |
| template <typename CharT> |
| struct fmt::formatter<stdplus::InAnyAddr, CharT> : |
| stdplus::Format<stdplus::ToStr<stdplus::InAnyAddr>, CharT> |
| {}; |