#ifndef AL_SPAN_H #define AL_SPAN_H #include #include #include #include #include #include #include #include "alassert.h" #include "almalloc.h" #include "altraits.h" namespace al { /* This is here primarily to help ensure proper behavior for span's iterators, * being an actual object with member functions instead of a raw pointer (which * has requirements like + and - working with ptrdiff_t). This also helps * silence clang-tidy's pointer arithmetic warnings for span and FlexArray * iterators. It otherwise behaves like a plain pointer and should optimize * accordingly. * * Shouldn't be needed once we use std::span in C++20. */ template class ptr_wrapper { static_assert(std::is_pointer_v); T mPointer{}; public: using value_type = std::remove_pointer_t; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; using iterator_category = std::random_access_iterator_tag; explicit constexpr ptr_wrapper(T ptr) : mPointer{ptr} { } /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ constexpr auto operator++() noexcept -> ptr_wrapper& { ++mPointer; return *this; } constexpr auto operator--() noexcept -> ptr_wrapper& { --mPointer; return *this; } constexpr auto operator++(int) noexcept -> ptr_wrapper { auto temp = *this; ++*this; return temp; } constexpr auto operator--(int) noexcept -> ptr_wrapper { auto temp = *this; --*this; return temp; } constexpr auto operator+=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer += n; return *this; } constexpr auto operator-=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer -= n; return *this; } [[nodiscard]] constexpr auto operator*() const noexcept -> value_type& { return *mPointer; } [[nodiscard]] constexpr auto operator->() const noexcept -> value_type* { return mPointer; } [[nodiscard]] constexpr auto operator[](std::size_t idx) const noexcept -> value_type& {return mPointer[idx];} [[nodiscard]] friend constexpr auto operator+(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper { return ptr_wrapper{lhs.mPointer + n}; } [[nodiscard]] friend constexpr auto operator+(std::ptrdiff_t n, const ptr_wrapper &rhs) noexcept -> ptr_wrapper { return ptr_wrapper{n + rhs.mPointer}; } [[nodiscard]] friend constexpr auto operator-(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper { return ptr_wrapper{lhs.mPointer - n}; } [[nodiscard]] friend constexpr auto operator-(const ptr_wrapper &lhs, const ptr_wrapper &rhs)noexcept->std::ptrdiff_t { return lhs.mPointer - rhs.mPointer; } [[nodiscard]] friend constexpr auto operator==(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer == rhs.mPointer; } [[nodiscard]] friend constexpr auto operator!=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer != rhs.mPointer; } [[nodiscard]] friend constexpr auto operator<=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer <= rhs.mPointer; } [[nodiscard]] friend constexpr auto operator>=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer >= rhs.mPointer; } [[nodiscard]] friend constexpr auto operator<(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer < rhs.mPointer; } [[nodiscard]] friend constexpr auto operator>(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool { return lhs.mPointer > rhs.mPointer; } /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ }; inline constexpr std::size_t dynamic_extent{static_cast(-1)}; template class span; namespace detail_ { template struct is_span_ : std::false_type { }; template struct is_span_> : std::true_type { }; template inline constexpr bool is_span_v = is_span_>::value; template struct is_std_array_ : std::false_type { }; template struct is_std_array_> : std::true_type { }; template inline constexpr bool is_std_array_v = is_std_array_>::value; template inline constexpr bool has_size_and_data = false; template inline constexpr bool has_size_and_data())),decltype(std::data(std::declval()))>> = true; template inline constexpr bool is_valid_container_type = !is_span_v && !is_std_array_v && !std::is_array::value && has_size_and_data; template inline constexpr bool is_array_compatible = std::is_convertible::value; /* NOLINT(*-avoid-c-arrays) */ template inline constexpr bool is_valid_container = is_valid_container_type && is_array_compatible()))>,T>; } // namespace detail_ #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true template class span { public: using element_type = T; using value_type = std::remove_cv_t; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using iterator = ptr_wrapper; using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; static constexpr std::size_t extent{E}; template constexpr span() noexcept { } template constexpr explicit span(U iter, size_type size_) : mData{::al::to_address(iter)} { alassert(size_ == extent); } template::value)> constexpr explicit span(U first, V last) : mData{::al::to_address(first)} { alassert(static_cast(last-first) == extent); } template constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ : mData{std::data(arr)} { static_assert(N == extent); } template constexpr span(std::array &arr) noexcept : mData{std::data(arr)} { static_assert(N == extent); } template::value)> constexpr span(const std::array &arr) noexcept : mData{std::data(arr)} { static_assert(N == extent); } template)> constexpr explicit span(U&& cont) : span{std::data(cont), std::size(cont)} { } template::value && detail_::is_array_compatible && N == dynamic_extent)> constexpr explicit span(const span &span_) noexcept : mData{std::data(span_)} { alassert(std::size(span_) == extent); } template::value && detail_::is_array_compatible && N == extent)> constexpr span(const span &span_) noexcept : mData{std::data(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } [[nodiscard]] constexpr auto back() const -> reference { return mData[E-1]; } [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference { return mData[idx]; } [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } [[nodiscard]] constexpr auto size() const noexcept -> size_type { return E; } [[nodiscard]] constexpr auto size_bytes() const noexcept -> size_type { return E * sizeof(value_type); } [[nodiscard]] constexpr auto empty() const noexcept -> bool { return E == 0; } [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } [[nodiscard]] constexpr auto end() const noexcept -> iterator { return iterator{mData+E}; } [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator { return const_iterator{mData+E}; } [[nodiscard]] constexpr auto rbegin() const noexcept -> reverse_iterator { return end(); } [[nodiscard]] constexpr auto rend() const noexcept -> reverse_iterator { return begin(); } [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } template [[nodiscard]] constexpr auto first() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData, C}; } template [[nodiscard]] constexpr auto last() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData+(E-C), C}; } template [[nodiscard]] constexpr auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); static_assert(E-O >= C, "New size exceeds original capacity"); return span{mData+O, C}; } template [[nodiscard]] constexpr auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); return span{mData+O, E-O}; } /* NOTE: Can't declare objects of a specialized template class prior to * defining the specialization. As a result, these methods need to be * defined later. */ [[nodiscard]] constexpr auto first(std::size_t count) const noexcept -> span; [[nodiscard]] constexpr auto last(std::size_t count) const noexcept -> span; [[nodiscard]] constexpr auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept -> span; private: pointer mData{nullptr}; }; template class span { public: using element_type = T; using value_type = std::remove_cv_t; using size_type = std::size_t; using difference_type = ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using iterator = ptr_wrapper; using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; static constexpr std::size_t extent{dynamic_extent}; constexpr span() noexcept = default; template constexpr span(U iter, size_type count) : mData{::al::to_address(iter)}, mDataLength{count} { } template::value)> constexpr span(U first, V last) : span{::al::to_address(first), static_cast(last-first)} { } template constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ : mData{std::data(arr)}, mDataLength{std::size(arr)} { } template constexpr span(std::array &arr) noexcept : mData{std::data(arr)}, mDataLength{std::size(arr)} { } template::value)> constexpr span(const std::array &arr) noexcept : mData{std::data(arr)}, mDataLength{std::size(arr)} { } template)> constexpr span(U&& cont) : span{std::data(cont), std::size(cont)} { } template && (!std::is_same::value || extent != N))> constexpr span(const span &span_) noexcept : span{std::data(span_), std::size(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } [[nodiscard]] constexpr auto back() const -> reference { return mData[mDataLength-1]; } [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference {return mData[idx];} [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } [[nodiscard]] constexpr auto size() const noexcept -> size_type { return mDataLength; } [[nodiscard]] constexpr auto size_bytes() const noexcept -> size_type { return mDataLength * sizeof(value_type); } [[nodiscard]] constexpr auto empty() const noexcept -> bool { return mDataLength == 0; } [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } [[nodiscard]] constexpr auto end() const noexcept -> iterator { return iterator{mData+mDataLength}; } [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator { return const_iterator{mData+mDataLength}; } [[nodiscard]] constexpr auto rbegin() const noexcept -> reverse_iterator { return end(); } [[nodiscard]] constexpr auto rend() const noexcept -> reverse_iterator { return begin(); } [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } template [[nodiscard]] constexpr auto first() const noexcept -> span { assert(C <= mDataLength); return span{mData, C}; } [[nodiscard]] constexpr auto first(std::size_t count) const noexcept -> span { assert(count <= mDataLength); return span{mData, count}; } template [[nodiscard]] constexpr auto last() const noexcept -> span { assert(C <= mDataLength); return span{mData+mDataLength-C, C}; } [[nodiscard]] constexpr auto last(std::size_t count) const noexcept -> span { assert(count <= mDataLength); return span{mData+mDataLength-count, count}; } template [[nodiscard]] constexpr auto subspan() const noexcept -> std::enable_if_t> { assert(O <= mDataLength); assert(C <= mDataLength-O); return span{mData+O, C}; } template [[nodiscard]] constexpr auto subspan() const noexcept -> std::enable_if_t> { assert(O <= mDataLength); return span{mData+O, mDataLength-O}; } [[nodiscard]] constexpr auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept -> span { assert(offset <= mDataLength); if(count != dynamic_extent) { assert(count <= mDataLength-offset); return span{mData+offset, count}; } return span{mData+offset, mDataLength-offset}; } private: pointer mData{nullptr}; size_type mDataLength{0}; }; template [[nodiscard]] constexpr auto span::first(std::size_t count) const noexcept -> span { assert(count <= size()); return span{mData, count}; } template [[nodiscard]] constexpr auto span::last(std::size_t count) const noexcept -> span { assert(count <= size()); return span{mData+size()-count, count}; } template [[nodiscard]] constexpr auto span::subspan(std::size_t offset, std::size_t count) const noexcept -> span { assert(offset <= size()); if(count != dynamic_extent) { assert(count <= size()-offset); return span{mData+offset, count}; } return span{mData+offset, size()-offset}; } template span(T, EndOrSize) -> span())>>; template span(T (&)[N]) -> span; /* NOLINT(*-avoid-c-arrays) */ template span(std::array&) -> span; template span(const std::array&) -> span; template)> span(C&&) -> span()))>>; #undef REQUIRES } // namespace al #endif /* AL_SPAN_H */