/// @file dpf/parallel_bit_iterable.hpp /// @author Christopher Jiang /// @brief /// @details /// @copyright Copyright (c) 2019-2024 Ryan Henry and [others]{@ref authors} /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see [LICENSE.md](@ref license) for details. #ifndef LIBDPF_INCLUDE_DPF_PARALLEL_BIT_ITERABLE_HPP__ #define LIBDPF_INCLUDE_DPF_PARALLEL_BIT_ITERABLE_HPP__ #include "hedley/hedley.h" #include #include #include #include #include #include #include #include #include #include "dpf/bit_array.hpp" #include "dpf/parallel_bit_iterable_helpers.hpp" namespace dpf { template class parallel_const_bit_iterator; // forward declaration template class parallel_bit_iterable { public: using word_pointer = typename bit_array_base::const_word_pointer; static constexpr auto batch_size = BatchSize; using const_iterator = parallel_const_bit_iterator; template explicit parallel_bit_iterable(Iter it) : begin_{init_array(it, [](Iter it){ return it->data(); })}, end_{init_array(it, [](Iter it){ return it->data() + it->data_length(); })} { } template explicit parallel_bit_iterable(const T & t, const Ts & ...ts) : begin_{t.data(), ts.data()...}, end_{t.data()+t.data_length(), ts.data()+ts.data_length()...} { } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE const_iterator begin() const noexcept { return const_iterator{begin_}; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE const_iterator cbegin() const noexcept { return begin(); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE const_iterator end() const noexcept { return const_iterator{end_}; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE const_iterator cend() const noexcept { return end(); } private: using array_type = std::array; template HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr array_type init_array_impl(Iter it, std::index_sequence, const F & lambda) noexcept { return {{ ((void)Is, lambda(it++))... }}; } template > HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr array_type init_array(Iter it, const F & lambda) noexcept { return init_array_impl(it, Indices{}, lambda); } const array_type begin_, end_; }; // class dpf::parallel_bit_iterable template class parallel_const_bit_iterator { private: using word_type = typename bit_array_base::word_type; using word_array = std::array; using word_pointer = typename bit_array_base::const_word_pointer; using word_pointer_array = std::array; static constexpr std::size_t lg_batch_size = (N <= 2) ? 2 : std::ceil(std::log2(N)); using helper = dpf::parallel_bit_iterable_helper; using element_type = typename helper::element_type; using simde_type = typename helper::simde_type; static constexpr auto bits_per_word = bit_array_base::bits_per_word; static constexpr auto bits_per_element = helper::bits_per_element; static constexpr auto bytes_per_batch = N * (bits_per_element/CHAR_BIT); static_assert(CHAR_BIT == 8, "CHAR_BIT not equal to 8"); static constexpr auto elements_per_word = helper::elements_per_word; using simde_array = typename helper::simde_array; public: static constexpr auto batch_size = N; using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::array, batch_size>; using value_type = std::array; using reference = value_type &; using const_reference = const value_type &; using pointer = std::add_pointer_t; HEDLEY_ALWAYS_INLINE constexpr parallel_const_bit_iterator(parallel_const_bit_iterator &&) noexcept = default; HEDLEY_ALWAYS_INLINE constexpr parallel_const_bit_iterator(const parallel_const_bit_iterator &) noexcept = default; HEDLEY_PURE HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE value_type operator*() const noexcept { value_type ret; simde_type temp = helper::bit_and(all_vecs_[element_cnt_], vec_mask_); std::memcpy(ret.data(), &temp, bytes_per_batch); return ret; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE parallel_const_bit_iterator & operator++() noexcept { if (HEDLEY_UNLIKELY(!(word_mask_ <<= 1))) { word_mask_ = word_lsb; element_mask_ = element_lsb; vec_mask_ = helper::right_shift(vec_mask_, bits_per_element - 1); element_cnt_ = 0; std::transform(iter_.begin(), iter_.end(), cur_word_.begin(), [](auto & it) { return *(++it); }); all_vecs_ = helper::build_vecs(cur_word_.data(), batch_size); } else if (HEDLEY_UNLIKELY(!(element_mask_ <<= 1))) { element_mask_ = element_lsb; vec_mask_ = helper::right_shift(vec_mask_, bits_per_element - 1); ++element_cnt_; } else { vec_mask_ = helper::left_shift(vec_mask_, 1); } return *this; } HEDLEY_NO_THROW parallel_const_bit_iterator operator++(int) noexcept { auto tmp = *this; parallel_const_bit_iterator::operator++(); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE parallel_const_bit_iterator & operator--() noexcept { if (HEDLEY_UNLIKELY(!(word_mask_ >>= 1))) { word_mask_ = word_msb; element_mask_ = element_msb; vec_mask_ = helper::left_shift(vec_mask_, bits_per_element - 1); element_cnt_ = elements_per_word - 1; std::transform(iter_.begin(), iter_.end(), cur_word_.begin(), [](auto & it) { return *(--it); }); all_vecs_ = helper::build_vecs(cur_word_.data(), batch_size); } else if (HEDLEY_UNLIKELY(!(element_mask_ >>= 1))) { element_mask_ = element_msb; vec_mask_ = helper::left_shift(vec_mask_, bits_per_element - 1); --element_cnt_; } else { vec_mask_ = helper::right_shift(vec_mask_, 1); } return *this; } HEDLEY_NO_THROW parallel_const_bit_iterator operator--(int) noexcept { auto tmp = *this; parallel_const_bit_iterator::operator--(); return tmp; } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator==(const parallel_const_bit_iterator & rhs) const noexcept { return (word_mask_ == rhs.word_mask_) && (std::equal(iter_.begin(), iter_.end(), rhs.iter_.begin())); } HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr bool operator!=(const parallel_const_bit_iterator & rhs) const noexcept { return !(*this == rhs); } private: static constexpr word_type word_lsb = word_type(1); static constexpr word_type word_msb = word_lsb << (bits_per_word-1); static constexpr element_type element_lsb = element_type(1); static constexpr element_type element_msb = element_lsb << (bits_per_element-1); template word_array dereferencing_initializer_impl( const word_pointer_array & arr, std::index_sequence) { return {{ *(arr[Is])... }}; } word_array dereferencing_initializer(const word_pointer_array & arr) { return dereferencing_initializer_impl(arr, std::make_index_sequence()); } explicit constexpr parallel_const_bit_iterator( const word_pointer_array & arr) noexcept : iter_{arr}, word_mask_{word_lsb}, element_mask_{element_lsb}, element_cnt_{0}, cur_word_{dereferencing_initializer(arr)}, vec_mask_{helper::get_mask()}, all_vecs_{helper::build_vecs(cur_word_.data(), batch_size)} { } word_pointer_array iter_; word_type word_mask_; element_type element_mask_; std::size_t element_cnt_; word_array cur_word_; simde_type vec_mask_; simde_array all_vecs_; friend parallel_const_bit_iterator parallel_bit_iterable::begin() const noexcept; friend parallel_const_bit_iterator parallel_bit_iterable::end() const noexcept; }; // class dpf::parallel_const_bit_iterator template HEDLEY_PURE HEDLEY_ALWAYS_INLINE auto batch_of(Iter it) noexcept { return dpf::parallel_bit_iterable{it}; } template HEDLEY_PURE HEDLEY_ALWAYS_INLINE auto batch_of(const dpf::bit_array_base & t, const Ts & ...ts) noexcept { return dpf::parallel_bit_iterable<1+sizeof...(Ts), ChildT>{t, ts...}; } template HEDLEY_ALWAYS_INLINE void for_each_bit_parallel(Iter it, UnaryFunction f) { for (auto i : batch_of(it)) f(i); } template HEDLEY_ALWAYS_INLINE void for_each_bit_parallel(const dpf::bit_array_base & t, const Ts & ...ts, UnaryFunction f) { for (auto i : batch_of<1+sizeof...(Ts), ChildT>(t, ts...)) f(i); } } // namespace dpf namespace std { template struct iterator_traits> { private: using type = dpf::parallel_const_bit_iterator; public: using iterator_category = typename type::iterator_category; using difference_type = typename type::difference_type; using value_type = typename type::value_type; using reference = typename type::reference; using const_reference = typename type::const_reference; using pointer = typename type::pointer; }; } // namespace std #endif // LIBDPF_INCLUDE_DPF_PARALLEL_BIT_ITERABLE_HPP__