code/include/swoc/MemSpan.h (856 lines of code) (raw):

// SPDX-License-Identifier: Apache-2.0 // Copyright Verizon Media 2020 /** @file Spans of writable memory. This is similar but independently developed from @c std::span. The goal is to provide convenient handling for chunks of memory. These chunks can be treated as arrays of arbitrary types via template methods. */ #pragma once #include "swoc/swoc_version.h" #include "swoc/Scalar.h" #include <cstring> #include <memory> #include <type_traits> #include <ratio> #include <tuple> #include <exception> /// For template deduction guides #include <array> #include <vector> #include <string_view> namespace swoc { inline namespace SWOC_VERSION_NS { /** A span of contiguous piece of memory. A @c MemSpan does not own the memory to which it refers, it is simply a span of part of some (presumably) larger memory object. It acts as a pointer, not a container - copy and assignment change the span, not the memory to which the span refers. The purpose is that frequently code needs to work on a specific part of the memory. This can 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<T,N> maps to a @c MemSpan<T> - A @c const @c std::array<T,N> maps to a @c MemSpan<const T> For convenience a @c MemSpan<const T> can be constructed from a @c MemSpan<T> because this maintains @c const correctness and models how a @c T @c const* can be constructed from a @c T* . */ template <typename T> class MemSpan { using self_type = MemSpan; ///< Self reference type. protected: T *_ptr = nullptr; ///< Pointer to base of memory chunk. size_t _count = 0; ///< Number of elements. public: using value_type = T; ///< Element type for span. using iterator = T *; ///< Iterator. using const_iterator = T const *; ///< Constant iterator. /// Default constructor (empty buffer). constexpr MemSpan() = default; /// Copy constructor. constexpr MemSpan(self_type const &that) = default; /// Copy constructor. constexpr MemSpan(self_type &that) = default; /** Construct from a first element @a start and a @a count of elements. * * @param start First element. * @param count Total number of elements. */ constexpr MemSpan(value_type *ptr, size_t count); /** Construct from a half open range [start, last). * * @param begin Start of range. * @param end Past end of range. */ constexpr MemSpan(value_type *begin, value_type *end); /** Construct to cover an array. * * @tparam N Number of elements in the array. * @param a The array. */ template <auto N> constexpr MemSpan(T (&a)[N]); /** 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 <auto N, typename U, typename META = std::enable_if_t<std::conjunction_v<std::is_const<T>, std::is_same<std::remove_const_t<U>, std::remove_const_t<T>>>>> constexpr MemSpan(std::array<U, N> const &a); /** Construct from a @c std::array. * * @tparam N Array size. * @param a Array instance. */ template <auto N> constexpr MemSpan(std::array<T, N> &a); /** Construct a span of constant values from a span of non-constant. * * @tparam U Span types. * @tparam META Metaprogramming type to control conversion existence. * @param that Source span. * * This enables the standard conversion from non-const to const. */ template <typename U, typename META = std::enable_if_t<std::conjunction_v<std::is_const<T>, std::is_same<U, std::remove_const_t<T>>>>> constexpr MemSpan(MemSpan<U> const &that) : _ptr(that.data()), _count(that.count()) {} /** Construct from any vector like container. * * @tparam C Container type. * @param c container * * The container type must have the methods @c data and @c size which must return values convertible * to the pointer type for @a T and @c size_t respectively. * * @internal A non-const variant of this is needed because passing by CR means imposing constness * on the container which can then undesirably propagate that to the element type. Best example - * constructing from @c std::string. Without this variant it's not possible to construct a @c char * span vs. a @c char @c const. */ template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), T *> && std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, void>> constexpr MemSpan(C &c); /** Construct from any vector like container. * * @tparam C Container type. * @param c container * * The container type must have the methods @c data and @c size which must return values convertible * to the pointer type for @a T and @c size_t respectively. * * @note Because the container is passed as a constant reference, this may cause the span type to * also be a constant element type. */ template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().data()), T *> && std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, void>> constexpr MemSpan(C const &c); /** Construct from nullptr. This implicitly makes the length 0. */ constexpr MemSpan(std::nullptr_t); /** Equality. Compare the span contents. @return @c true if the contents of @a that are the same as the content of @a this, @c false otherwise. */ constexpr bool operator==(self_type const &that) const; /** Identical. Check if the spans refer to the same span of memory. @return @c true if @a this and @a that refer to the same span, @c false if not. */ bool is_same(self_type const &that) const; /** Inequality. @return @c true if @a that does not refer to the same span as @a this, @c false otherwise. */ constexpr bool operator!=(self_type const &that) const; /// Assignment - the span is copied, not the content. self_type &operator=(self_type const &that) = default; /// Access element at index @a idx. T &operator[](size_t idx) const; /// Check for empty span. /// @return @c true if the span is empty (no contents), @c false otherwise. bool operator!() const; /// Check for non-empty span. /// @return @c true if the span contains bytes. explicit operator bool() const; /// Check for empty span (no content). /// @see operator bool constexpr bool empty() const; /// @name Accessors. //@{ /// Pointer to the first element in the span. constexpr T *begin() const; /// Pointer to first element not in the span. constexpr T *end() const; /// Number of elements in the span constexpr size_t size() const; /// Number of elements in the span /// @note Deprecate for 1.5.0. constexpr size_t count() const; /// Number of elements in the span constexpr size_t length() const; /// Number of bytes in the span. constexpr size_t data_size() const; /// @return Pointer to memory in the span. T *data() const; /// @return Pointer to immediate after memory in the span. constexpr T *data_end() const; /** Access the first element in the span. * * @return A reference to the first element in the span. */ T &front(); /** Access the last element in the span. * * @return A reference to the last element in the span. */ T &back(); /** Apply a function @a f to every element of the span. * * @tparam F Functor type. * @param f Functor instance. * @return @a this */ template <typename F> self_type &apply(F &&f); /** Make a copy of @a this span on the same memory but of type @a U. * * @tparam U Type for the created span. * @return A @c MemSpan which contains the same memory as instances of @a U. * * if no type is specified, the default is @c void or @c void @c const according to whether * @a value_type is @c const. */ template <typename U = std::conditional_t<std::is_const_v<T>, void const, void>> MemSpan<U> rebind() const; /// Set the span. /// This is faster but equivalent to constructing a new span with the same /// arguments and assigning it. /// @return @c this. self_type &assign(T *ptr, ///< Buffer start. size_t count ///< # of elements. ); /** Adjust the span. * * @param first Starting point of the span. * @param last Past the end of the span. * @return @a this */ self_type &assign(T *first, T const *last); /// Clear the span (become an empty span). self_type &clear(); /// @return @c true if the byte at @a *p is in the span. bool contains(value_type const *p) const; /** Get the initial segment of @a count elements. @return An instance that contains the leading @a count elements of @a this. */ constexpr self_type prefix(size_t count) const; /** Get the initial segment of @a count elements. @return An instance that contains the leading @a count elements of @a this. @note Synonymn for @c prefix for STL compatibility. */ constexpr self_type first(size_t count) const; /** Shrink the span by removing @a count leading elements. * * @param count The number of elements to remove. * @return @c *this */ self_type &remove_prefix(size_t count); /** Remove and return a prefix. * * @param count Number of items in the prefix. * @return The removed prefix. * * @a count items are removed from the beginning of @a this and a view of those elements is returned. */ self_type clip_prefix(size_t count); /** Get the trailing segment of @a count elements. * * @param count Number of elements to retrieve. * @return An instance that contains the trailing @a count elements of @a this. */ constexpr self_type suffix(size_t count) const; /** Get the trailing segment of @a count elements. * * @param count Number of elements to retrieve. * @return An instance that contains the trailing @a count elements of @a this. * * @note Synonymn for @c suffix for STL compatibility. */ constexpr self_type last(size_t count) const; /** Shrink the span by removing @a count trailing elements. * * @param count Number of elements to remove. * @return @c *this */ self_type &remove_suffix(size_t count); /** Remove and return a suffix. * * @param count Number of items in the suffix. * @return The removed suffix. * * @a count items are removed from the end of @a this and a view of those elements is returned. */ self_type clip_suffix(size_t count); /** Return a sub span of @a this span. * * @param offset Offset (index) of first element in subspan. * @param count Number of elements in the subspan. * @return A subspan starting at @a offset for @a count elements. * * The result is clipped by @a this - if @a offset is out of range an empty span is returned. * Otherwise @c count is clipped by the number of elements available in @a this. */ constexpr self_type subspan(size_t offset, size_t count) const; /** Limit the size of the span. * * @param n Maximum number of elements. * @return @a this * * If the number of elements is greater than @a n, the size is changed to @a n. */ constexpr self_type &restrict(size_t n); /** Construct all elements in the span. * * For each element in the span, construct an instance of the span type using the @a args. If the * instances need destruction this must be done explicitly. */ template <typename... Args> self_type &make(Args &&...args); /// Destruct all elements in the span. void destroy(); template <typename U> friend class MemSpan; }; /** Specialization for void pointers. * * Key differences: * * - No subscript operator. * - No array initialization. * - Other non-const @c MemSpan types implicitly convert to this type. * * @internal I tried to be clever about the base template but there were too many differences * One major issue was the array initialization did not work at all if the @c void case didn't * exclude that. Once separate there are a number of useful tweaks available. */ template <> class MemSpan<void const> { using self_type = MemSpan; ///< Self reference type. template <typename U> friend class MemSpan; public: using value_type = void const; /// Export base type. protected: void *_ptr = nullptr; ///< Pointer to base of memory chunk. size_t _size = 0; ///< Number of bytes in the chunk. public: /// Default constructor (empty buffer). constexpr MemSpan() = default; /// Copy constructor. constexpr MemSpan(self_type const &that) = default; /// Copy assignment constexpr self_type &operator=(self_type const &that) = default; /** Construct to cover an array. * * @tparam N Number of elements in the array. * @param a The array. */ template <auto N, typename U> constexpr MemSpan(U (&a)[N]); /// Special constructor for @c void constexpr MemSpan(MemSpan<void> const &that); /** Cross type copy constructor. * * @tparam U Type for source span. * @param that Source span. *` * This enables any @c MemSpan to be automatically converted to a void span, just as any pointer * can convert to a void pointer. */ template <typename U> constexpr MemSpan(MemSpan<U> const &that); /** Construct from a pointer @a start and a size @a n bytes. * * @param start Start of the span. * @param n # of bytes in the span. */ constexpr MemSpan(value_type *ptr, size_t n); /** Construct from a half open range of [start, last). * * @param start Start of the range. * @param last Past end of range. */ MemSpan(value_type *begin, value_type *end); /** Construct from any vector like container. * * @tparam C Container type. * @param c container * * The container type must have the methods @c data and @c size which must return values convertible * to @c void* and @c size_t respectively. */ template <typename C, typename = std::enable_if_t<std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, void>> constexpr MemSpan(C const &c); /** Construct from nullptr. This implicitly makes the length 0. */ constexpr MemSpan(std::nullptr_t); /** Equality. Compare the span contents. @return @c true if the contents of @a that are bytewise the same as the content of @a this, @c false otherwise. */ bool operator==(self_type const &that) const; /** Equivalent. Check if the spans refer to the same span of memory. @return @c true if @a this and @a that refer to the same memory, @c false if not. */ bool is_same(self_type const &that) const; /** Inequality. @return @c true if @a that does not refer to the same span as @a this, @c false otherwise. */ bool operator!=(self_type const &that) const; /// Check for non-empty span. /// @return @c true if the span contains bytes. explicit operator bool() const; /// Check for empty span. /// @return @c true if the span is empty (no contents), @c false otherwise. bool operator!() const; /// Check for empty span (no content). /// @see operator bool constexpr bool empty() const; /// @return Number of bytes in the span. constexpr size_t size() const; /// @return Number of bytes in the span. /// @note Template compatibility. constexpr size_t count() const; /// @return Number of bytes in the span. /// @note Template compatibility. constexpr size_t length() const; /// Number of bytes in the span. constexpr size_t data_size() const; /// Pointer to memory in the span. constexpr value_type *data() const; /// Pointer to just after memory in the span. value_type *data_end() const; /// Assignment - special handling for @c void. constexpr self_type &operator=(MemSpan<void> const &that); /// Assignment - the span is copied, not the content. /// Any type of @c MemSpan can be assigned to @c MemSpan<void>. template <typename U> self_type &operator=(MemSpan<U> const &that); /** Update the span. * * @param ptr Start of span memory. * @param n Number of elements in the span. * @return @a this */ self_type &assign(value_type *ptr, size_t n); /** Update the span. * * @param first First element in the span. * @param last One past the last element in the span. * @return @a this */ self_type &assign(value_type *first, value_type const *last); /** Create a new span for a different type @a V on the same memory. * * @tparam V Type for the created span. * @return A @c MemSpan which contains the same memory as instances of @a V. */ template <typename U> MemSpan<U> rebind() const; /** Cast the span as a the instance of a type. * * @tparam U Target type. * @return A pointer to the span as a constant instance of @a U. * * @note This throws if the size is not a match for @a U. */ template <typename U> U const *as_ptr() const; /// Clear the span (become an empty span). self_type &clear(); /// @return @c true if the byte at @a *ptr is in the span. bool contains(void const *ptr) const; /** Get the initial segment of @a n bytes. @return An instance that contains the leading @a n bytes of @a this. */ self_type prefix(size_t n) const; /** Shrink the span by removing @a n leading bytes. * * @param count The number of elements to remove. * @return @c *this */ self_type &remove_prefix(size_t n); /** Remove and return a prefix. * * @param n Number of bytes in the prefix. * @return The removed prefix. * * @a n bytes are removed from the beginning of @a this and a view of those elements is returned. */ self_type clip_prefix(size_t n); /** Get the trailing segment of @a n bytes. * * @param n Number of bytes to retrieve. * @return An instance that contains the trailing @a count elements of @a this. */ self_type suffix(size_t n) const; /** Shrink the span by removing @a n bytes. * * @param n Number of bytes to remove. * @return @c *this */ self_type &remove_suffix(size_t n); /** Remove and return a suffix. * * @param n Number of items in the suffix. * @return The removed suffix. * * @a n bytes are removed from the end of @a this and a view of those bytes is returned. */ self_type clip_suffix(size_t n); /** Return a sub span of @a this span. * * @param offset Offset (index) of first element. * @param n Number of elements. * @return The span starting at @a offset for @a count elements in @a this. * * The result is clipped by @a this - if @a offset is out of range an empty span is returned. Otherwise @c count is clipped by the * number of elements available in @a this. In effect the intersection of the span described by ( @a offset , @a count ) and @a * this span is returned, which may be the empty span. */ constexpr self_type subspan(size_t offset, size_t n) const; /** Align span for a type. * * @tparam T Alignment type. * @return A suffix of the span suitably aligned for @a T. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). */ template <typename T> self_type align() const; /** Force memory alignment. * * @param alignment Alignment size (must be power of 2). * @return An aligned span. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). */ self_type align(size_t alignment) const; /** Force memory alignment. * * @param alignment Alignment size (must be power of 2). * @param obj_size Size of instances requiring alignment. * @return An aligned span. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). Trailing space * is discarded such that the resulting memory space is a multiple of @a size. * * @note @a obj_size should be a multiple of @a alignment. This happens naturally if @c sizeof is used. */ self_type align(size_t alignment, size_t obj_size) const; }; template <> class MemSpan<void> : public MemSpan<void const> { using self_type = MemSpan; using super_type = MemSpan<void const>; template <typename U> friend class MemSpan; public: using value_type = void; using pointer_type = value_type *; /// Default constructor (empty buffer). constexpr MemSpan() = default; /// Copy constructor. constexpr MemSpan(self_type const &that) = default; /// Copy assignment constexpr self_type &operator=(self_type const &that) = default; /** Cross type copy constructor. * * @tparam U Type for source span. * @param that Source span. *` * This enables any @c MemSpan to be automatically converted to a void span, just as any pointer * can convert to a void pointer. */ template <typename U> constexpr MemSpan(MemSpan<U> const &that); /** Construct from a pointer @a start and a size @a n bytes. * * @param start Start of the span. * @param n # of bytes in the span. */ constexpr MemSpan(value_type *start, size_t n); /** Construct from a half open range of [start, last). * * @param begin Start of the range. * @param end Past end of range. */ MemSpan(value_type *begin, value_type *end); /** Construct to cover an array. * * @tparam N Number of elements in the array. * @tparam U Element type. * @param a The array. */ template <auto N, typename U> constexpr MemSpan(U (&a)[N]); /** Construct from nullptr. This implicitly makes the length 0. */ constexpr MemSpan(std::nullptr_t); /// Pointer to memory in the span. constexpr value_type *data() const; /// Pointer to just after memory in the span. value_type *data_end() const; /// Assignment - the span is copied, not the content. /// Any type of @c MemSpan can be assigned to @c MemSpan<void>. template <typename U> self_type &operator=(MemSpan<U> const &that); /** Construct from any vector like container. * * @tparam C Container type. * @param c container * * The container type must have the methods @c data and @c size which must return values convertible * to @c void* and @c size_t respectively. */ template <typename C, typename = std::enable_if_t<!std::is_const_v<decltype(std::declval<C>().data()[0])> && std::is_convertible_v<decltype(std::declval<C>().size()), size_t>, void>> constexpr MemSpan(C const &c); /** Update the span. * * @param ptr Start of span memory. * @param n Number of elements in the span. * @return @a this */ self_type &assign(value_type *ptr, size_t n); /** Update the span. * * @param first First element in the span. * @param last One past the last element in the span. * @return @a this */ self_type &assign(value_type *first, value_type const *last); /// @return An instance that contains the leading @a n bytes of @a this. self_type prefix(size_t n) const; /** Shrink the span by removing @a n leading bytes. * * @param count The number of elements to remove. * @return @c *this */ self_type &remove_prefix(size_t count); /** Remove and return a prefix. * * @param count Number of byte in the prefix. * @return The removed prefix. * * @a n bytes are removed from the beginning of @a this and a view of those bytes is returned. */ self_type clip_prefix(size_t n); /** Get the trailing segment of @a n bytes. * * @param n Number of bytes to retrieve. * @return An instance that contains the trailing @a count elements of @a this. */ self_type suffix(size_t n) const; /** Shrink the span by removing @a n bytes. * * @param n Number of bytes to remove. * @return @c *this */ self_type &remove_suffix(size_t n); /** Remove and return a suffix. * * @param n Number of items in the suffix. * @return The removed suffix. * * @a n bytes are removed from the end of @a this and a view of those bytes is returned. */ self_type clip_suffix(size_t n); /** Return a sub span of @a this span. * * @param offset Offset (index) of first element. * @param n Number of elements. * @return The span starting at @a offset for @a count elements in @a this. * * The result is clipped by @a this - if @a offset is out of range an empty span is returned. Otherwise @c count is clipped by the * number of elements available in @a this. In effect the intersection of the span described by ( @a offset , @a count ) and @a * this span is returned, which may be the empty span. */ constexpr self_type subspan(size_t offset, size_t n) const; /** Align span for a type. * * @tparam T Alignment type. * @return A suffix of the span suitably aligned for @a T. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). */ template <typename T> self_type align() const; /** Force memory alignment. * * @param alignment Alignment size (must be power of 2). * @return An aligned span. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). */ self_type align(size_t alignment) const; /** Force memory alignment. * * @param alignment Alignment size (must be power of 2). * @param obj_size Size of instances requiring alignment. * @return An aligned span. * * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). Trailing space * is discarded such that the resulting memory space is a multiple of @a size. * * @note @a obj_size should be a multiple of @a alignment. This happens naturally if @c sizeof is used. */ self_type align(size_t alignment, size_t obj_size) const; /** Create a new span for a different type @a V on the same memory. * * @tparam V Type for the created span. * @return A @c MemSpan which contains the same memory as instances of @a V. */ template <typename U> MemSpan<U> rebind() const; /** Cast the span as a the instance of a type. * * @tparam U Target type. * @return A pointer to the span as a constant instance of @a U. * * @note This throws if the size is not a match for @a U. */ template <typename U> U *as_ptr() const; /// Clear the span (become an empty span). self_type &clear(); protected: /** Construct from @c const @c void span. * * @param super Source span. * * @note This is protected because it can only be used in situations were @c const correctness * is not violated by converting a @c const span. The primary use is to enable @c void span * method implementations to return a @c const span as @c self_type without explicit casting. * In such cases the original span was not @c const and so there is no violation. */ MemSpan(super_type const &super) : super_type(super) {} }; // -- Implementation -- namespace detail { /// @cond INTERNAL_DETAIL inline size_t ptr_distance(void const *first, void const *last) { return static_cast<const char *>(last) - static_cast<const char *>(first); } template <typename T> size_t ptr_distance(T const *first, T const *last) { return last - first; } inline void * ptr_add(void *ptr, size_t count) { return static_cast<char *>(ptr) + count; } inline void const * ptr_add(void const *ptr, size_t count) { return static_cast<char const *>(ptr) + count; } /// @endcond /** 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 <typename T, typename U> 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<sizeof(T), sizeof(U)>::num == 1 || std::ratio<sizeof(U), sizeof(T)>::num == 1) && (std::is_const_v<U> || !std::is_const_v<T>); // can't lose constancy. /** Compute the new size in units of @c sizeof(U). * * @param size Size in bytes. * @return Size in units of @c sizeof(U). * * The critical part of this is the @c static_assert that guarantees the result is an integral * number of instances of @a U. */ static size_t count(size_t size); }; template <typename T, typename U> size_t is_span_compatible<T, U>::count(size_t size) { if (size % sizeof(U)) { throw std::invalid_argument("MemSpan rebind where span size is not a multiple of the element size"); } return size / sizeof(U); } /// @cond INTERNAL_DETAIL // Must specialize for rebinding to @c void because @c sizeof doesn't work. Rebinding from @c void // is handled by the @c MemSpan<void>::rebind specialization and doesn't use this mechanism. template <typename T> struct is_span_compatible<T, void> { static constexpr bool value = !std::is_const_v<T>; static size_t count(size_t size); }; template <typename T> size_t is_span_compatible<T, void>::count(size_t size) { return sizeof(T) * size; } template <typename T> struct is_span_compatible<T, void const> { static constexpr bool value = true; static size_t count(size_t size); }; template <typename T> size_t is_span_compatible<T, void const>::count(size_t size) { return sizeof(T) * size; } /// @endcond } // namespace detail // --- Standard memory operations --- template class MemSpan<unsigned char>; template <typename T> int memcmp(MemSpan<T> const &lhs, MemSpan<T> const &rhs) { int zret = 0; size_t n = lhs.size(); // Seems a bit ugly but size comparisons must be done anyway to get the memcmp args. if (lhs.count() < rhs.count()) { zret = 1; } else if (lhs.count() > rhs.count()) { zret = -1; n = rhs.size(); } // else the counts are equal therefore @a n and @a zret are already correct. int r = std::memcmp(lhs.data(), rhs.data(), n); if (0 != r) { // If we got a not-equal, override the size based result. zret = r; } return zret; } using std::memcmp; template <typename T> T * memcpy(MemSpan<T> &dst, MemSpan<T> const &src) { return static_cast<T *>(std::memcpy(dst.data(), src.data(), std::min(dst.size(), src.size()))); } template <typename T> T * memcpy(MemSpan<T> &dst, T *src) { return static_cast<T *>(std::memcpy(dst.data(), src, dst.size())); } template <typename T> T * memcpy(T *dst, MemSpan<T> &src) { return static_cast<T *>(std::memcpy(dst, src.data(), src.size())); } inline char * memcpy(MemSpan<char> &span, std::string_view view) { return static_cast<char *>(std::memcpy(span.data(), view.data(), std::min(view.size(), view.size()))); } inline void * memcpy(MemSpan<void> &span, std::string_view view) { return std::memcpy(span.data(), view.data(), std::min(view.size(), view.size())); } using std::memcpy; /** Set contents of a span to a fixed @a value. * * @tparam T Span type. * @param dst Span to change. * @param value Source value. * @return */ template <typename T> inline MemSpan<T> const & memset(MemSpan<T> const &dst, T const &value) { for (auto &e : dst) { e = value; } return dst; } /// @cond INTERNAL_DETAIL /** Specialized @c memset. * * @tparam D Target span type. * @tparam S Source value type. * @param dst Target span. * @param c Source data. * @return @a dst * * If @a D and @a S are size 1 and instances of @a S can convert to @a D this directly calls @c memset. * This handles the various synonyms for a single byte, such as @c char, @c unsigned @c char, @c uint8_t, etc. */ template <typename D, typename S> auto memset(MemSpan<D> const &dst, S c) -> std::enable_if_t<sizeof(D) == 1 && sizeof(S) == 1 && std::is_convertible_v<S, D>, MemSpan<D>> { D d = c; std::memset(dst.data(), d, dst.size()); return dst; } // Optimization for @c char. inline MemSpan<void> const & memset(MemSpan<void> const &dst, char c) { std::memset(dst.data(), c, dst.size()); return dst; } /// @endcond using std::memset; // --- MemSpan<T> --- template <typename T> constexpr MemSpan<T>::MemSpan(T *ptr, size_t count) : _ptr{ptr}, _count{count} {} template <typename T> constexpr MemSpan<T>::MemSpan(T *begin, T *end) : _ptr{begin}, _count{detail::ptr_distance(begin, end)} {} template <typename T> template <auto N> constexpr MemSpan<T>::MemSpan(T (&a)[N]) : _ptr{a}, _count{N} { // Magic for string literals to drop the trailing nul terminator, which is almost always what is expected. if constexpr (N > 0 && std::is_same_v<char const, T>) { if (a[N - 1] == 0) { _count -= 1; } } } template <typename T> constexpr MemSpan<T>::MemSpan(std::nullptr_t) {} template <typename T> template <auto N, typename U, typename META> constexpr MemSpan<T>::MemSpan(std::array<U, N> const &a) : _ptr{a.data()}, _count{a.size()} {} template <typename T> template <auto N> constexpr MemSpan<T>::MemSpan(std::array<T, N> &a) : _ptr{a.data()}, _count{a.size()} {} template <typename T> template <typename C, typename> constexpr MemSpan<T>::MemSpan(C &c) : _ptr(c.data()), _count(c.size()) {} template <typename T> template <typename C, typename> constexpr MemSpan<T>::MemSpan(C const &c) : _ptr(c.data()), _count(c.size()) {} template <typename T> MemSpan<T> & MemSpan<T>::assign(T *ptr, size_t count) { _ptr = ptr; _count = count; return *this; } template <typename T> MemSpan<T> & MemSpan<T>::assign(T *first, T const *last) { _ptr = first; _count = detail::ptr_distance(first, last); return *this; } template <typename T> MemSpan<T> & MemSpan<T>::clear() { _ptr = nullptr; _count = 0; return *this; } template <typename T> bool MemSpan<T>::is_same(self_type const &that) const { return _ptr == that._ptr && _count == that._count; } template <typename T> constexpr bool MemSpan<T>::operator==(self_type const &that) const { return _count == that._count && (_ptr == that._ptr || 0 == memcmp(_ptr, that._ptr, this->size())); } template <typename T> constexpr bool MemSpan<T>::operator!=(self_type const &that) const { return !(*this == that); } template <typename T> bool MemSpan<T>::operator!() const { return _count == 0; } template <typename T> MemSpan<T>::operator bool() const { return _count != 0; } template <typename T> constexpr bool MemSpan<T>::empty() const { return _count == 0; } template <typename T> constexpr T * MemSpan<T>::begin() const { return _ptr; } template <typename T> T * MemSpan<T>::data() const { return _ptr; } template <typename T> constexpr T * MemSpan<T>::data_end() const { return _ptr + _count; } template <typename T> constexpr T * MemSpan<T>::end() const { return _ptr + _count; } template <typename T> T & MemSpan<T>::operator[](size_t idx) const { return _ptr[idx]; } template <typename T> constexpr size_t MemSpan<T>::size() const { return _count; } template <typename T> constexpr size_t MemSpan<T>::count() const { return _count; } template <typename T> constexpr size_t MemSpan<T>::length() const { return _count; } template <typename T> constexpr size_t MemSpan<T>::data_size() const { return _count * sizeof(T); } template <typename T> bool MemSpan<T>::contains(T const *ptr) const { return _ptr <= ptr && ptr < _ptr + _count; } template <typename T> constexpr auto MemSpan<T>::prefix(size_t count) const -> self_type { return {_ptr, std::min(count, _count)}; } template <typename T> constexpr auto MemSpan<T>::first(size_t count) const -> self_type { return this->prefix(count); } template <typename T> auto MemSpan<T>::remove_prefix(size_t count) -> self_type & { count = std::min(_count, count); _count -= count; _ptr += count; return *this; } template <typename T> constexpr auto MemSpan<T>::suffix(size_t count) const -> self_type { count = std::min(_count, count); return {(_ptr + _count) - count, count}; } template <typename T> constexpr MemSpan<T> MemSpan<T>::last(size_t count) const { return this->suffix(count); } template <typename T> MemSpan<T> & MemSpan<T>::remove_suffix(size_t count) { _count -= std::min(count, _count); return *this; } template <typename T> auto MemSpan<T>::clip_prefix(size_t count) -> self_type { if (count >= _count) { auto zret = *this; _count = 0; return zret; } self_type zret{_ptr, count}; _ptr += count; _count -= count; return zret; } template <typename T> auto MemSpan<T>::clip_suffix(size_t count) -> self_type { if (count >= _count) { auto zret = *this; _count = 0; return zret; } _count -= count; return {_ptr + _count, count}; } template <typename T> constexpr MemSpan<T> MemSpan<T>::subspan(size_t offset, size_t count) const { return offset < _count ? self_type{this->data() + offset, std::min(count, _count - offset)} : self_type{}; } template <typename T> T & MemSpan<T>::front() { return *_ptr; } template <typename T> T & MemSpan<T>::back() { return _ptr[_count - 1]; } template <typename T> template <typename F> typename MemSpan<T>::self_type & MemSpan<T>::apply(F &&f) { for (auto &item : *this) { f(item); } return *this; } template <typename T> template <typename U> MemSpan<U> MemSpan<T>::rebind() const { static_assert( detail::is_span_compatible<T, U>::value, "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<std::is_const_v<U>, const void *, void *>; return {static_cast<U *>(static_cast<VOID_PTR>(_ptr)), detail::is_span_compatible<T, U>::count(this->data_size())}; } template <typename T> template <typename... Args> auto MemSpan<T>::make(Args &&...args) -> self_type & { for (T *elt = this->data(), *limit = this->data_end(); elt < limit; ++elt) { new (elt) T(std::forward<Args>(args)...); } return *this; } template <typename T> void MemSpan<T>::destroy() { for (T *elt = this->data(), *limit = this->data_end(); elt < limit; ++elt) { std::destroy_at(elt); } } // --- void specializations --- template <typename U> constexpr MemSpan<void const>::MemSpan(MemSpan<U> const &that) : _ptr(const_cast<std::remove_const_t<U> *>(that._ptr)), _size(sizeof(U) * that.size()) {} template <typename U> constexpr MemSpan<void>::MemSpan(MemSpan<U> const &that) : super_type(that) { static_assert(!std::is_const_v<U>, "MemSpan<void> does not support constant memory."); } inline constexpr MemSpan<void const>::MemSpan(MemSpan<void> const &that) : _ptr(that._ptr), _size(that.size()) {} inline constexpr MemSpan<void const>::MemSpan(value_type *ptr, size_t n) : _ptr{const_cast<void *>(ptr)}, _size{n} {} inline constexpr MemSpan<void>::MemSpan(value_type *ptr, size_t n) : super_type(ptr, n) {} inline MemSpan<void const>::MemSpan(value_type *begin, value_type *end) : _ptr{const_cast<void *>(begin)}, _size{detail::ptr_distance(begin, end)} {} inline MemSpan<void>::MemSpan(value_type *begin, value_type *end) : super_type(begin, end) {} template <auto N, typename U> constexpr MemSpan<void const>::MemSpan(U (&a)[N]) : _ptr(const_cast<std::remove_const_t<U> *>(a)), _size(N * sizeof(U)) { // Magic for string literals to drop the trailing nul terminator, which is almost always what is expected. if constexpr (N > 0 && std::is_same_v<char const, U>) { if (a[N - 1] == 0) { _size -= 1; } } } template <auto N, typename U> constexpr MemSpan<void>::MemSpan(U (&a)[N]) : super_type(a) { static_assert(!std::is_const_v<U>, "Error: constructing non-constant view with constant data."); } template <typename C, typename> constexpr MemSpan<void const>::MemSpan(C const &c) : _ptr(const_cast<std::remove_const_t<std::remove_reference_t<decltype(*(std::declval<C>().data()))>> *>(c.data())), _size(c.size() * sizeof(*(std::declval<C>().data()))) {} template <typename C, typename> constexpr MemSpan<void>::MemSpan(C const &c) : super_type(c) {} inline constexpr MemSpan<void const>::MemSpan(std::nullptr_t) {} inline constexpr MemSpan<void>::MemSpan(std::nullptr_t) {} inline bool MemSpan<void const>::is_same(self_type const &that) const { return _ptr == that._ptr && _size == that._size; } inline bool MemSpan<void const>::operator==(self_type const &that) const { return _size == that._size && (_ptr == that._ptr || 0 == memcmp(_ptr, that._ptr, _size)); } inline bool MemSpan<void const>::operator!=(self_type const &that) const { return !(*this == that); } inline MemSpan<void const>::operator bool() const { return _size != 0; } inline bool MemSpan<void const>::operator!() const { return _size == 0; } inline constexpr bool MemSpan<void const>::empty() const { return _size == 0; } template <typename U> auto MemSpan<void const>::operator=(MemSpan<U> const &that) -> self_type & { _ptr = that._ptr; _size = sizeof(U) * that.size(); return *this; } inline constexpr auto MemSpan<void const>::operator=(MemSpan<void> const &that) -> self_type & { _ptr = that._ptr; _size = that.size(); return *this; } template <typename U> auto MemSpan<void>::operator=(MemSpan<U> const &that) -> self_type & { static_assert(!std::is_const_v<U>, "Cannot assign constant pointer to MemSpan<void>"); this->super_type::operator=(that); return *this; } inline auto MemSpan<void const>::assign(value_type *ptr, size_t n) -> self_type & { _ptr = const_cast<void *>(ptr); _size = n; return *this; } inline auto MemSpan<void>::assign(value_type *ptr, size_t n) -> self_type & { super_type::assign(ptr, n); return *this; } inline auto MemSpan<void const>::assign(value_type *first, value_type const *last) -> self_type & { _ptr = const_cast<void *>(first); _size = detail::ptr_distance(first, last); return *this; } inline auto MemSpan<void>::assign(value_type *first, value_type const *last) -> self_type & { super_type::assign(first, last); return *this; } inline auto MemSpan<void const>::clear() -> self_type & { _ptr = nullptr; _size = 0; return *this; } inline auto MemSpan<void>::clear() -> self_type & { super_type::clear(); return *this; } inline constexpr void const * MemSpan<void const>::data() const { return _ptr; } inline constexpr void * MemSpan<void>::data() const { return _ptr; } inline void const * MemSpan<void const>::data_end() const { return detail::ptr_add(_ptr, _size); } inline void * MemSpan<void>::data_end() const { return detail::ptr_add(_ptr, _size); } inline constexpr size_t MemSpan<void const>::size() const { return _size; } inline constexpr size_t MemSpan<void const>::count() const { return _size; } inline constexpr size_t MemSpan<void const>::length() const { return _size; } inline constexpr size_t MemSpan<void const>::data_size() const { return _size; } inline bool MemSpan<void const>::contains(value_type const *ptr) const { return _ptr <= ptr && ptr < this->data_end(); } inline auto MemSpan<void const>::prefix(size_t n) const -> self_type { return {_ptr, std::min(n, _size)}; } inline auto MemSpan<void>::prefix(size_t n) const -> self_type { return {_ptr, std::min(n, _size)}; } inline auto MemSpan<void const>::remove_prefix(size_t n) -> self_type & { n = std::min(_size, n); _size -= n; _ptr = detail::ptr_add(_ptr, n); return *this; } inline auto MemSpan<void>::remove_prefix(size_t n) -> self_type & { super_type::remove_prefix(n); return *this; } inline auto MemSpan<void const>::suffix(size_t n) const -> self_type { n = std::min(n, _size); return {detail::ptr_add(this->data_end(), -n), n}; } inline auto MemSpan<void>::suffix(size_t n) const -> self_type { n = std::min(n, _size); return {detail::ptr_add(this->data_end(), -n), n}; } inline auto MemSpan<void const>::remove_suffix(size_t n) -> self_type & { _size -= std::min(n, _size); return *this; } inline auto MemSpan<void>::remove_suffix(size_t n) -> self_type & { this->super_type::remove_suffix(n); return *this; } inline auto MemSpan<void const>::clip_prefix(size_t n) -> self_type { if (n >= _size) { auto zret = *this; _size = 0; return zret; } self_type zret{_ptr, n}; _ptr = detail::ptr_add(_ptr, n); _size -= n; return zret; } inline auto MemSpan<void>::clip_prefix(size_t n) -> self_type { return super_type::clip_prefix(n); } inline auto MemSpan<void const>::clip_suffix(size_t n) -> self_type { if (n >= _size) { auto zret = *this; _size = 0; return zret; } _size -= n; return {detail::ptr_add(_ptr, _size), n}; } inline auto MemSpan<void>::clip_suffix(size_t n) -> self_type { return super_type::clip_suffix(n); } inline constexpr auto MemSpan<void const>::subspan(size_t offset, size_t n) const -> self_type { return offset <= _size ? self_type{detail::ptr_add(this->data(), offset), std::min(n, _size - offset)} : self_type{}; } inline constexpr auto MemSpan<void>::subspan(size_t offset, size_t n) const -> self_type { return offset <= _size ? self_type{detail::ptr_add(this->data(), offset), std::min(n, _size - offset)} : self_type{}; } template <typename T> constexpr auto MemSpan<T>::restrict(size_t n) -> self_type & { _count = std::min(_count, n); return *this; } template <typename T> auto MemSpan<void const>::align() const -> self_type { return this->align(alignof(T), sizeof(T)); } template <typename T> auto MemSpan<void>::align() const -> self_type { return this->align(alignof(T), sizeof(T)); } inline auto MemSpan<void const>::align(size_t alignment) const -> self_type { auto p = uintptr_t(_ptr); auto padding = p & (alignment - 1); size_t size = 0; if (_size > padding) { // if there's not enough to pad, result is zero size. size = _size - padding; } return {reinterpret_cast<void *>(p + padding), size}; } inline auto MemSpan<void>::align(size_t alignment) const -> self_type { auto &&[ptr, size] = super_type::align(alignment); return {ptr, size}; } inline auto MemSpan<void const>::align(size_t alignment, size_t obj_size) const -> self_type { auto p = uintptr_t(_ptr); auto padding = p & (alignment - 1); size_t size = 0; if (_size > padding) { // if there's not enough to pad, result is zero size. size = ((_size - padding) / obj_size) * obj_size; } return {reinterpret_cast<void *>(p + padding), size}; } inline auto MemSpan<void>::align(size_t alignment, size_t obj_size) const -> self_type { auto &&[ptr, n] = super_type::align(alignment, obj_size); return {ptr, n}; } template <typename U> MemSpan<U> MemSpan<void const>::rebind() const { static_assert(std::is_const_v<U>, "Cannot rebind MemSpan<const void> to non-const type."); return {static_cast<U *>(_ptr), detail::is_span_compatible<value_type, U>::count(_size)}; } template <typename U> MemSpan<U> MemSpan<void>::rebind() const { return {static_cast<U *>(_ptr), detail::is_span_compatible<value_type, U>::count(_size)}; } // Specialize so that @c void -> @c void rebinding compiles and works as expected. template <> inline auto MemSpan<void const>::rebind() const -> self_type { return *this; } template <> inline auto MemSpan<void>::rebind() const -> self_type { return *this; } template <> inline auto MemSpan<void>::rebind() const -> MemSpan<void const> { return {_ptr, _size}; } template <typename U> U * MemSpan<void>::as_ptr() const { if (_size != sizeof(U)) { throw std::invalid_argument("MemSpan::as size is not compatible with target type."); } return static_cast<U *>(_ptr); } template <typename U> U const * MemSpan<void const>::as_ptr() const { if (_size != sizeof(U)) { throw std::invalid_argument("MemSpan::as size is not compatible with target type."); } return static_cast<U const *>(_ptr); } /// Deduction guides template <typename T, size_t N> MemSpan(std::array<T, N> &) -> MemSpan<T>; template <typename T, size_t N> MemSpan(std::array<T, N> const &) -> MemSpan<T const>; template <size_t N> MemSpan(char (&)[N]) -> MemSpan<char>; template <size_t N> MemSpan(char const (&)[N]) -> MemSpan<char const>; template <typename T> MemSpan(std::vector<T> &) -> MemSpan<T>; template <typename T> MemSpan(std::vector<T> const &) -> MemSpan<T const>; MemSpan(std::string_view const &) -> MemSpan<char const>; MemSpan(std::string &) -> MemSpan<char>; MemSpan(std::string const &) -> MemSpan<char const>; namespace detail { struct malloc_liberator { void operator()(void *ptr) { ::free(ptr); } }; } // namespace detail. /// A variant of @c unique_ptr that handles memory from @c malloc. template <typename T> using unique_malloc = std::unique_ptr<T, detail::malloc_liberator>; }} // namespace swoc::SWOC_VERSION_NS /// @cond NO_DOXYGEN // STL tuple support - this allows the @c MemSpan to be used as a tuple of a pointer // and size. namespace std { template <size_t IDX, typename R> class tuple_element<IDX, swoc::MemSpan<R>> { static_assert("swoc::MemSpan tuple index out of range"); }; template <typename R> class tuple_element<0, swoc::MemSpan<R>> { public: using type = R *; }; template <typename R> class tuple_element<1, swoc::MemSpan<R>> { public: using type = size_t; }; template <typename R> class tuple_size<swoc::MemSpan<R>> : public std::integral_constant<size_t, 2> {}; } // namespace std /// @endcond