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)
|