diff --git a/code/include/swoc/MemSpan.h b/code/include/swoc/MemSpan.h index 57482cd..be1e6bf 100644 --- a/code/include/swoc/MemSpan.h +++ b/code/include/swoc/MemSpan.h @@ -33,6 +33,17 @@ namespace swoc { inline namespace SWOC_VERSION_NS { avoid copying or allocation by allocating all needed memory at once and then working with it via instances of this class. + @note The issue of @c const correctness is tricky. Because this class is intended as a "smart" + pointer, its constancy does not carry over to its elements, just as a constant pointer doesn't + make its target constant. This makes it different than containers such as @c std::array or + @c std::vector. This means when creating an instance based on such containers the constancy of + the container affects the element type of the span. E.g. + + - A @c std::array maps to a @c MemSpan + - A @c const @c std::array maps to a @c MemSpan + + For convenience a @c MemSpan can be constructed from a @c MemSpan because this maintains + @c const correctness and models how a @c T @c const* can be constructed from a @c T* . */ template class MemSpan { using self_type = MemSpan; ///< Self reference type. @@ -71,14 +82,23 @@ template class MemSpan { * @tparam N Number of elements in the array. * @param a The array. */ - template MemSpan(T (&a)[N]); + template MemSpan(T (&a)[N]); - /** Construct from a @c std::array. + /** Construct from constant @c std::array. * * @tparam N Array size. * @param a Array instance. + * + * @note Because the elements in a constant array are constant the span value type must be constant. */ - template constexpr MemSpan(std::array const& a); + template < auto N, typename U + , typename META = std::enable_if_t< + std::conjunction_v< + std::is_const + , std::is_same, std::remove_const_t> + > + > + > constexpr MemSpan(std::array const& a); /** Construct from a @c std::array. * @@ -486,17 +506,23 @@ ptr_add(void *ptr, size_t count) { return static_cast(ptr) + count; } -/** Functor to convert span types. +/** Meta Function to check the type compatibility of two spans.. * * @tparam T Source span type. * @tparam U Destination span type. * + * The types are compatible if one is an integral multiple of the other, so the span divides evenly. + * + * @a U must not lose constancy compared to @a T. + * * @internal More void handling. This can't go in @c MemSpan because template specialization is * invalid in class scope and this needs to be specialized for @c void. */ template struct is_span_compatible { /// @c true if the size of @a T is an integral multiple of the size of @a U or vice versa. - static constexpr bool value = std::ratio::num == 1 || std::ratio::num == 1; + static constexpr bool value = + (std::ratio::num == 1 || std::ratio::num == 1) && + (std::is_const_v || ! std::is_const_v); // can't lose constancy. /** Compute the new size in units of @c sizeof(U). * * @param size Size in bytes. @@ -521,7 +547,7 @@ is_span_compatible::count(size_t size) { // Must specialize for rebinding to @c void because @c sizeof doesn't work. Rebinding from @c void // is handled by the @c MemSpan::rebind specialization and doesn't use this mechanism. template struct is_span_compatible { - static constexpr bool value = true; + static constexpr bool value = ! std::is_const_v; static size_t count(size_t size); }; @@ -627,11 +653,11 @@ template constexpr MemSpan::MemSpan(T *ptr, size_t count) : _ptr template constexpr MemSpan::MemSpan(T *first, T *last) : _ptr{first}, _count{detail::ptr_distance(first, last)} {} -template template MemSpan::MemSpan(T (&a)[N]) : _ptr{a}, _count{N} {} +template template MemSpan::MemSpan(T (&a)[N]) : _ptr{a}, _count{N} {} template constexpr MemSpan::MemSpan(std::nullptr_t) {} -template template constexpr MemSpan::MemSpan(std::array const& a) : _ptr{a.data()} , _count{a.size()} {} +template template constexpr MemSpan::MemSpan(std::array const& a) : _ptr{a.data()} , _count{a.size()} {} template template constexpr MemSpan::MemSpan(std::array & a) : _ptr{a.data()} , _count{a.size()} {} template @@ -808,8 +834,9 @@ template MemSpan MemSpan::rebind() const { static_assert(detail::is_span_compatible::value, - "MemSpan only allows rebinding between types who sizes are integral multiples."); - return {static_cast(static_cast(_ptr)), detail::is_span_compatible::count(this->size())}; + "MemSpan only allows rebinding between types where the sizes are such that one is an integral multiple of the other."); + using VOID_PTR = std::conditional_t, const void *, void*>; + return {static_cast(static_cast(_ptr)), detail::is_span_compatible::count(this->size())}; } template @@ -954,6 +981,10 @@ MemSpan::view() const { return {static_cast(_ptr), _size}; } +/// Deduction guide for constructing from a @c std::array. +template MemSpan(std::array &) -> MemSpan; +template MemSpan(std::array const &) -> MemSpan; + }} // namespace swoc::SWOC_VERSION_NS /// @cond NO_DOXYGEN diff --git a/code/include/swoc/swoc_ip.h b/code/include/swoc/swoc_ip.h index 430c0e2..2132c73 100644 --- a/code/include/swoc/swoc_ip.h +++ b/code/include/swoc/swoc_ip.h @@ -203,6 +203,7 @@ class IP4Addr { public: static constexpr size_t SIZE = sizeof(in_addr_t); ///< Size of IPv4 address in bytes. static constexpr size_t WIDTH = std::numeric_limits::digits * SIZE; ///< # of bits in an address. + static const self_type MIN; ///< Minimum value. static const self_type MAX; ///< Maximum value. static constexpr sa_family_t AF_value = AF_INET; ///< Address family type. @@ -350,8 +351,8 @@ class IP4Addr { */ class IP6Addr { using self_type = IP6Addr; ///< Self reference type. - friend class IP6Range; + friend class IP6Range; friend class IPMask; public: @@ -361,35 +362,13 @@ class IP6Addr { using quad_type = uint16_t; ///< Size of one segment of an IPv6 address. static constexpr size_t N_QUADS = SIZE / sizeof(quad_type); ///< # of quads in an IPv6 address. + /// Number of bits per quad. + static constexpr size_t QUAD_WIDTH = std::numeric_limits::digits * sizeof(quad_type); /// Direct access type for the address. /// Equivalent to the data type for data member @c s6_addr in @c in6_addr. using raw_type = std::array; - /// Direct access type for the address by quads (16 bits). - /// This corresponds to the elements of the text format of the address. - using quad_store_type = std::array; - - /// Number of bits per quad. - static constexpr size_t QUAD_WIDTH = std::numeric_limits::digits * sizeof(quad_type); - - /// A bit mask of all 1 bits the size of a quad. - static constexpr quad_type QUAD_MASK = ~quad_type{0}; - - /// Type used as a "word", the natural working unit of the address. - using word_type = uint64_t; - - static constexpr size_t WORD_SIZE = sizeof(word_type); - - /// Number of bits per word. - static constexpr size_t WORD_WIDTH = std::numeric_limits::digits * WORD_SIZE; - - /// Number of words used for basic address storage. - static constexpr size_t N_STORE = SIZE / WORD_SIZE; - - /// Type used to store the address. - using word_store_type = std::array; - /// Minimum value of an address. static const self_type MIN; /// Maximum value of an address. @@ -531,6 +510,14 @@ class IP6Addr { */ static void reorder(raw_type &dst, in6_addr const &src); + template < typename T > auto as_span() -> std::enable_if_t, swoc::MemSpan> { + return swoc::MemSpan(_addr._store).template rebind(); + } + + template < typename T > auto as_span() const -> std::enable_if_t, std::byte, uint8_t, uint16_t, uint32_t, uint64_t>, swoc::MemSpan> { + return swoc::MemSpan(_addr._store).template rebind(); + } + protected: friend bool operator==(self_type const &, self_type const &); @@ -540,6 +527,27 @@ class IP6Addr { friend bool operator<=(self_type const &, self_type const &); + /// Direct access type for the address by quads (16 bits). + /// This corresponds to the elements of the text format of the address. + using quad_store_type = std::array; + + /// A bit mask of all 1 bits the size of a quad. + static constexpr quad_type QUAD_MASK = ~quad_type{0}; + + /// Type used as a "word", the natural working unit of the address. + using word_type = uint64_t; + + static constexpr size_t WORD_SIZE = sizeof(word_type); + + /// Number of bits per word. + static constexpr size_t WORD_WIDTH = std::numeric_limits::digits * WORD_SIZE; + + /// Number of words used for basic address storage. + static constexpr size_t N_STORE = SIZE / WORD_SIZE; + + /// Type used to store the address. + using word_store_type = std::array; + /// Type for digging around inside the address, with the various forms of access. /// These are in sort of host order - @a _store elements are host order, but the /// MSW and LSW are swapped (big-endian). This makes various bits of the implementation @@ -3339,3 +3347,29 @@ get(swoc::IPNet const &net) { } }} // namespace swoc::SWOC_VERSION_NS + +namespace std { +template <> struct hash { + uint32_t operator()(swoc::IP4Addr const &addr) const { + return addr.network_order(); + } +}; + +template <> struct hash { + uint32_t operator()(swoc::IP6Addr const &addr) const { + // XOR the 64 chunks then XOR that down to 32 bits. + auto words = addr.as_span(); + union { + uint64_t w; + uint32_t n[2]; + } x{words[0] ^ words[1]}; + return x.n[0] ^ x.n[1]; + } +}; + +template <> struct hash { + uint32_t operator()(swoc::IPAddr const &addr) const { + return addr.is_ip4() ? hash()(addr.ip4()) : addr.is_ip6() ? hash()(addr.ip6()) : 0; + } +}; +} // namespace std diff --git a/unit_tests/test_MemSpan.cc b/unit_tests/test_MemSpan.cc index 64aaf72..91f81d1 100644 --- a/unit_tests/test_MemSpan.cc +++ b/unit_tests/test_MemSpan.cc @@ -112,3 +112,19 @@ TEST_CASE("MemSpan", "[libswoc][MemSpan]") REQUIRE(left.size() + span.size() == 1024); }; + +TEST_CASE("MemSpan conversions", "[libswoc][MemSpan]") +{ + std::array a1; + auto const & ra1 = a1; + auto ms1 = MemSpan(a1); + [[maybe_unused]] auto ms2 = MemSpan(a1); + [[maybe_unused]] auto ms3 = MemSpan(ra1); + [[maybe_unused]] auto ms4 = MemSpan(ra1); + // Construct a span of constant from a const ref to an array with non-const type. + MemSpan ms5 { ra1 }; + // Construct a span of constant from a ref to an array with non-const type. + MemSpan ms6 { a1 }; + + [[maybe_unused]] MemSpan c1 = ms1; // Conversion from T to T const. +}