libdpf/include/dpf/deferred_rotated_subinterval.hpp

304 lines
8.7 KiB
C++
Raw Normal View History

/// @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 <cstddef>
#include <iterator>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
#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 <typename Rotation>
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<decltype(std::declval<const Rotation &>()[0])>;
using reference = decltype(std::declval<const Rotation &>()[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<typename Rotation::difference_type>(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 <typename DpfKey,
typename IteratorT>
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<iterator>::difference_type;
using rotation_type = rotation_iterable<iterator>;
using view_type = deferred_rotated_range<rotation_type>;
/// @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<input_type>{};
static constexpr auto bits = utils::bitlength_of_v<input_type>;
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<difference_type>(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<size_type>(std::distance(begin_, end_));
const auto min_i =
to_integral_t(std::numeric_limits<input_type>::min());
auto from_i = to_integral_t(from_);
auto span = to_integral_t(to_) - from_i;
if constexpr (bits < utils::bitlength_of_v<decltype(span)>)
{
span &= (decltype(span){1} << bits) - 1;
}
auto from_rel = from_i - min_i;
if constexpr (bits < utils::bitlength_of_v<decltype(from_rel)>)
{
from_rel &= (decltype(from_rel){1} << bits) - 1;
}
const auto start = static_cast<size_type>(from_rel);
const auto count = static_cast<size_type>(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<rotation_type> rot_;
};
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_DEFERRED_ROTATED_SUBINTERVAL_HPP__