Skip to content

Instantly share code, notes, and snippets.

@schaumb
Last active October 17, 2022 21:33
Show Gist options
  • Save schaumb/f01404476dd863167ac3597b7769e5ce to your computer and use it in GitHub Desktop.
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>
#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