From 40db8c690a2d67a2ac4eac0e81db5dc526d9a745 Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Fri, 11 Jul 2025 09:34:29 -0400 Subject: [PATCH] Remove unused functions from bit_details. Replace some with stl --- include/bitlib/bit-algorithms/count.hpp | 70 ++- include/bitlib/bit-algorithms/find.hpp | 30 +- include/bitlib/bit-iterator/bit_details.hpp | 501 ++---------------- include/bitlib/bit-iterator/bit_reference.hpp | 5 +- 4 files changed, 86 insertions(+), 520 deletions(-) diff --git a/include/bitlib/bit-algorithms/count.hpp b/include/bitlib/bit-algorithms/count.hpp index 321edec8..3d69a011 100644 --- a/include/bitlib/bit-algorithms/count.hpp +++ b/include/bitlib/bit-algorithms/count.hpp @@ -57,7 +57,7 @@ count( if (first.position() != 0) { word_type first_value = lsr(*first.base(), first.position()); - result = _popcnt(first_value); + result = std::popcount(static_cast>(first_value)); ++it; } // The SIMD implementation here is actually slower than the standard @@ -67,46 +67,44 @@ count( //{ //// Align to boundary //for (; it != last.base() && !is_aligned(&(*it), 64); ++it) { - //result += _popcnt(*it); - //} - - //// SIMD - //hn::ScalableTag d; - //for (; std::distance(it, last.base()) >= hn::Lanes(d); it += hn::Lanes(d)) - //{ - //const auto popcntV = hn::PopulationCount(hn::Load(d, &*it)); - //result += hn::ReduceSum(d, popcntV); - //} - - //// Remaining - //for (; it != last.base(); ++it) { - //result += _popcnt(*it); - //} - //} else -//#endif - { - // std:: version - //result += std::transform_reduce( - //it, - //last.base(), - //0, - //std::plus{}, - //[](word_type word) {return _popcnt(word); } - //); - - // libpopcnt - result += popcnt(&*it, (digits / 8) * std::distance(it, last.base())); - } + //result += std::popcount(*it); + //} + + //// SIMD + //hn::ScalableTag d; + //for (; std::distance(it, last.base()) >= hn::Lanes(d); it += hn::Lanes(d)) + //{ + //const auto popcntV = hn::PopulationCount(hn::Load(d, &*it)); + //result += hn::ReduceSum(d, popcntV); + //} + + //// Remaining + //for (; it != last.base(); ++it) { + //result += std::popcount(*it); + //} + //} else + //#endif + { + // std:: version + //result += std::transform_reduce( + //it, + //last.base(), + //0, + //std::plus{}, + //[](word_type word) {return std::popcount(word); } + //); + + // libpopcnt + result += popcnt(&*it, (digits / 8) * std::distance(it, last.base())); + } if (last.position() != 0) { word_type last_value = *last.base() << (digits - last.position()); - result += _popcnt(last_value); + result += std::popcount(static_cast>(last_value)); } // Computation when bits belong to the same underlying word } else { - result = _popcnt( - _bextr(*first.base(), first.position(), last.position() - - first.position()) - ); + result = std::popcount(static_cast>( + _bextr(*first.base(), first.position(), last.position() - first.position()))); } // Negates when the number of zero bits is requested diff --git a/include/bitlib/bit-algorithms/find.hpp b/include/bitlib/bit-algorithms/find.hpp index 0227d3d0..95b2c2be 100644 --- a/include/bitlib/bit-algorithms/find.hpp +++ b/include/bitlib/bit-algorithms/find.hpp @@ -9,6 +9,7 @@ // ============================== PREAMBLE ================================== // // C++ standard library +#include #include // Project sources #include "bitlib/bit-iterator/bit.hpp" @@ -37,6 +38,7 @@ constexpr bit_iterator find( ) { using word_type = typename bit_iterator::word_type; + using uword_type = std::make_unsigned_t; using size_type = typename bit_iterator::size_type; const std::size_t digits = binary_digits::value; @@ -48,8 +50,8 @@ constexpr bit_iterator find( if (!is_first_aligned) { word_type shifted_first = lsr(*first.base(), first.position()); size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~shifted_first)) - : _tzcnt(static_cast(shifted_first)); + ? std::countr_zero(static_cast(~shifted_first)) + : std::countr_zero(static_cast(shifted_first)); if (std::next(first.base(), is_last_aligned) == last.base()) { return first + std::min(num_trailing_complementary_bits, static_cast(distance(first, last))); } else if (num_trailing_complementary_bits + first.position() < digits) { @@ -72,8 +74,8 @@ constexpr bit_iterator find( } size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); return bit_iterator(it, (size_type) num_trailing_complementary_bits); } @@ -92,8 +94,8 @@ constexpr bit_iterator find( { it += hn::FindKnownFirstTrue(d, found); size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); return bit_iterator(it, (size_type) num_trailing_complementary_bits); } } @@ -106,18 +108,18 @@ constexpr bit_iterator find( } if (it != last.base()) { - size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); - return bit_iterator(it, static_cast(num_trailing_complementary_bits)); + size_type num_trailing_complementary_bits = (bv == bit0) + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); + return bit_iterator(it, static_cast(num_trailing_complementary_bits)); } // Deal with any unaligned boundaries if (!is_last_aligned) { - size_type num_trailing_complementary_bits = (bv == bit0) - ? _tzcnt(static_cast(~*it)) - : _tzcnt(static_cast(*it)); - return bit_iterator(it, static_cast(std::min(num_trailing_complementary_bits, last.position()))); + size_type num_trailing_complementary_bits = (bv == bit0) + ? std::countr_zero(static_cast(~*it)) + : std::countr_zero(static_cast(*it)); + return bit_iterator(it, static_cast(std::min(num_trailing_complementary_bits, last.position()))); } return last; } diff --git a/include/bitlib/bit-iterator/bit_details.hpp b/include/bitlib/bit-iterator/bit_details.hpp index cac7b81f..150b1aaf 100644 --- a/include/bitlib/bit-iterator/bit_details.hpp +++ b/include/bitlib/bit-iterator/bit_details.hpp @@ -15,9 +15,15 @@ // ================================ PREAMBLE ================================ // // C++ standard library + +#if __has_include() #include +#else +#define NO_X86_INTRINSICS +#endif #include +#include #include #include #include @@ -121,170 +127,6 @@ struct _cv_iterator_traits }; /* ************************************************************************** */ -#if 0 -/* *********** IMPLEMENTATION DETAILS: NARROWEST AND WIDEST TYPES *********** */ -// Narrowest type structure declaration -template -struct _narrowest_type; - -// Narrowest type structure specialization: selects the only passed type -template -struct _narrowest_type -: std::common_type -{ - static_assert(binary_digits::value, ""); -}; - -// Narrowest type structure specialization: selects the type with less bits -template -struct _narrowest_type -: _narrowest_type< - typename std::conditional< - (binary_digits::value < binary_digits::value), - T, - typename std::conditional< - (binary_digits::value > binary_digits::value), - U, - typename std::common_type::type - >::type - >::type -> -{ -}; - -// Narrowest type structure specialization: recursively selects the right type -template -struct _narrowest_type -: _narrowest_type::type> -{ -}; - -// Narrowest type alias -template -using _narrowest_type_t = typename _narrowest_type::type; - -// Widest type structure declaration -template -struct _widest_type; - -// Widest type structure specialization: selects the only passed type -template -struct _widest_type -: std::common_type -{ - static_assert(binary_digits::value, ""); -}; - -// Widest type structure specialization: selects the type with more bits -template -struct _widest_type -: _widest_type< - typename std::conditional< - (binary_digits::value > binary_digits::value), - T, - typename std::conditional< - (binary_digits::value < binary_digits::value), - U, - typename std::common_type::type - >::type - >::type -> -{ -}; - -// Widest type structure specialization: recursively selects the right type -template -struct _widest_type -: _widest_type::type> -{ -}; - -// Widest type alias -template -using _widest_type_t = typename _widest_type::type; -/* ************************************************************************** */ - - - -/* ************ IMPLEMENTATION DETAILS: NARROWER AND WIDER TYPES ************ */ -// Narrower type structure definition -template -struct _narrower_type -{ - using tuple = std::tuple< - unsigned long long int, - unsigned long int, - unsigned int, - unsigned short int, - unsigned char - >; - using lhs_bits = binary_digits; - using rhs_bits = binary_digits::type>; - using type = typename std::conditional< - (lhs_bits::value > rhs_bits::value), - typename std::tuple_element::type, - typename std::conditional< - (I + 1 < std::tuple_size::value), - typename _narrower_type< - T, - (I + 1 < std::tuple_size::value ? I + 1 : -1) - >::type, - typename std::tuple_element::type - >::type - >::type; -}; - -// Narrower type structure specialization: not found -template -struct _narrower_type -{ - using type = T; -}; - -// Narrower type alias -template -using _narrower_type_t = typename _narrower_type::type; - -// Wider type structure definition -template -struct _wider_type -{ - using tuple = std::tuple< - unsigned char, - unsigned short int, - unsigned int, - unsigned long int, - unsigned long long int - >; - using lhs_bits = binary_digits; - using rhs_bits = binary_digits::type>; - using type = typename std::conditional< - (lhs_bits::value < rhs_bits::value), - typename std::tuple_element::type, - typename std::conditional< - (I + 1 < std::tuple_size::value), - typename _narrower_type< - T, - (I + 1 < std::tuple_size::value ? I + 1 : -1) - >::type, - typename std::tuple_element::type - >::type - >::type; -}; - -// Wider type structure specialization: not found -template -struct _wider_type -{ - using type = T; -}; - -// Wider type alias -template -using _wider_type_t = typename _wider_type::type; -/* ************************************************************************** */ -#endif - /* exact_floor_integral is used to determine the exact integral type that a proxy reference can be implicitly converted to. @@ -338,54 +180,12 @@ template constexpr bool _assert_range_viability(Iterator first, Iterator last); /* ************************************************************************** */ - - -/* ****************** IMPLEMENTATION DETAILS: INSTRUCTIONS ****************** */ -// Population count -template -constexpr T _popcnt(T src) noexcept; -template -constexpr T _popcnt(T src, X...) noexcept; - -// Leading zeros count -template -constexpr T _lzcnt(T src) noexcept; -template -constexpr T _lzcnt(T src, X...) noexcept; - -// Trailing zeros count -template -constexpr T _tzcnt(T src) noexcept; -template -constexpr T _tzcnt(T src, X...) noexcept; - // Bit field extraction template constexpr T _bextr(T src, T start, T len) noexcept; template constexpr T _bextr(T src, T start, T len, X...) noexcept; -#if 0 -// Parallel bits deposit -template -constexpr T _pdep(T src, T msk) noexcept; -template -constexpr T _pdep(T src, T msk, X...) noexcept; - -// Parallel bits extract -template -constexpr T _pext(T src, T msk) noexcept; -template -constexpr T _pext(T src, T msk, X...) noexcept; - -// Byte swap -template -constexpr T _byteswap(T src) noexcept; -template -constexpr T _byteswap(T src, X...) noexcept; - -#endif - // Bit swap template constexpr T _bitswap(T src) noexcept; @@ -402,10 +202,6 @@ constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept; template constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept; -// Bit compare -template -constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept; - // Double precision shift left template constexpr T _shld(T dst, T src, T cnt) noexcept; @@ -414,26 +210,6 @@ constexpr T _shld(T dst, T src, T cnt) noexcept; template constexpr T _shrd(T dst, T src, T cnt) noexcept; -// Add carry -template -using _supports_adc = decltype(__builtin_ia32_addcarryx_u64(T()...)); -template > -constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept; -template -constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept; - -// Sub borrow -template -using _supports_sbb = decltype(__builtin_ia32_sbb_u64(T()...)); -template -using _supports_sbb_alt = decltype(__builtin_ia32_subborrow_u64(T()...)); -template > -constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept; -template > -constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept; -template -constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept; - // Multiword multiply template constexpr T _mulx(T src0, T src1, T* hi) noexcept; @@ -449,6 +225,9 @@ constexpr T lsr(const T val, const size_type shift) { return static_cast(static_cast>(val) >> shift); } +/* +Logic shift right when `val` operand is a proxy reference +*/ template constexpr exact_floor_integral_t lsr(const T val, const size_type shift) { static_assert(!std::is_same_v, void>, @@ -489,112 +268,6 @@ constexpr bool _assert_range_viability(Iterator first, Iterator last) { } // -------------------------------------------------------------------------- // -// --------- IMPLEMENTATION DETAILS: INSTRUCTIONS: POPULATION COUNT --------- // -// Counts the number of bits set to 1 with compiler intrinsics -template -constexpr T _popcnt(T src) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - if (digits <= std::numeric_limits::digits) { - src = __builtin_popcount(static_cast>(src)); - } else if (digits <= std::numeric_limits::digits) { - src = __builtin_popcountl(static_cast>(src)); - } else if (digits <= std::numeric_limits::digits) { - src = __builtin_popcountll(static_cast>(src)); - } else { - src = _popcnt(src, std::ignore); - } - return src; -} - -// Counts the number of bits set to 1 without compiler intrinsics -template -constexpr T _popcnt(T src, X...) noexcept { - static_assert(binary_digits::value, ""); - T dst = T(); - for (dst = T(); src; src = lsr(src, 1)) { - dst += src & 1; - } - return dst; -} -// -------------------------------------------------------------------------- // - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: LEADING ZEROS COUNT -------- // -// Counts the number of leading zeros with compiler intrinsics -template -constexpr T _lzcnt(T src) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clz(src) - (std::numeric_limits::digits - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clz(src) : digits; - } else if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clzl(src) - (std::numeric_limits::digits - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clzl(src) : digits; - } else if (digits < std::numeric_limits::digits) { - dst = src ? __builtin_clzll(src) - (std::numeric_limits::digits - digits) - : digits; - } else if (digits == std::numeric_limits::digits) { - dst = src ? __builtin_clzll(src) : digits; - } else { - dst = _lzcnt(src, std::ignore); - } - return dst; -} - -// Counts the number of leading zeros without compiler intrinsics -template -constexpr T _lzcnt(T src, X...) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = src != T(); - while ((src = lsr(src, 1))) { - ++dst; - } - return digits - dst; -} -// -------------------------------------------------------------------------- // - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: TRAILING ZEROS COUNT ------- // -// Counts the number of trailing zeros with compiler intrinsics -template -constexpr T _tzcnt(T src) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctz(src) : digits; - } else if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctzl(src) : digits; - } else if (digits <= std::numeric_limits::digits) { - dst = src ? __builtin_ctzll(src) : digits; - } else { - dst = _tzcnt(src, std::ignore); - } - return dst; -} - -// Counts the number of trailing zeros without compiler intrinsics -template -constexpr T _tzcnt(T src, X...) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = digits; - if (src) { - src = lsr((src ^ (src - 1)), 1); - for (dst = T(); src; dst++) { - src = lsr(src, 1); - } - } - return dst; -} -// -------------------------------------------------------------------------- // - // ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- // // Extacts to lsbs a field of contiguous bits with compiler intrinsics template @@ -623,128 +296,6 @@ constexpr T _bextr(T src, T start, T len, X...) noexcept { } // -------------------------------------------------------------------------- // -#if 0 -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT DEPOSIT ------- // -// Deposits bits according to a mask with compiler instrinsics -template -constexpr T _pdep(T src, T msk) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = _pdep_u32(src, msk); - } else if (digits <= std::numeric_limits::digits) { - dst = _pdep_u64(src, msk); - } else { - dst = _pdep(src, msk, std::ignore); - } - return dst; -} - -// Deposits bits according to a mask without compiler instrinsics -template -constexpr T _pdep(T src, T msk, X...) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - T cnt = T(); - while (msk) { - dst = lsr(dst, 1); - if (msk & 1) { - dst |= src << (digits - 1); - src = lsr(src, 1); - } - msk = lsr(msk, 1); - ++cnt; - } - dst = lsr(dst, (digits - cnt) * (cnt > 0)); - return dst; -} -// -------------------------------------------------------------------------- // - -// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT EXTRACT ------- // -// Extracts bits according to a mask with compiler instrinsics -template -constexpr T _pext(T src, T msk) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - if (digits <= std::numeric_limits::digits) { - dst = _pext_u32(src, msk); - } else if (digits <= std::numeric_limits::digits) { - dst = _pext_u64(src, msk); - } else { - dst = _pext(src, msk, std::ignore); - } - return dst; -} - -// Extracts bits according to a mask without compiler instrinsics -template -constexpr T _pext(T src, T msk, X...) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - T dst = T(); - T cnt = T(); - while (msk) { - if (msk & 1) { - dst = lsr(dst, 1); - dst |= src << (digits - 1); - ++cnt; - } - src = lsr(src, 1); - msk = lsr(msk, 1); - } - dst = lsr(dst, (digits - cnt) * (cnt > 0)); - return dst; -} -// -------------------------------------------------------------------------- // - -// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BYTE SWAP ------------- // -// Reverses the order of the underlying bytes with compiler intrinsics -template -constexpr T _byteswap(T src) noexcept { - static_assert(binary_digits::value, ""); - using byte_t = unsigned char; - constexpr T digits = sizeof(T) * std::numeric_limits::digits; - std::uint64_t tmp64 = 0; - std::uint64_t* ptr64 = nullptr; - if (std::is_same::value) { - ptr64 = reinterpret_cast(&src); - tmp64 = __builtin_bswap64(*ptr64); - *ptr64 = __builtin_bswap64(*(ptr64 + 1)); - *(ptr64 + 1) = tmp64; - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap16(src); - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap32(src); - } else if (digits == std::numeric_limits::digits) { - src = __builtin_bswap64(src); - } else if (digits > std::numeric_limits::digits) { - src = _byteswap(src, std::ignore); - } - return src; -} - -// Reverses the order of the underlying bytes without compiler intrinsics -template -constexpr T _byteswap(T src, X...) noexcept { - static_assert(binary_digits::value, ""); - using byte_t = unsigned char; - constexpr T half = sizeof(T) / 2; - constexpr T end = sizeof(T) - 1; - unsigned char* bytes = reinterpret_cast(&src); - unsigned char byte = 0; - for (T i = T(); i < half; ++i) { - byte = bytes[i]; - bytes[i] = bytes[end - i]; - bytes[end - i] = byte; - } - return src; -} -// -------------------------------------------------------------------------- // -#endif - // ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- // // Reverses the order of the bits with or without of compiler intrinsics template @@ -760,7 +311,7 @@ constexpr T _bitswap(T src) noexcept { constexpr bool is_size1 = sizeof(T) == 1; constexpr bool is_byte = digits == std::numeric_limits::digits; constexpr bool is_octet = std::numeric_limits::digits == 8; - constexpr bool is_pow2 = _popcnt(digits, ignore) == 1; + constexpr bool is_pow2 = std::has_single_bit(static_cast>(digits)); T dst = src; T i = digits - 1; if (is_size1 && is_byte && is_octet) { @@ -908,15 +459,6 @@ constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept // clang-format on // -------------------------------------------------------------------------- // -// ----------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT COMPARE ------------ // -// Compares a subsequence of bits within src0 and src1 and returns 0 if equal -template -constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept { - static_assert(binary_digits::value, ""); - return _bextr(src0, start0, len) == _bextr(src1, start1, len); -} -// -------------------------------------------------------------------------- // - // --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- // // Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src template @@ -947,6 +489,28 @@ constexpr T _shrd(T dst, T src, T cnt) noexcept { } // -------------------------------------------------------------------------- // +#if defined(NO_X86_INTRINSICS) +template +static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept { + static_assert(std::is_unsigned_v, "Only unsigned types are supported"); + + if constexpr (Add) { + U sum1 = a + b; + U sum2 = sum1 + static_cast(c_in); + *out = sum2; + + // Carry occurs if either sum1 overflows a+b or sum2 overflows sum1+carry + return static_cast((sum1 < a) || (sum2 < sum1)); + } else { + U diff1 = a - b; + U diff2 = diff1 - static_cast(c_in); + *out = diff2; + + // Borrow occurs if a < b or a - b < carry_in + return static_cast((a < b) || (diff1 < c_in)); + } +} +#else #if defined(__ADX__) template unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) { @@ -1000,6 +564,7 @@ static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U assert(((void)"add carry intrinsics support only support powers of 2 bits", false)); } } +#endif // NO_X86_INTRINSICS template static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept { diff --git a/include/bitlib/bit-iterator/bit_reference.hpp b/include/bitlib/bit-iterator/bit_reference.hpp index 6a706c6a..87af4ba3 100644 --- a/include/bitlib/bit-iterator/bit_reference.hpp +++ b/include/bitlib/bit-iterator/bit_reference.hpp @@ -15,6 +15,7 @@ // ================================ PREAMBLE ================================ // // C++ standard library // Project sources +#include #include #include "bit_details.hpp" @@ -221,12 +222,12 @@ constexpr bit_reference::operator bool() const noexcept { // Gets a bit pointer from the bit reference template constexpr bit_pointer> bit_reference::operator&() const noexcept { - return bit_pointer(&_ref, _tzcnt(_mask)); + return bit_pointer(&_ref, std::countr_zero(_mask)); } template constexpr bit_pointer> bit_reference::operator&() noexcept { - return bit_pointer(&_ref, _tzcnt(_mask)); + return bit_pointer(&_ref, std::countr_zero(_mask)); } // -------------------------------------------------------------------------- //