/// @file grotto/offset_iterable.hpp /// @author Ryan Henry /// @brief defines `dpf::rotated_iterable` and associated helpers /// @details /// @copyright Copyright (c) 2019-2023 Ryan Henry and others /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see [LICENSE.md](@ref GPLv2) for details. #ifndef LIBDPF_INCLUDE_GROTTO_OFFSET_VIEW_HPP__ #define LIBDPF_INCLUDE_GROTTO_OFFSET_VIEW_HPP__ #include "hedley/hedley.h" #include #include #include #include #include #include "dpf/rotation_iterable.hpp" #include "dpf/utils.hpp" namespace grotto { template struct offset_iterator; template struct offset_const_iterator; template struct offset_iterable { using wrapped_iterator = WrappedIterator; using iterator = offset_iterator::iterator>; using const_iterator = offset_const_iterator::const_iterator>; using value_type = typename std::iterator_traits::value_type; using size_type = std::size_t; using difference_type = typename std::iterator_traits::difference_type; using reference = std::add_const_t::reference>; using const_reference = std::add_const_t::reference>; using pointer = typename std::iterator_traits::pointer; HEDLEY_ALWAYS_INLINE constexpr offset_iterable(wrapped_iterator begin, wrapped_iterator end, size_type offset) : rotated_iterable_{begin, end, std::distance(begin, std::upper_bound(begin, end, offset, std::less_equal{}))}, offset_{offset} { assert(std::is_sorted(begin, end)); } HEDLEY_ALWAYS_INLINE constexpr value_type operator[](size_type index) const { return rotated_iterable_[index] - offset_; } HEDLEY_ALWAYS_INLINE constexpr auto rotation() const { return rotated_iterable_; } HEDLEY_ALWAYS_INLINE constexpr auto offset() const { return offset_; } iterator begin() { return iterator{std::begin(rotated_iterable_), offset_}; } const_iterator begin() const { return const_iterator{std::begin(rotated_iterable_), offset_}; } constexpr const_iterator cbegin() const { return begin(); } iterator end() { return iterator{std::end(rotated_iterable_), offset_}; } const_iterator end() const { return const_iterator{std::end(rotated_iterable_), offset_}; } constexpr const_iterator cend() const { return end(); } private: dpf::rotation_iterable rotated_iterable_; size_type offset_; }; template struct offset_iterator_base { public: using wrapped_iterator = WrappedIterator; using value_type = typename std::iterator_traits::value_type; using reference = typename std::iterator_traits::reference; using const_reference = std::conditional_t< std::is_reference_v, std::add_lvalue_reference_t>>, std::add_const_t>; using size_type = std::size_t; constexpr offset_iterator_base(wrapped_iterator iter, size_type offset) noexcept : it{iter}, offset_{offset} { } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator==(const offset_iterator_base & rhs) const noexcept { return it == rhs.it && offset_ == rhs.offset_; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator!=(const offset_iterator_base & rhs) const noexcept { return !(*this == rhs); } protected: wrapped_iterator it; size_type offset_; }; template struct offset_iterator : public offset_iterator_base { private: using base = offset_iterator_base; public: using base::base; using typename base::value_type; HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_iterator & operator++() noexcept { ++this->it; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_iterator operator++(int) noexcept { auto tmp = *this; ++(*this); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_iterator & operator--() noexcept { --this->it; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_iterator operator--(int) noexcept { auto tmp = *this; --(*this); return tmp; } HEDLEY_ALWAYS_INLINE constexpr auto operator*() const { return static_cast(*this->it) - this->offset_; } }; template struct offset_const_iterator : public offset_iterator_base { private: using base = offset_iterator_base; public: using base::base; using typename base::value_type; HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_const_iterator & operator++() noexcept { ++this->it; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_const_iterator operator++(int) noexcept { auto tmp = *this; ++(*this); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_const_iterator & operator--() noexcept { --this->it; return *this; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr offset_const_iterator operator--(int) noexcept { auto tmp = *this; --(*this); return tmp; } HEDLEY_ALWAYS_INLINE constexpr auto operator*() const { return static_cast(*this->it) - this->offset_; } }; template auto offset_by(const ContainerT & container, typename ContainerT::size_type offset) { return offset_iterable{std::begin(container), std::end(container), offset}; } // assumes the range is sorted template ::value_type>, bool> = false> constexpr auto for_each_offset(IteratorT begin, IteratorT end, InputT offset, Function && f) { assert(std::is_sorted(begin, end)); InputT val = -offset; dpf::utils::flip_msb_if_signed_integral(val); auto size = std::distance(begin, end); // find the new "start" of the sorted sequence after offsetting by offset std::size_t new_first = std::distance(begin, std::upper_bound(begin, end, val, std::less_equal{})); // iterate in sorted order. `new_first` is the index of the // lexicographically smallest value in the `[begin,end)` after // shifting each element by `-offset` // in the input such that `*next(begin, new_first) auto it = std::next(begin, new_first); for (std::size_t i = new_first; i < size; ++i, ++it) f(i, *it - val); it = begin; for (std::size_t i = 0; i < new_first; ++i, ++it) f(i, *it - val); return new_first; } } // namespace grotto #endif // LIBDPF_INCLUDE_GROTTO_OFFSET_VIEW_HPP__