blob: 6987255473f8fe8063f3d42c7c721ebd8427c9de [file] [log] [blame]
#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>
{};