|  | #include <stdplus/net/addr/ip.hpp> | 
|  | #include <stdplus/numeric/endian.hpp> | 
|  |  | 
|  | #include <limits> | 
|  | #include <type_traits> | 
|  |  | 
|  | namespace stdplus | 
|  | { | 
|  | namespace detail | 
|  | { | 
|  |  | 
|  | // AddressSan doesn't understand our masking of shift UB | 
|  | __attribute__((no_sanitize("undefined"))) constexpr uint32_t | 
|  | addr32Mask(std::ptrdiff_t pfx) noexcept | 
|  | { | 
|  | // Positive prefix check + mask to handle UB when the left shift becomes | 
|  | // more than 31 bits | 
|  | return hton(static_cast<uint32_t>(-int32_t{pfx > 0}) & ~uint32_t{0} | 
|  | << (32 - pfx)); | 
|  | } | 
|  |  | 
|  | constexpr In4Addr addrToSubnet(In4Addr a, std::size_t pfx) noexcept | 
|  | { | 
|  | return In4Addr{in_addr{a.s4_addr32 & addr32Mask(pfx)}}; | 
|  | } | 
|  |  | 
|  | constexpr In6Addr addrToSubnet(In6Addr a, std::size_t pfx, std::size_t i = 0, | 
|  | std::size_t s = 0, In6Addr ret = {}) noexcept | 
|  | { | 
|  | if (s + 32 < pfx) | 
|  | { | 
|  | ret.s6_addr32[i] = a.s6_addr32[i]; | 
|  | return addrToSubnet(a, pfx, i + 1, s + 32, ret); | 
|  | } | 
|  | ret.s6_addr32[i] = a.s6_addr32[i] & addr32Mask(pfx - s); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | constexpr InAnyAddr addrToSubnet(InAnyAddr a, std::size_t pfx) noexcept | 
|  | { | 
|  | return std::visit([&](auto av) { return InAnyAddr{addrToSubnet(av, pfx)}; }, | 
|  | a); | 
|  | } | 
|  |  | 
|  | constexpr bool subnetContains(auto, auto, std::size_t) noexcept | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | constexpr bool subnetContains(T l, T r, std::size_t pfx) noexcept | 
|  | { | 
|  | return addrToSubnet(l, pfx) == addrToSubnet(r, pfx); | 
|  | } | 
|  |  | 
|  | constexpr bool subnetContains(InAnyAddr l, auto r, std::size_t pfx) noexcept | 
|  | { | 
|  | return std::visit([&](auto v) { return detail::subnetContains(v, r, pfx); }, | 
|  | l); | 
|  | } | 
|  |  | 
|  | constexpr std::size_t addrBits(auto a) noexcept | 
|  | { | 
|  | return sizeof(a) << 3; | 
|  | } | 
|  |  | 
|  | void invalidSubnetPfx(std::size_t pfx); | 
|  |  | 
|  | template <typename Addr, typename Pfx> | 
|  | class Subnet46 | 
|  | { | 
|  | private: | 
|  | static constexpr inline std::size_t maxPfx = sizeof(Addr) * 8; | 
|  | static_assert(std::is_unsigned_v<Pfx> && std::is_integral_v<Pfx>); | 
|  | static_assert(std::numeric_limits<Pfx>::max() >= maxPfx); | 
|  |  | 
|  | Addr addr; | 
|  | Pfx pfx; | 
|  |  | 
|  | public: | 
|  | constexpr Subnet46(Addr addr, Pfx pfx) : addr(addr), pfx(pfx) | 
|  | { | 
|  | if (addrBits(addr) < pfx) | 
|  | { | 
|  | invalidSubnetPfx(pfx); | 
|  | } | 
|  | } | 
|  |  | 
|  | constexpr auto getAddr() const noexcept | 
|  | { | 
|  | return addr; | 
|  | } | 
|  |  | 
|  | constexpr auto getPfx() const noexcept | 
|  | { | 
|  | return pfx; | 
|  | } | 
|  |  | 
|  | constexpr bool operator==(Subnet46 rhs) const noexcept | 
|  | { | 
|  | return addr == rhs.addr && pfx == rhs.pfx; | 
|  | } | 
|  |  | 
|  | constexpr Addr network() const noexcept | 
|  | { | 
|  | return addrToSubnet(addr, pfx); | 
|  | } | 
|  |  | 
|  | constexpr bool contains(Addr addr) const noexcept | 
|  | { | 
|  | return addrToSubnet(this->addr, pfx) == addrToSubnet(addr, pfx); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace detail | 
|  |  | 
|  | using Subnet4 = detail::Subnet46<In4Addr, uint8_t>; | 
|  | using Subnet6 = detail::Subnet46<In6Addr, uint8_t>; | 
|  |  | 
|  | class SubnetAny | 
|  | { | 
|  | private: | 
|  | InAnyAddr addr; | 
|  | uint8_t pfx; | 
|  |  | 
|  | public: | 
|  | constexpr SubnetAny(auto addr, uint8_t pfx) : addr(addr), pfx(pfx) | 
|  | { | 
|  | if (detail::addrBits(addr) < pfx) | 
|  | { | 
|  | detail::invalidSubnetPfx(pfx); | 
|  | } | 
|  | } | 
|  | constexpr SubnetAny(InAnyAddr addr, uint8_t pfx) : addr(addr), pfx(pfx) | 
|  | { | 
|  | if (std::visit([](auto v) { return detail::addrBits(v); }, addr) < pfx) | 
|  | { | 
|  | detail::invalidSubnetPfx(pfx); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename T, typename S> | 
|  | constexpr SubnetAny(detail::Subnet46<T, S> o) noexcept : | 
|  | addr(o.getAddr()), pfx(o.getPfx()) | 
|  | {} | 
|  |  | 
|  | constexpr auto getAddr() const noexcept | 
|  | { | 
|  | return addr; | 
|  | } | 
|  |  | 
|  | constexpr auto getPfx() const noexcept | 
|  | { | 
|  | return pfx; | 
|  | } | 
|  |  | 
|  | template <typename T, typename S> | 
|  | constexpr bool operator==(detail::Subnet46<T, S> rhs) const noexcept | 
|  | { | 
|  | return addr == rhs.getAddr() && pfx == rhs.getPfx(); | 
|  | } | 
|  | constexpr bool operator==(SubnetAny rhs) const noexcept | 
|  | { | 
|  | return addr == rhs.addr && pfx == rhs.pfx; | 
|  | } | 
|  |  | 
|  | constexpr InAnyAddr network() const noexcept | 
|  | { | 
|  | return detail::addrToSubnet(addr, pfx); | 
|  | } | 
|  |  | 
|  | constexpr bool contains(In4Addr addr) const noexcept | 
|  | { | 
|  | return detail::subnetContains(this->addr, addr, pfx); | 
|  | } | 
|  | constexpr bool contains(in_addr addr) const noexcept | 
|  | { | 
|  | return contains(In4Addr{addr}); | 
|  | } | 
|  | constexpr bool contains(In6Addr addr) const noexcept | 
|  | { | 
|  | return detail::subnetContains(this->addr, addr, pfx); | 
|  | } | 
|  | constexpr bool contains(in6_addr addr) const noexcept | 
|  | { | 
|  | return contains(In6Addr{addr}); | 
|  | } | 
|  | constexpr bool contains(InAnyAddr addr) const noexcept | 
|  | { | 
|  | return std::visit([&](auto v) { return contains(v); }, addr); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace stdplus |