diff --git a/source/mijin/container/stride_span.hpp b/source/mijin/container/stride_span.hpp new file mode 100644 index 0000000..47c26d7 --- /dev/null +++ b/source/mijin/container/stride_span.hpp @@ -0,0 +1,232 @@ + +#pragma once + +#if !defined(MIJIN_CONTAINER_STRIDE_SPAN_HPP_INCLUDED) +#define MIJIN_CONTAINER_STRIDE_SPAN_HPP_INCLUDED 1 + +#include +#include +#include +#include "../debug/assert.hpp" + +namespace mijin +{ + +// +// public defines +// + +// +// public constants +// + +// +// public types +// + +template +[[nodiscard]] std::ptrdiff_t bytediff(T* first, U* second) noexcept +{ + return std::bit_cast(second) - std::bit_cast(first); +} +template +[[nodiscard]] T* byteoffset(T* old, std::ptrdiff_t offset) noexcept +{ + return std::bit_cast(std::bit_cast(old) + offset); +} + +template +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 +inline StrideSpanIterator operator+(std::iter_difference_t diff, const StrideSpanIterator& iter) { + return iter + diff; +} +static_assert(std::random_access_iterator>); + +template +class StrideSpan +{ +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 = StrideSpanIterator; + using const_iterator = StrideSpanIterator; +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; + + reference operator[](size_type index); + const_reference operator[](size_type index) const; + + [[nodiscard]] constexpr size_type size() const noexcept { return bytediff(begin_, end_) / strideBytes_; } + + [[nodiscard]] iterator begin() { return StrideSpanIterator(begin_, strideBytes_, begin_, end_); } + [[nodiscard]] const_iterator begin() const { return StrideSpanIterator(begin_, strideBytes_, begin_, end_); } + [[nodiscard]] const_iterator cbegin() const { return StrideSpanIterator(begin_, strideBytes_, begin_, end_); } + [[nodiscard]] iterator end() { return StrideSpanIterator(end_, strideBytes_, begin_, end_); } + [[nodiscard]] const_iterator end() const { return StrideSpanIterator(end_, strideBytes_, begin_, end_); } + [[nodiscard]] const_iterator cend() const { return StrideSpanIterator(end_, strideBytes_, begin_, end_); } +}; + +// +// public functions +// + +template +StrideSpanIterator& StrideSpanIterator::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 +T& StrideSpanIterator::operator*() const +{ +#if MIJIN_CHECKED_ITERATORS + MIJIN_ASSERT(ele_ >= start_ && ele_ < end_, "Attempting to dereference invalid iterator."); +#endif + return *ele_; +} + +template +T* StrideSpanIterator::operator->() const +{ +#if MIJIN_CHECKED_ITERATORS + MIJIN_ASSERT(ele_ >= start_ && ele_ < end_, "Attempting to dereference invalid iterator."); +#endif + return ele_; +} +template +StrideSpanIterator& StrideSpanIterator::operator++() +{ +#if MIJIN_CHECKED_ITERATORS + MIJIN_ASSERT(ele_ < end_, "Attempting to move iterator past end."); +#endif + ele_ = byteoffset(ele_, strideBytes_); + return *this; +} + +template +StrideSpanIterator StrideSpanIterator::operator++(int) +{ + StrideSpanIterator copy(*this); + ++(*this); + return copy; +} + +template +StrideSpanIterator& StrideSpanIterator::operator--() +{ +#if MIJIN_CHECKED_ITERATORS + MIJIN_ASSERT(ele_ > start_, "Attempting to move iterator before start."); +#endif + ele_ = byteoffset(ele_, -strideBytes_); + return *this; +} + +template +StrideSpanIterator StrideSpanIterator::operator--(int) +{ + StrideSpanIterator copy(*this); + --(*this); + return copy; +} + +template +StrideSpanIterator StrideSpanIterator::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 +auto StrideSpan::operator[](size_type index) -> reference +{ + MIJIN_ASSERT(index < size(), "Attempting to access StrideSpan out of bounds."); + return *byteoffset(begin_, index * strideBytes_); +} + +template +auto StrideSpan::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) diff --git a/source/mijin/container/typeless_buffer.hpp b/source/mijin/container/typeless_buffer.hpp index a9c8a20..4b63387 100644 --- a/source/mijin/container/typeless_buffer.hpp +++ b/source/mijin/container/typeless_buffer.hpp @@ -5,6 +5,7 @@ #define MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED 1 #include +#include #include #include "../debug/assert.hpp" @@ -41,6 +42,12 @@ public: template [[nodiscard]] BufferView makeBufferView() { return BufferView(this); } + + template + [[nodiscard]] std::span makeSpan(); + + template + [[nodiscard]] std::span makeSpan() const; }; template @@ -99,6 +106,25 @@ public: // public functions // +template +std::span TypelessBuffer::makeSpan() +{ + MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type."); + return { + std::bit_cast(bytes_.data()), + std::bit_cast(bytes_.data() + bytes_.size()) + }; +} + +template +std::span TypelessBuffer::makeSpan() const +{ + MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type."); + return { + std::bit_cast(bytes_.data()), + std::bit_cast(bytes_.data() + bytes_.size()) + }; +} } // namespace mijin #endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)