246 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_CONTAINER_STRIDE_SPAN_HPP_INCLUDED)
 | |
| #define MIJIN_CONTAINER_STRIDE_SPAN_HPP_INCLUDED 1
 | |
| 
 | |
| #include <bit>
 | |
| #include <cstddef>
 | |
| #include <iterator>
 | |
| #include <type_traits>
 | |
| #include "../debug/assert.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // public defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // public constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // public types
 | |
| //
 | |
| 
 | |
| template<typename T, typename U>
 | |
| [[nodiscard]] std::ptrdiff_t bytediff(T* first, U* second) noexcept
 | |
| {
 | |
|     return std::bit_cast<std::byte*>(second) - std::bit_cast<std::byte*>(first);
 | |
| }
 | |
| template<typename T>
 | |
| [[nodiscard]] T* byteoffset(T* old, std::ptrdiff_t offset) noexcept
 | |
| {
 | |
|     return std::bit_cast<T*>(std::bit_cast<std::byte*>(old) + offset);
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| class StrideSpanIterator
 | |
| {
 | |
| public:
 | |
|     using difference_type = std::ptrdiff_t;
 | |
|     using value_type = T;
 | |
| private:
 | |
|     T* ele_ = nullptr;
 | |
|     std::size_t strideBytes_ = 0;
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     T* start_ = nullptr;
 | |
|     T* end_ = nullptr;
 | |
| #endif
 | |
| public:
 | |
|     StrideSpanIterator() = default;
 | |
|     constexpr StrideSpanIterator(T* ele, std::size_t strideBytes, [[maybe_unused]] T* start, [[maybe_unused]] T* end)
 | |
|         : ele_(ele), strideBytes_(strideBytes)
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|         , start_(start), end_(end)
 | |
| #endif
 | |
|     {
 | |
|         MIJIN_ASSERT(strideBytes_!= 0 && bytediff(start_, end_) % strideBytes_ == 0, "StrideSpan: stride elements don't match supplied pointers.");
 | |
|     }
 | |
|     StrideSpanIterator(const StrideSpanIterator&) = default;
 | |
| 
 | |
|     StrideSpanIterator& operator=(const StrideSpanIterator&) = default;
 | |
|     StrideSpanIterator& operator+=(difference_type diff);
 | |
|     StrideSpanIterator& operator-=(difference_type diff) {
 | |
|         return *this += -diff;
 | |
|     }
 | |
| 
 | |
|     constexpr auto operator<=>(const StrideSpanIterator&) const noexcept = default;
 | |
| 
 | |
|     [[nodiscard]] T& operator*() const;
 | |
|     [[nodiscard]] T* operator->() const;
 | |
|     StrideSpanIterator& operator++();
 | |
|     StrideSpanIterator operator++(int);
 | |
|     StrideSpanIterator& operator--();
 | |
|     StrideSpanIterator operator--(int);
 | |
| 
 | |
|     [[nodiscard]] difference_type operator-(const StrideSpanIterator& other) const { return bytediff(other.ele_, ele_) / strideBytes_; }
 | |
|     [[nodiscard]] StrideSpanIterator operator+(difference_type diff) const;
 | |
|     [[nodiscard]] StrideSpanIterator operator-(difference_type diff) const { return (*this + -diff); }
 | |
| 
 | |
|     [[nodiscard]] T& operator[](difference_type diff) const { return *(*this + diff); }
 | |
| };
 | |
| template<typename T>
 | |
| inline StrideSpanIterator<T> operator+(std::iter_difference_t<T> diff, const StrideSpanIterator<T>& iter) {
 | |
|     return iter + diff;
 | |
| }
 | |
| static_assert(std::random_access_iterator<StrideSpanIterator<int>>);
 | |
| 
 | |
| template<typename T>
 | |
| class StrideSpan
 | |
| {
 | |
| public:
 | |
|     using element_type = T;
 | |
|     using value_type = std::remove_cv_t<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 = StrideSpanIterator<T>;
 | |
|     using const_iterator = StrideSpanIterator<const T>;
 | |
| private:
 | |
|     T* begin_ = nullptr;
 | |
|     T* end_ = nullptr;
 | |
|     std::size_t strideBytes_ = 0;
 | |
| public:
 | |
|     StrideSpan() = default;
 | |
|     StrideSpan(const StrideSpan&) = default;
 | |
|     StrideSpan(T* begin, T* end, std::size_t strideBytes) noexcept : begin_(begin), end_(end), strideBytes_(strideBytes) {
 | |
|         MIJIN_ASSERT(strideBytes_ > 0, "Stride elements must be >0.");
 | |
|         MIJIN_ASSERT(strideBytes_ % alignof(T) == 0, "Stride must be a multiple of element align.");
 | |
|         MIJIN_ASSERT(bytediff(begin_, end_) % strideBytes_ == 0, "Difference of begin and end must be a multiple of stride bytes.");
 | |
|     }
 | |
| 
 | |
|     StrideSpan& operator=(const StrideSpan&) = default;
 | |
|     auto operator<=>(const StrideSpan&) const noexcept = default;
 | |
| 
 | |
|     constexpr operator bool() const noexcept { return !empty(); }
 | |
|     constexpr bool operator!() const noexcept { return empty(); }
 | |
|     explicit operator std::span<T>() const noexcept;
 | |
| 
 | |
|     reference operator[](size_type index);
 | |
|     const_reference operator[](size_type index) const;
 | |
| 
 | |
|     [[nodiscard]] constexpr size_type size() const noexcept { return strideBytes_ == 0 ? 0 : bytediff(begin_, end_) / strideBytes_; }
 | |
|     [[nodiscard]] constexpr bool empty() const noexcept { return begin_ == end_; }
 | |
| 
 | |
|     [[nodiscard]] iterator begin() { return StrideSpanIterator<T>(begin_, strideBytes_, begin_, end_); }
 | |
|     [[nodiscard]] const_iterator begin() const { return StrideSpanIterator<const T>(begin_, strideBytes_, begin_, end_); }
 | |
|     [[nodiscard]] const_iterator cbegin() const { return StrideSpanIterator<const T>(begin_, strideBytes_, begin_, end_); }
 | |
|     [[nodiscard]] iterator end() { return StrideSpanIterator<T>(end_, strideBytes_, begin_, end_); }
 | |
|     [[nodiscard]] const_iterator end() const { return StrideSpanIterator<const T>(end_, strideBytes_, begin_, end_); }
 | |
|     [[nodiscard]] const_iterator cend() const { return StrideSpanIterator<const T>(end_, strideBytes_, begin_, end_); }
 | |
| };
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpan<T>::operator std::span<T>() const noexcept
 | |
| {
 | |
|     MIJIN_ASSERT(strideBytes_ == sizeof(T), "Can't convert to regular span if stride isn't exactly size of type.");
 | |
|     return {&*begin_, &*end_};
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpanIterator<T>& StrideSpanIterator<T>::operator+=(difference_type diff)
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT(byteoffset(ele_, diff * strideBytes_) <= end_ && byteoffset(ele_, diff * strideBytes_) >= start_,
 | |
|         "Moving iterator out-of-bounds.");
 | |
| #endif
 | |
|     ele_ = byteoffset(ele_, diff * strideBytes_);
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| T& StrideSpanIterator<T>::operator*() const
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT(ele_ >= start_ && ele_ < end_, "Attempting to dereference invalid iterator.");
 | |
| #endif
 | |
|     return *ele_;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| T* StrideSpanIterator<T>::operator->() const
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT(ele_ >= start_ && ele_ < end_, "Attempting to dereference invalid iterator.");
 | |
| #endif
 | |
|     return ele_;
 | |
| }
 | |
| template<typename T>
 | |
| StrideSpanIterator<T>& StrideSpanIterator<T>::operator++()
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT(ele_ < end_, "Attempting to move iterator past end.");
 | |
| #endif
 | |
|     ele_ = byteoffset(ele_, strideBytes_);
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpanIterator<T> StrideSpanIterator<T>::operator++(int)
 | |
| {
 | |
|     StrideSpanIterator copy(*this);
 | |
|     ++(*this);
 | |
|     return copy;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpanIterator<T>& StrideSpanIterator<T>::operator--()
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT(ele_ > start_, "Attempting to move iterator before start.");
 | |
| #endif
 | |
|     ele_ = byteoffset(ele_, -strideBytes_);
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpanIterator<T> StrideSpanIterator<T>::operator--(int)
 | |
| {
 | |
|     StrideSpanIterator copy(*this);
 | |
|     --(*this);
 | |
|     return copy;
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| StrideSpanIterator<T> StrideSpanIterator<T>::operator+(difference_type diff) const
 | |
| {
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|     MIJIN_ASSERT((byteoffset(ele_ + strideBytes_ * diff) <= end_) && (byteoffset(ele_, strideBytes_ * diff) >= start_),
 | |
|                  "Creating out-of-bounds iterator.");
 | |
| #endif
 | |
|     return StrideSpanIterator(byteoffset(ele_, diff * strideBytes_), strideBytes_,
 | |
| #if MIJIN_CHECKED_ITERATORS
 | |
|         start_, end_
 | |
| #else
 | |
|         nullptr, nullptr
 | |
| #endif
 | |
|     );
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| auto StrideSpan<T>::operator[](size_type index) -> reference
 | |
| {
 | |
|     MIJIN_ASSERT(index < size(), "Attempting to access StrideSpan out of bounds.");
 | |
|     return *byteoffset(begin_, index * strideBytes_);
 | |
| }
 | |
| 
 | |
| template<typename T>
 | |
| auto StrideSpan<T>::operator[](size_type index) const -> const_reference
 | |
| {
 | |
|     MIJIN_ASSERT(index < size(), "Attempting to access StrideSpan out of bounds.");
 | |
|     return *byteoffset(begin_, index * strideBytes_);
 | |
| }
 | |
| } // namespace mijin
 | |
| 
 | |
| #endif // !defined(MIJIN_CONTAINER_STRIDE_SPAN_HPP_INCLUDED)
 |