/// @file dpf/deferred_rotated_subinterval.hpp /// @brief Deferred view over a full-domain buffer until the input offset is set. /// @details After `defer_eval_interval` / `defer_eval_full` fills a full-domain /// buffer at identity, this view waits for `assign_wildcard_input`. /// Materialization builds `rotation_iterable` by `offset_x(0)`. The /// logical `[from, to]` is then indexed in domain-walk order (same as /// `eval_full`), including wrap-around when `from > to` in that order /// (e.g. unsigned `[200, 10]`). /// @see dpf::defer_eval_interval /// @see dpf::rotation_iterable /// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors) /// @license Released under a GNU General Public v2.0 (GPLv2) license. #ifndef LIBDPF_INCLUDE_DPF_DEFERRED_ROTATED_SUBINTERVAL_HPP__ #define LIBDPF_INCLUDE_DPF_DEFERRED_ROTATED_SUBINTERVAL_HPP__ #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/rotation_iterable.hpp" #include "dpf/utils.hpp" #include "dpf/wildcard.hpp" namespace dpf { /// @brief Index-based range over a (possibly wrapping) slice of a rotation. /// @details Indexing uses `rotation_iterable::operator[]`, so wrapping /// intervals stay correct without requiring a contiguous iterator /// walk past `end()`. template class deferred_rotated_range { public: class iterator { public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = std::decay_t()[0])>; using reference = decltype(std::declval()[0]); using pointer = void; HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator() noexcept : rot_{nullptr}, start_{0}, pos_{0}, count_{0}, n_{0} { } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator(const Rotation * rot, std::size_t start, std::size_t pos, std::size_t count, std::size_t n) noexcept : rot_{rot}, start_{start}, pos_{pos}, count_{count}, n_{n} { } HEDLEY_ALWAYS_INLINE reference operator*() const { const std::size_t idx = (start_ + pos_) % n_; return (*rot_)[static_cast(idx)]; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator & operator++() noexcept { ++pos_; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator operator++(int) noexcept { iterator tmp = *this; ++(*this); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator & operator--() noexcept { --pos_; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator operator--(int) noexcept { iterator tmp = *this; --(*this); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator==(const iterator & rhs) const noexcept { return pos_ == rhs.pos_ && rot_ == rhs.rot_ && start_ == rhs.start_ && count_ == rhs.count_ && n_ == rhs.n_; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator!=(const iterator & rhs) const noexcept { return !(*this == rhs); } private: const Rotation * rot_; std::size_t start_; std::size_t pos_; std::size_t count_; std::size_t n_; }; using const_iterator = iterator; HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr deferred_rotated_range(const Rotation * rot, std::size_t start, std::size_t count, std::size_t n) noexcept : rot_{rot}, start_{start}, count_{count}, n_{n} { } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator begin() const noexcept { return iterator(rot_, start_, 0, count_, n_); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr iterator end() const noexcept { return iterator(rot_, start_, count_, count_, n_); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr const_iterator cbegin() const noexcept { return begin(); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr const_iterator cend() const noexcept { return end(); } private: const Rotation * rot_; std::size_t start_; std::size_t count_; std::size_t n_; }; /// @brief Full-domain buffer view that rotates after the input offset is ready. /// @tparam DpfKey DPF key type (wildcard input) /// @tparam IteratorT random-access iterator into the full-domain output buffer template class deferred_rotated_subinterval { public: using dpf_type = DpfKey; using input_type = typename dpf_type::input_type; using iterator = IteratorT; using size_type = std::size_t; using difference_type = typename std::iterator_traits::difference_type; using rotation_type = rotation_iterable; using view_type = deferred_rotated_range; /// @param dpf key whose `offset_x` will supply the rotation once ready /// @param begin begin of the full-domain buffer (identity eval order) /// @param end end of the full-domain buffer /// @param from inclusive logical start /// @param to inclusive logical end /// @param outputs_per_leaf leaf packing width (usually `DpfKey::outputs_per_leaf`) HEDLEY_NO_THROW deferred_rotated_subinterval(const dpf_type & dpf, iterator begin, iterator end, input_type from, input_type to, size_type outputs_per_leaf) noexcept : dpf_{dpf}, begin_{begin}, end_{end}, from_{from}, to_{to}, outputs_{outputs_per_leaf}, rot_{std::nullopt} { (void)outputs_; } /// @brief Materialize the rotated subinterval; requires assigned input. /// @return a bidirectional range over the logical `[from, to]` /// @note The returned range borrows this object's cached rotation; keep /// `*this` alive for the range's lifetime. view_type get() { ensure_rotation(); return make_view(); } HEDLEY_ALWAYS_INLINE auto begin() { return get().begin(); } HEDLEY_ALWAYS_INLINE auto end() { return get().end(); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr const dpf_type & dpf() const noexcept { return dpf_; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr input_type from() const noexcept { return from_; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr input_type to() const noexcept { return to_; } private: static constexpr auto to_integral_t = utils::to_integral_type{}; static constexpr auto bits = utils::bitlength_of_v; void ensure_rotation() { assert_not_wildcard_input(dpf_); if (!rot_.has_value()) { const auto offset = dpf_.offset_x(input_type{}); rot_ = rotation_type(begin_, end_, static_cast(to_integral_t(offset))); } } view_type make_view() { // Domain walk of `eval_full`: index 0 is `min`, then `++` order. const size_type n = static_cast(std::distance(begin_, end_)); const auto min_i = to_integral_t(std::numeric_limits::min()); auto from_i = to_integral_t(from_); auto span = to_integral_t(to_) - from_i; if constexpr (bits < utils::bitlength_of_v) { span &= (decltype(span){1} << bits) - 1; } auto from_rel = from_i - min_i; if constexpr (bits < utils::bitlength_of_v) { from_rel &= (decltype(from_rel){1} << bits) - 1; } const auto start = static_cast(from_rel); const auto count = static_cast(span) + size_type{1}; return view_type(&rot_.value(), start, count, n); } const dpf_type & dpf_; iterator begin_; iterator end_; input_type from_; input_type to_; size_type outputs_; std::optional rot_; }; } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_DEFERRED_ROTATED_SUBINTERVAL_HPP__