304 lines
8.7 KiB
C++
304 lines
8.7 KiB
C++
|
|
/// @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__
|