Last active
October 17, 2022 21:33
-
-
Save schaumb/f01404476dd863167ac3597b7769e5ce to your computer and use it in GitHub Desktop.
optimalized bitset like true value iterator helper, like std::bitset and std::vector<bool>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef BITSET_ITERATOR_HELPER_HPP | |
#define BITSET_ITERATOR_HELPER_HPP | |
#include <utility> | |
#include <limits> | |
#include <functional> | |
#include <cstdint> | |
template<class T> | |
struct bitset_like_iterator_helper { | |
template<class U = T, class V = decltype(std::declval<const U&>()._Getword(size_t{}))> | |
static constexpr std::integral_constant<V (U::*) (size_t) const, &U::_Getword> get_word_impl(std::nullptr_t); | |
template<class U, class V> | |
constexpr static auto get_underlying_data_begin = [] (const U& c, size_t v) -> V { return c.begin()._M_p[v]; }; | |
template<class U = T, class V = decltype(std::declval<const U&>().begin()._M_p[size_t{}])> | |
static constexpr auto get_word_impl(std::nullptr_t) { | |
return std::integral_constant<V (*)(const U&, size_t), +(get_underlying_data_begin<U, V>)>{}; | |
} | |
template<class U, class V> | |
constexpr static auto get_underlying_data_attr = [] (const U& c, size_t v) -> V { return c._Myvec[v]; }; | |
template<class U = T, class V = decltype(std::declval<const U&>()._Myvec[size_t{}])> | |
static constexpr auto get_word_impl(std::nullptr_t, int = 0) { | |
return std::integral_constant<V (*)(const U&, size_t), +(get_underlying_data_attr<U, V>)>{}; | |
} | |
template<class U = T> static constexpr std::nullptr_t get_word_impl(...); | |
template<class U = T, class V = decltype(std::declval<const U&>()._Find_first())> | |
static constexpr std::integral_constant<V (U::*) () const, &U::_Find_first> get_first_impl(std::nullptr_t); | |
template<class U = T> static constexpr std::nullptr_t get_first_impl(...); | |
template<class U = T, class V = decltype(std::declval<const U&>()._Find_next(size_t{}))> | |
static constexpr std::integral_constant<V (U::*) (size_t) const, &U::_Find_next> get_next_impl(std::nullptr_t); | |
template<class U = T> static constexpr std::nullptr_t get_next_impl(...); | |
template<class U> | |
constexpr static auto builtin_fun_wrapper_ctzll = [] (U v) -> int { return v ? __builtin_ctzll(v) : std::numeric_limits<U>::digits; }; | |
template<class U = std::uint64_t, class = decltype(__builtin_ctzll(std::declval<U>()))> | |
static constexpr auto get_builtin_countr_zero(std::nullptr_t) { | |
return std::integral_constant<int (*)(U), +(builtin_fun_wrapper_ctzll<U>)>{}; | |
}; | |
template<class U> | |
constexpr static auto builtin_fun_wrapper_scan = [] (U v) -> int { | |
unsigned long res; | |
return _BitScanForward64(&res, v) ? static_cast<int>(res) : std::numeric_limits<U>::digits; | |
}; | |
template<class U = std::uint64_t, class = decltype(_BitScanForward64(std::declval<unsigned long*>(), std::declval<U>()))> | |
static constexpr auto get_builtin_countr_zero(std::nullptr_t, int = 0) { | |
return std::integral_constant<int (*)(U), +(builtin_fun_wrapper_scan<U>)>{}; | |
}; | |
template<class U = std::uint64_t> | |
constexpr static auto fun_wrapper_dummy = [] (U x) -> int { | |
constexpr unsigned char const mod67[ 67 ] = { 64, 4, 54, 24, 19, 41, 59, 16, 12, 3, 23, 40, 15, 2, 39, 1, 0, | |
0, 33, 34, 6, 35, 48, 7, 56, 36, 45, 49, 26, 8, 52, 57, 21, | |
37, 31, 46, 43, 50, 29, 27, 61, 9, 63, 53, 18, 58, 11, 22, 14, | |
38, 0, 32, 5, 47, 55, 44, 25, 51, 20, 30, 42, 28, 60, 62, 17, | |
10, 13 }; | |
x |= x << 1; | |
x |= x << 2; | |
x |= x << 4; | |
x |= x << 8; | |
x |= x << 16; | |
x |= x << 32; | |
return static_cast<int>(mod67[ x % 67 ]); | |
}; | |
template<class U = std::uint64_t> static constexpr auto get_builtin_countr_zero(...) { | |
return std::integral_constant<int (*)(U), +(fun_wrapper_dummy<U>)>{}; | |
}; | |
constexpr static auto get_word_v = decltype(bitset_like_iterator_helper<T>::template get_word_impl(nullptr)){}; | |
constexpr static auto get_first_v = decltype(bitset_like_iterator_helper<T>::template get_first_impl(nullptr)){}; | |
constexpr static auto get_next_v = decltype(bitset_like_iterator_helper<T>::template get_next_impl(nullptr)){}; | |
constexpr static auto countr_zero = decltype(bitset_like_iterator_helper<T>::template get_builtin_countr_zero(nullptr)){}; | |
constexpr static std::size_t get_first(const T& obj) { | |
if constexpr(get_first_v != nullptr) { | |
return (obj.*get_first_v())(); | |
} else if constexpr (get_word_v != nullptr) { | |
using word_t = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(get_word_v()), T, size_t>>>; | |
constexpr auto word_digits = std::numeric_limits<word_t>::digits; | |
const auto size_v = obj.size(); | |
const auto words = size_v / word_digits + (size_v % word_digits > 0); | |
for (std::size_t ix{}; ix < words; ++ix) | |
if (auto r = std::invoke(get_word_v(), obj, ix)) | |
return ix * word_digits + countr_zero(r); | |
return size_v; | |
} else { | |
const auto size_v = obj.size(); | |
for (std::size_t ix{}; ix < size_v; ++ix) | |
if (obj[ix]) | |
return ix; | |
return size_v; | |
} | |
} | |
constexpr static std::size_t get_next(const T& obj, std::size_t prev) { | |
if constexpr(get_next_v != nullptr) { | |
return (obj.*get_next_v())(prev); | |
} else if constexpr(get_word_v != nullptr) { | |
using word_t = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(get_word_v()), T, size_t>>>; | |
constexpr auto word_digits = std::numeric_limits<word_t>::digits; | |
const auto size_v = obj.size(); | |
const auto words = size_v / word_digits + (size_v % word_digits > 0); | |
if (++prev >= size_v) | |
return size_v; | |
size_t ix = prev / word_digits; | |
if (auto this_word = std::invoke(get_word_v(), obj, ix); this_word &= ~word_t{} << (prev % word_digits)) | |
return ix * word_digits + countr_zero(this_word); | |
for (++ix; ix < words; ++ix) | |
if (auto this_word = std::invoke(get_word_v(), obj, ix)) | |
return ix * word_digits + countr_zero(this_word); | |
return size_v; | |
} else { | |
const auto size_v = obj.size(); | |
for (++prev; prev < size_v; ++prev) | |
if (obj[prev]) | |
return prev; | |
return size_v; | |
} | |
} | |
constexpr static std::size_t get_end(const T& obj) { | |
return obj.size(); | |
} | |
}; | |
#endif // BITSET_ITERATOR_HELPER_HPP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment