diff --git a/.codecov.yml b/.codecov.yml index 464e2d45..fd894e6f 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,5 @@ ignore: - "**/libpopcnt.h" - - "test/inc/*" + - "test/**" - "benchmark/**" - "example/**" \ No newline at end of file diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index ce816881..046edde5 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -3,6 +3,7 @@ name: CMake on multiple platforms on: + workflow_dispatch: push: branches: [ "master" ] paths-ignore: @@ -12,6 +13,7 @@ on: paths-ignore: - '**/*.md' + jobs: build: runs-on: ${{ matrix.distro }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 061f11a9..32f3b4a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,9 +57,25 @@ target_sources(bitlib INTERFACE ) target_compile_features(bitlib INTERFACE cxx_std_23) +include(CheckCXXSourceCompiles) if (BITLIB_MDSPAN) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(USING_LIBSTDCXX 1) + endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CODE " + #include + #ifdef __GLIBCXX__ + int main() { return 0; } + #else + #error Not using libstdc++ + #endif + ") + + check_cxx_source_compiles("${CODE}" USING_LIBSTDCXX) + endif() + if (USING_LIBSTDCXX) if (NOT TARGET std::mdspan) find_package(mdspan CONFIG) if (NOT mdspan_DIR OR "${mdspan_DIR}" STREQUAL "mdspan-NOTFOUND") diff --git a/include/bitlib/bit-algorithms/bit_algorithm_details.hpp b/include/bitlib/bit-algorithms/bit_algorithm_details.hpp index 57e2fb87..3be853b6 100644 --- a/include/bitlib/bit-algorithms/bit_algorithm_details.hpp +++ b/include/bitlib/bit-algorithms/bit_algorithm_details.hpp @@ -4,6 +4,7 @@ // Description: A set of utilities to assist in writing algorithms // Contributor(s): Vincent Reverdy [2019] // Bryce Kille [2019] +// Peter McLean [2025] // License: BSD 3-Clause License // ========================================================================== // #ifndef _BIT_ALGORITHM_DETAILS_HPP_INCLUDED @@ -103,12 +104,11 @@ constexpr bool is_within( // Get next len bits beginning at start and store them in a word of type T template -T get_word(bit_iterator first, size_t len=binary_digits::value) -{ +T get_word(const bit_iterator& first, size_t len = binary_digits::value) { using native_word_type = typename bit_iterator::word_type; constexpr T digits = binary_digits::value; assert(digits >= len); - using non_const_T = std::remove_cv_t; + using non_const_T = std::remove_cvref_t; non_const_T offset = digits - first.position(); non_const_T ret_word = lsr(*first.base(), first.position()); @@ -224,7 +224,7 @@ void write_word(src_type src, bit_iterator dst_bit_it, } else { *dst_bit_it.base() = _bitblend( *dst_bit_it.base(), - src << dst_bit_it.position(), + static_cast(src << dst_bit_it.position()), dst_bit_it.position(), std::min( dst_digits - dst_bit_it.position(), diff --git a/include/bitlib/bit-algorithms/copy.hpp b/include/bitlib/bit-algorithms/copy.hpp index 9ecb336a..00913d6f 100644 --- a/include/bitlib/bit-algorithms/copy.hpp +++ b/include/bitlib/bit-algorithms/copy.hpp @@ -22,38 +22,41 @@ namespace bit { // ========================================================================== // +struct copy_impl; +// Status: Does not work for Input/Output iterators due to distance call +template +constexpr bit_iterator copy( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} // ---------------------------- Copy Algorithms ----------------------------- // - -// Status: Does not work for Input/Output iterators due to distance call -template -constexpr bit_iterator copy(bit_iterator first, - bit_iterator last, - bit_iterator d_first -) -{ +struct copy_impl { + // Status: Does not work for Input/Output iterators due to distance call + template + constexpr bit_iterator operator()( + bit_iterator first, + bit_iterator last, + bit_iterator d_first) { // Types and constants using dst_word_type = typename bit_iterator::word_type; using src_word_type = typename bit_iterator::word_type; // This checks for differing word types and uses an unoptimized copy in that event - if (!::std::is_same::value) { - while (first != last) { - *d_first = *first; - first++; - d_first++; - } - return d_first; - } + static_assert(std::is_same::value, "Both types must be the same"); + using word_type = dst_word_type; using size_type = typename bit_iterator::size_type; constexpr size_type digits = binary_digits::value; // Assertions _assert_range_viability(first, last); - if (first == last) return d_first; - + if (first == last) { + return d_first; + } // Initialization const bool is_d_first_aligned = d_first.position() == 0; @@ -61,25 +64,21 @@ constexpr bit_iterator copy(bit_iterator first size_type remaining_bits_to_copy = total_bits_to_copy; auto it = d_first.base(); - // d_first is not aligned. Copy partial word to align it if (!is_d_first_aligned) { - size_type partial_bits_to_copy = ::std::min( - remaining_bits_to_copy, - digits - d_first.position() - ); - *it = _bitblend( - *it, - static_cast( - get_word(first, partial_bits_to_copy) - << static_cast(d_first.position()) - ), - static_cast(d_first.position()), - static_cast(partial_bits_to_copy) - ); - remaining_bits_to_copy -= partial_bits_to_copy; - advance(first, partial_bits_to_copy); - it++; + size_type partial_bits_to_copy = ::std::min( + remaining_bits_to_copy, + digits - d_first.position()); + *it = _bitblend( + *it, + static_cast( + get_word(first, partial_bits_to_copy) + << static_cast(d_first.position())), + static_cast(d_first.position()), + static_cast(partial_bits_to_copy)); + remaining_bits_to_copy -= partial_bits_to_copy; + advance(first, partial_bits_to_copy); + it++; } if (remaining_bits_to_copy > 0) { @@ -108,12 +107,13 @@ constexpr bit_iterator copy(bit_iterator first } } return d_first + total_bits_to_copy; -} + } +}; // -------------------------------------------------------------------------- // // ========================================================================== // -} // namespace bit +} // namespace bit #endif // _COPY_HPP_INCLUDED // ========================================================================== // diff --git a/include/bitlib/bit-algorithms/copy_backward.hpp b/include/bitlib/bit-algorithms/copy_backward.hpp index 67c071f0..365ade22 100644 --- a/include/bitlib/bit-algorithms/copy_backward.hpp +++ b/include/bitlib/bit-algorithms/copy_backward.hpp @@ -56,13 +56,12 @@ constexpr bit_iterator copy_backward(bit_iterator( - *it, - static_cast( - get_word(last - partial_bits_to_copy, partial_bits_to_copy) - ) << (d_last.position() - partial_bits_to_copy), - d_last.position() - partial_bits_to_copy, - static_cast(partial_bits_to_copy) - ); + *it, + static_cast( + get_word(last - partial_bits_to_copy, partial_bits_to_copy) + << (d_last.position() - partial_bits_to_copy)), + d_last.position() - partial_bits_to_copy, + static_cast(partial_bits_to_copy)); remaining_bits_to_copy -= partial_bits_to_copy; advance(last, -partial_bits_to_copy); } @@ -87,14 +86,13 @@ constexpr bit_iterator copy_backward(bit_iterator 0) { - *it = _bitblend( - *it, - get_word(last - remaining_bits_to_copy, remaining_bits_to_copy) - << (digits - remaining_bits_to_copy), - digits - remaining_bits_to_copy, - remaining_bits_to_copy - ); - remaining_bits_to_copy = 0; + *it = _bitblend( + *it, + static_cast(get_word(last - remaining_bits_to_copy, remaining_bits_to_copy) + << (digits - remaining_bits_to_copy)), + digits - remaining_bits_to_copy, + remaining_bits_to_copy); + remaining_bits_to_copy = 0; } } return d_last - total_bits_to_copy; diff --git a/include/bitlib/bit-algorithms/equal.hpp b/include/bitlib/bit-algorithms/equal.hpp index a733a625..60642d8e 100644 --- a/include/bitlib/bit-algorithms/equal.hpp +++ b/include/bitlib/bit-algorithms/equal.hpp @@ -2,14 +2,13 @@ // Project: The Experimental Bit Algorithms Library // Name: equal.hpp // Contributor: Bryce Kille [2019] +// Peter McLean [2025] // License: BSD 3-Clause License // ========================================================================== // #ifndef _EQUAL_HPP_INCLUDED #define _EQUAL_HPP_INCLUDED // ========================================================================== // - - // ================================ PREAMBLE ================================ // // C++ standard library #include @@ -20,30 +19,39 @@ // Miscellaneous namespace bit { // ========================================================================== // +struct equal_impl; - +// Status: Does not work for Input/Output iterators due to distance call +template +constexpr bool equal( + const bit_iterator& first, + const bit_iterator& last, + const bit_iterator& d_first) { + return with_bit_iterator_adapter(first, last, d_first); +} // ---------------------------- Equal Algorithms ----------------------------- // // Status: Does not work for Input/Output iterators due to distance call -template -constexpr bool equal( - bit_iterator first, - bit_iterator last, - bit_iterator d_first -) -{ +struct equal_impl { + template + constexpr bool operator()( + bit_iterator first, + bit_iterator last, + bit_iterator d_first) { // Types and constants using dst_word_type = typename bit_iterator::word_type; using src_word_type = typename bit_iterator::word_type; + static_assert(::std::is_same::value, "Underlying word types must be equal"); using word_type = dst_word_type; using size_type = typename bit_iterator::size_type; constexpr size_type digits = binary_digits::value; // Assertions _assert_range_viability(first, last); - static_assert(::std::is_same::value, "Underlying word types must be equal"); - if (first == last) return true; + if (first == last) { + return true; + } // Initialization const bool is_d_first_aligned = d_first.position() == 0; @@ -53,48 +61,55 @@ constexpr bool equal( // d_first is not aligned. if (!is_d_first_aligned) { - const size_type partial_bits_to_check = ::std::min( - remaining_bits_to_check, - digits - d_first.position()); - const word_type mask = _mask(partial_bits_to_check) << d_first.position(); - const word_type comp = static_cast( - get_word(first, partial_bits_to_check) - << d_first.position()); - if ((mask & *it) != (mask & comp)) { return false; } - remaining_bits_to_check -= partial_bits_to_check; - advance(first, partial_bits_to_check); - it++; + const size_type partial_bits_to_check = ::std::min( + remaining_bits_to_check, + digits - d_first.position()); + const word_type mask = _mask(partial_bits_to_check) << d_first.position(); + const word_type comp = static_cast( + get_word(first, partial_bits_to_check) + << d_first.position()); + if ((mask & *it) != (mask & comp)) { + return false; + } + remaining_bits_to_check -= partial_bits_to_check; + advance(first, partial_bits_to_check); + it++; } if (remaining_bits_to_check > 0) { - const bool is_first_aligned = first.position() == 0; - // d_first will be aligned at this point - if (is_first_aligned && remaining_bits_to_check >= digits) { - auto N = ::std::distance(first.base(), last.base()); - bool found_mismatch = !::std::equal(first.base(), last.base(), it); - if (found_mismatch) {return false;} - it += N; - first += digits * N; - remaining_bits_to_check -= digits * N; - } else { - // TODO benchmark if its faster to ::std::check the entire range then shift - while (remaining_bits_to_check >= digits) { - if (*it != get_word(first, digits)) {return false;} - remaining_bits_to_check -= digits; - it++; - advance(first, digits); - } + const bool is_first_aligned = first.position() == 0; + // d_first will be aligned at this point + if (is_first_aligned && remaining_bits_to_check >= digits) { + auto N = ::std::distance(first.base(), last.base()); + bool found_mismatch = !::std::equal(first.base(), last.base(), it); + if (found_mismatch) { + return false; } - if (remaining_bits_to_check > 0) { - const word_type mask = _mask(remaining_bits_to_check); - const word_type comp = get_word(first, remaining_bits_to_check); - if ((mask & *it) != (mask & comp)) { + it += N; + first += digits * N; + remaining_bits_to_check -= digits * N; + } else { + // TODO benchmark if its faster to ::std::check the entire range then shift + while (remaining_bits_to_check >= digits) { + if (*it != get_word(first, digits)) { return false; } + remaining_bits_to_check -= digits; + it++; + advance(first, digits); + } + } + if (remaining_bits_to_check > 0) { + const word_type mask = _mask(remaining_bits_to_check); + const word_type comp = get_word(first, remaining_bits_to_check); + if ((mask & *it) != (mask & comp)) { + return false; } + } } return true; -} + } +}; // -------------------------------------------------------------------------- // diff --git a/include/bitlib/bit-algorithms/shift.hpp b/include/bitlib/bit-algorithms/shift.hpp index 1d1bf3a6..279877bc 100644 --- a/include/bitlib/bit-algorithms/shift.hpp +++ b/include/bitlib/bit-algorithms/shift.hpp @@ -77,13 +77,13 @@ bit_iterator shift_left( if (std::next(first.base(), is_last_aligned) == last.base()) { *first.base() = _bitblend( *first.base(), - lsr( + static_cast(lsr( *first.base() & ( lsr(static_cast(-1), ( digits - (is_last_aligned ? digits : last.position()) )) ) - , n), + , n)), first.position(), (is_last_aligned ? digits : last.position()) - first.position() ); @@ -107,7 +107,7 @@ bit_iterator shift_left( if (first.position() >= middle.position()) { *first.base() = _bitblend( *first.base(), - (*middle.base()) << (first.position() - middle.position()), + static_cast((*middle.base()) << (first.position() - middle.position())), first.position(), digits - first.position()); } else { @@ -120,7 +120,7 @@ bit_iterator shift_left( n1); *first.base() = _bitblend( *first.base(), - (*std::next(middle.base())) << (digits - n2), + static_cast((*std::next(middle.base())) << (digits - n2)), first.position() + n1, n2); } @@ -249,17 +249,14 @@ bit_iterator shift_right( // Single word case if (std::next(first.base(), is_last_aligned) == last.base()) { - *first.base() = _bitblend( - *first.base(), - ( - *first.base() & ( - static_cast(-1) << first.position() - ) - ) << n, - first.position(), - (is_last_aligned ? digits : last.position()) - first.position() - ); - return first + n; + *first.base() = _bitblend( + *first.base(), + static_cast( + (*first.base() & (static_cast(-1) << first.position())) + << n), + static_cast(first.position()), + static_cast((is_last_aligned ? digits : last.position()) - first.position())); + return first + n; } // Align last diff --git a/include/bitlib/bit-algorithms/type_traits.hpp b/include/bitlib/bit-algorithms/type_traits.hpp index 0cd1fb20..56b76d29 100644 --- a/include/bitlib/bit-algorithms/type_traits.hpp +++ b/include/bitlib/bit-algorithms/type_traits.hpp @@ -55,9 +55,8 @@ struct is_bit // Is bit structure specialization: bit reference template -struct is_bit> -: std::true_type -{ +struct is_bit> + : std::true_type { }; // Is bit value template definition diff --git a/include/bitlib/bit-containers/bit_array.hpp b/include/bitlib/bit-containers/bit_array.hpp index 53e32e1f..e531ca2f 100644 --- a/include/bitlib/bit-containers/bit_array.hpp +++ b/include/bitlib/bit-containers/bit_array.hpp @@ -50,11 +50,10 @@ using bit_array_d_cit = typename std::conditional, uint8_t, T>> -class bit_array : public bit_array_base, T, N, W, detail::bit_array_d_it, detail::bit_array_d_cit> { + typename W = std::conditional_t<(N == std::dynamic_extent), std::uintptr_t, ceil_integral>> +class bit_array : public bit_array_base, T, N, W, detail::bit_array_d_it, detail::bit_array_d_cit> { public: - using base = bit_array_base, T, N, W, detail::bit_array_d_it, detail::bit_array_d_cit>; + using base = bit_array_base, T, N, W, detail::bit_array_d_it, detail::bit_array_d_cit>; using base::end; using typename base::const_iterator; using typename base::const_pointer; @@ -75,10 +74,7 @@ class bit_array : public bit_array_base, T, N, W, detail:: } private: - static constexpr std::size_t Words_ = Words(N); - static constexpr std::size_t AlignedWords = (((Words_ * sizeof(word_type) + static_cast(V) - 1) & ~(static_cast(V) - 1)) + sizeof(word_type) - 1) / sizeof(word_type); - - alignas(static_cast(V)) std::array storage; + std::array storage; public: /* @@ -116,10 +112,10 @@ class bit_array : public bit_array_base, T, N, W, detail:: } } - constexpr bit_array(const bit_array& other) noexcept + constexpr bit_array(const bit_array& other) noexcept : storage(other.storage) {} - constexpr bit_array(const bit_array&& other) noexcept + constexpr bit_array(const bit_array&& other) noexcept : storage(other.storage) {} constexpr bit_array(const bit_sized_range auto& other) { @@ -148,7 +144,7 @@ class bit_array : public bit_array_base, T, N, W, detail:: constexpr bit_array(const std::initializer_list init) : storage{} { // Make sure we handle the case where init.size() != Words auto it = init.begin(); - for (size_type i = 0; i < std::min(AlignedWords, init.size()); ++i, ++it) { + for (size_type i = 0; i < std::min(Words(N), init.size()); ++i, ++it) { storage[i] = *it; } } @@ -173,7 +169,7 @@ class bit_array : public bit_array_base, T, N, W, detail:: /* * Assignment */ - constexpr bit_array& operator=(const bit_array& other) = default; + constexpr bit_array& operator=(const bit_array& other) = default; constexpr bit_array& operator=(const bit_sized_range auto& other) { if (other.size() != this->size()) [[unlikely]] { @@ -183,7 +179,7 @@ class bit_array : public bit_array_base, T, N, W, detail:: return *this; }; - constexpr bit_array& operator=(bit_array&& other) noexcept { + constexpr bit_array& operator=(bit_array&& other) noexcept { std::copy(other.storage.begin(), other.storage.end(), storage.begin()); return *this; } @@ -217,7 +213,7 @@ class bit_array : public bit_array_base, T, N, W, detail:: /* * Operations */ - constexpr void swap(bit_array& other) noexcept { + constexpr void swap(bit_array& other) noexcept { std::swap(this->storage, other.storage); } }; diff --git a/include/bitlib/bit-containers/bit_array_base.hpp b/include/bitlib/bit-containers/bit_array_base.hpp index 009a29e2..92414d4e 100644 --- a/include/bitlib/bit-containers/bit_array_base.hpp +++ b/include/bitlib/bit-containers/bit_array_base.hpp @@ -34,7 +34,6 @@ class bit_array_ref; template class bit_array; // ========================================================================== // @@ -67,8 +66,8 @@ class bit_array_base { using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using reference = typename std::conditional, bit_reference, T&>::type; - using const_reference = typename std::conditional, const bit_reference, const T&>::type; + using reference = typename std::conditional, bit_reference, T&>::type; + using const_reference = typename std::conditional, const bit_reference, const T&>::type; using pointer = typename std::conditional, bit_pointer, T&>::type; using const_pointer = const pointer; using iterator = It; @@ -196,7 +195,7 @@ class bit_array_base { return integral; } - using compatible_bitarray = bit_array; + using compatible_bitarray = bit_array; constexpr compatible_bitarray operator~() { compatible_bitarray result(derived().size()); diff --git a/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp b/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp index 4d6a7635..a446d831 100644 --- a/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp +++ b/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp @@ -37,11 +37,11 @@ using bit_array_cit = typename std::conditional, const word_type*>::type; } // namespace detail -template -class bit_array - : public bit_array_base, T, std::dynamic_extent, W, detail::bit_array_it, detail::bit_array_cit> { +template +class bit_array + : public bit_array_base, T, std::dynamic_extent, W, detail::bit_array_it, detail::bit_array_cit> { public: - using base = bit_array_base, T, std::dynamic_extent, W, detail::bit_array_it, detail::bit_array_cit>; + using base = bit_array_base, T, std::dynamic_extent, W, detail::bit_array_it, detail::bit_array_cit>; using base::end; using typename base::const_iterator; using typename base::const_pointer; @@ -55,51 +55,98 @@ class bit_array using typename base::word_type; private: - struct deleter { - size_type words; - void operator()(word_type* const p) const { - for (size_type i = 0; i < words; ++i) { - (p + i)->~word_type(); + using word_type_ptr = std::unique_ptr; + static const size_type FixedWords = sizeof(word_type_ptr) / sizeof(word_type); + static const size_type FixedBits = FixedWords * bitsof(); + + const size_type m_size; + + union Storage { + word_type_ptr pointer; + word_type fixed[FixedWords]; + + Storage(size_type words) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(new word_type[words]); + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(); + } } - ::operator delete(p, V); } - }; - const size_type m_size; - const std::unique_ptr storage; + Storage(size_type words, Storage&& other) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(std::move(other.pointer)); + other.pointer = nullptr; // Prevent double deletion + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(std::move(other.fixed[i])); + } + } + } + + Storage(size_type words, const Storage& other) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(new word_type[words]); + std::memcpy(pointer.get(), other.pointer.get(), words * sizeof(word_type)); + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(other.fixed[i]); + } + } + } + template + Storage(size_type words, const U& val) { + if (words > FixedWords) { + new (&pointer) word_type_ptr(new word_type[words](val)); + } else { + for (size_type i = 0; i < words; ++i) { + new (&fixed[i]) word_type(val); + } + } + } + Storage() = delete; + ~Storage() {} + } storage; static constexpr size_type Words(size_type N) { return (N * bitsof() + bitsof() - 1) / bitsof(); }; - - static constexpr size_t AlignedBytes(size_t N) { - return (Words(N) * sizeof(word_type) + static_cast(V) - 1) & ~(static_cast(V) - 1); - }; + /* + constexpr size_type Words() const { + return Words(m_size); + }*/ public: + ~bit_array() { + if (size() > FixedBits) { + storage.pointer.~unique_ptr(); + } else { + for (size_type i = 0; i < Words(size()); ++i) { + storage.fixed[i].~word_type(); + } + } + } + /* * Constructors, copies and moves... */ bit_array() = delete; constexpr bit_array(const size_type size) - : m_size(size), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - //std::uninitialized_fill_n(this->begin(), Words(m_size), word_type()); - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(); - } + : m_size(size), storage(Words(size)) { } template constexpr bit_array(const size_type size, const U& integral) - : m_size(size), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { + : bit_array(size) { assert(bitsof() <= size); - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(); + if (size() > FixedBits) { + std::memcpy(storage.pointer, &integral, sizeof(integral)); + } else { + std::memcpy(storage.fixed, &integral, sizeof(integral)); } - std::memcpy(storage.get(), &integral, sizeof(integral)); bool sign_extend = false; if constexpr (std::is_signed_v) { sign_extend = (integral & (1 << (bitsof() - 1))) ? true : false; @@ -115,50 +162,32 @@ class bit_array } } + constexpr bit_array(const size_type size, const word_type val) + : m_size(size), storage(Words(size), val) { + } + constexpr bit_array(const size_type size, const value_type bit_val) - : m_size(size), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - if constexpr (std::is_same::value) { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(bit_val); - } - } else { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(); - } - this->fill(bit_val); - } + requires(!std::is_same::value) + : m_size(size), storage(Words(size)) { + this->fill(bit_val); } - constexpr bit_array(const bit_array& other) - : m_size(other.size()), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(*(other.storage.get() + i)); - } + constexpr bit_array(const bit_array& other) + : m_size(other.size()), storage(Words(size()), other.storage) { } constexpr bit_array(const bit_sized_range auto& other) - : m_size(other.size()), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { + : bit_array(other.size()) { ::bit::copy(other.begin(), other.end(), this->begin()); } - constexpr bit_array(const bit_array&& other) - : m_size(other.size()), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(*(other.storage.get() + i)); - } + constexpr bit_array(bit_array&& other) + : m_size(other.size()), storage(Words(size()), std::move(other.storage)) { } constexpr bit_array(const std::initializer_list init) requires(!std::is_same_v) - : m_size(init.size()), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(); - } + : bit_array(init.size()) { std::copy(init.begin(), init.end(), this->begin()); } @@ -172,23 +201,15 @@ class bit_array } #endif - constexpr bit_array(const std::initializer_list init) - : m_size(bitsof() * init.size()), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - size_type i = 0; - auto it = init.begin(); - for (; i < Words(m_size) && it != init.end(); ++i, ++it) { - new (storage.get() + i) word_type(*it); - } + template + constexpr bit_array(const std::initializer_list init) + : bit_array(bitsof() * init.size()) { + std::copy(init.begin(), init.end(), data()); } constexpr bit_array(const std::string_view s) requires(std::is_same_v) - : m_size((std::count(s.begin(), s.end(), '0') + std::count(s.begin(), s.end(), '1'))), - storage(static_cast(::operator new(AlignedBytes(m_size), V)), deleter{Words(m_size)}) { - for (size_type i = 0; i < Words(m_size); ++i) { - new (storage.get() + i) word_type(); - } + : bit_array((std::count(s.begin(), s.end(), '0') + std::count(s.begin(), s.end(), '1'))) { size_type i = 0; for (char c : s) { if (c == '0') { @@ -199,20 +220,21 @@ class bit_array } } - ~bit_array() = default; - /* * Assignment */ - constexpr bit_array& operator=(const bit_array& other) { - if (nullptr == storage.get() || m_size != other.m_size) { + constexpr bit_array& operator=(const bit_array& other) { + if (nullptr == data() || size() != other.size()) { throw std::invalid_argument("Cannot reassign bit_array size"); } + if (this == &other) [[unlikely]] { + return *this; + } std::copy(other.begin(), other.end(), this->begin()); return *this; } - constexpr bit_array& operator=(const bit_sized_range auto& other) { + constexpr bit_array& operator=(const bit_sized_range auto& other) { if (other.size() != this->size()) [[unlikely]] { throw std::invalid_argument("other bit_range contains an invalid number of bits for bit_array."); } @@ -220,11 +242,12 @@ class bit_array return *this; }; - constexpr bit_array& operator=(bit_array&& other) { - if (nullptr == storage.get() || m_size != other.m_size) { + constexpr bit_array& operator=(bit_array&& other) { + if (nullptr == data() || size() != other.size()) { throw std::invalid_argument("Cannot reassign bit_array size"); } - std::copy(other.begin(), other.end(), this->begin()); + bit_array temp(std::move(other)); + swap(temp); return *this; } @@ -232,22 +255,38 @@ class bit_array * Element Access */ constexpr word_type* data() noexcept { - return size() ? storage.get() : nullptr; + if (size() > FixedBits) { + return storage.pointer.get(); + } else { + return storage.fixed; + } } constexpr const word_type* data() const noexcept { - return size() ? storage.get() : nullptr; + if (size() > FixedBits) { + return storage.pointer.get(); + } else { + return storage.fixed; + } } /* * Iterators */ constexpr iterator begin() noexcept { - return iterator(storage.get()); + if (size() > FixedBits) { + return iterator(storage.pointer.get()); + } else { + return iterator(storage.fixed); + } } constexpr const_iterator begin() const noexcept { - return const_iterator(storage.get()); + if (size() > FixedBits) { + return const_iterator(storage.pointer.get()); + } else { + return const_iterator(storage.fixed); + } } /* @@ -260,13 +299,14 @@ class bit_array /* * Operations */ - constexpr void swap(bit_array& other) noexcept { - assert(this->m_size == other.m_size); - // Cannot just swap storage as it is const. - W* it1 = this->storage.get(); - W* it2 = other.storage.get(); - for (size_type i = 0; i < Words(this->m_size); i++, it1++, it2++) { - std::swap(*it1, *it2); + constexpr void swap(bit_array& other) noexcept { + assert(size() == other.size()); + if (size() > FixedBits) { + std::swap(this->storage.pointer, other.storage.pointer); + } else { + for (size_type i = 0; i < Words(size()); ++i) { + std::swap(this->storage.fixed[i], other.storage.fixed[i]); + } } } }; diff --git a/include/bitlib/bit-containers/bit_array_ref.hpp b/include/bitlib/bit-containers/bit_array_ref.hpp index a55377cc..038c9bc9 100644 --- a/include/bitlib/bit-containers/bit_array_ref.hpp +++ b/include/bitlib/bit-containers/bit_array_ref.hpp @@ -37,7 +37,7 @@ namespace bit { * @tparam T The value type (typically bit_value) * @tparam W The word type used for storage */ -template +template class bit_array_ref : public bit_array_base, T, std::dynamic_extent, W, bit_iterator, bit_iterator> { public: diff --git a/include/bitlib/bit-containers/bit_bitsof.hpp b/include/bitlib/bit-containers/bit_bitsof.hpp index 6eafe2c0..ca369407 100644 --- a/include/bitlib/bit-containers/bit_bitsof.hpp +++ b/include/bitlib/bit-containers/bit_bitsof.hpp @@ -23,11 +23,18 @@ concept HasBits = requires { }; // General case: for types that do not have a bits member. -template requires (!HasBits) +template + requires(!HasBits && !std::is_integral_v) constexpr std::size_t bitsof() noexcept { return 8u * sizeof(T); } +template + requires(!HasBits && std::is_integral_v) +constexpr std::size_t bitsof() noexcept { + return std::integral_constant>::digits>::value; +} + // Overload for types with a bits member. template requires HasBits constexpr std::size_t bitsof() noexcept { diff --git a/include/bitlib/bit-containers/bit_mdspan_accessor.hpp b/include/bitlib/bit-containers/bit_mdspan_accessor.hpp index 68b6febe..a948a117 100644 --- a/include/bitlib/bit-containers/bit_mdspan_accessor.hpp +++ b/include/bitlib/bit-containers/bit_mdspan_accessor.hpp @@ -14,7 +14,7 @@ #include "bitlib/bit-iterator/bit_value.hpp" namespace bit { -template +template struct bit_default_accessor { using element_type = bit_value; using data_handle_type = bit_pointer; diff --git a/include/bitlib/bit-containers/bit_span.hpp b/include/bitlib/bit-containers/bit_span.hpp index 74c8ed2f..a1bd52a5 100644 --- a/include/bitlib/bit-containers/bit_span.hpp +++ b/include/bitlib/bit-containers/bit_span.hpp @@ -41,8 +41,8 @@ class bit_span : private bit_span_storage { using word_type = WordType; using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using reference = bit_reference; - using const_reference = const bit_reference; + using reference = bit_reference; + using const_reference = const bit_reference; using pointer = bit_pointer; using const_pointer = const pointer; using iterator = bit_iterator; diff --git a/include/bitlib/bit-containers/bit_vector.hpp b/include/bitlib/bit-containers/bit_vector.hpp index cb0b3ff2..a3fa9285 100644 --- a/include/bitlib/bit-containers/bit_vector.hpp +++ b/include/bitlib/bit-containers/bit_vector.hpp @@ -76,7 +76,7 @@ class bit_vector { using allocator_type = Allocator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using reference = bit_reference; + using reference = bit_reference; // typename std::vector::reference>; using const_reference = const reference; using pointer = bit_pointer; using iterator = bit_iterator::iterator>; diff --git a/include/bitlib/bit-iterator/bit.hpp b/include/bitlib/bit-iterator/bit.hpp index 8446eae2..d0b5f70d 100644 --- a/include/bitlib/bit-iterator/bit.hpp +++ b/include/bitlib/bit-iterator/bit.hpp @@ -23,6 +23,9 @@ #include "bit_iterator.hpp" #include "bit_reference.hpp" #include "bit_value.hpp" +#include "bit_word_pointer_adapter.hpp" +#include "bit_word_reference_adapter.hpp" + // Third-party libraries // Miscellaneous // ========================================================================== // diff --git a/include/bitlib/bit-iterator/bit_details.hpp b/include/bitlib/bit-iterator/bit_details.hpp index fbef2aa8..c59b97dd 100644 --- a/include/bitlib/bit-iterator/bit_details.hpp +++ b/include/bitlib/bit-iterator/bit_details.hpp @@ -40,9 +40,12 @@ namespace bit { class bit_value; template class bit_reference; -template class bit_iterator; +template +class bit_iterator; template using bit_pointer = bit_iterator; +template +class bit_word_pointer_adapter; // ========================================================================== // @@ -68,24 +71,22 @@ struct binary_digits : binary_digits_impl> {}; // Binary digits value template constexpr std::size_t binary_digits_v = binary_digits::value; -/* ************************************************************************** */ +/*************************************************************************** */ -#if 0 -template -using smallest_integral = std::conditional_t< - (sizeof(T) <= sizeof(std::uint8_t)), +template +using ceil_integral = std::conditional_t< + (N <= bitsof()), std::uint8_t, std::conditional_t< - (sizeof(T) <= sizeof(std::uint16_t)), + (N <= bitsof()), std::uint16_t, std::conditional_t< - (sizeof(T) <= sizeof(std::uint32_t)), + (N <= bitsof()), std::uint32_t, std::conditional_t< - (sizeof(T) <= sizeof(std::uint64_t)), + (N <= bitsof()), std::uint64_t, - T>>>>; -#endif + std::uint64_t>>>>; /* *************** IMPLEMENTATION DETAILS: CV ITERATOR TRAITS *************** */ // Cv iterator traits structure definition @@ -106,8 +107,9 @@ struct _cv_iterator_traits using _raw_pointer_t = typename std::remove_cv<_no_pointer_t>::type; using _raw_reference_t = typename std::remove_cv<_no_reference_t>::type; using _cv_value_t = _no_reference_t; - static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, ""); - static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, ""); + + // static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, ""); + // static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, ""); // Types public: @@ -119,8 +121,7 @@ struct _cv_iterator_traits }; /* ************************************************************************** */ - - +#if 0 /* *********** IMPLEMENTATION DETAILS: NARROWEST AND WIDEST TYPES *********** */ // Narrowest type structure declaration template @@ -282,8 +283,53 @@ struct _wider_type template using _wider_type_t = typename _wider_type::type; /* ************************************************************************** */ +#endif + +/* +exact_ceil_integral is used to determine the exact integral type that a proxy reference +can be implicitly converted to. +If the proxy reference supports multiple types, it will pick the largest, preferring unsigned. +*/ +template +struct exact_ceil_integral { + private: + using U = std::remove_cvref_t; + + template + static constexpr bool is_exactly_convertible() { + if constexpr (!std::is_convertible_v) { + return false; + } else { + // Try brace-initialization to detect narrowing + return requires(From f) { + To{f}; // will fail if narrowing + }; + } + } + public: + using type = std::conditional_t< + is_exactly_convertible(), uint64_t, + std::conditional_t< + is_exactly_convertible(), int64_t, + std::conditional_t< + is_exactly_convertible(), uint32_t, + std::conditional_t< + is_exactly_convertible(), int32_t, + std::conditional_t< + is_exactly_convertible(), uint16_t, + std::conditional_t< + is_exactly_convertible(), int16_t, + std::conditional_t< + is_exactly_convertible(), uint8_t, + std::conditional_t< + is_exactly_convertible(), int8_t, + void>>>>>>>>; +}; +// Helper alias +template +using exact_ceil_integral_t = typename exact_ceil_integral::type; /* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */ // Assertions @@ -318,6 +364,7 @@ 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; @@ -336,6 +383,8 @@ constexpr T _byteswap(T src) noexcept; template constexpr T _byteswap(T src, X...) noexcept; +#endif + // Bit swap template constexpr T _bitswap(T src) noexcept; @@ -344,12 +393,6 @@ constexpr T _bitswap(T src) noexcept; template constexpr T _bitswap() noexcept; -// Bit blend -template -constexpr T _bitblend(T src0, T src1, T msk) noexcept; -template -constexpr T _bitblend(T src0, T src1, T start, T len) noexcept; - // Bit exchange template constexpr void _bitexch(T& src0, T& src1, T msk) noexcept; @@ -401,10 +444,15 @@ constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept; Logical shift right */ template -constexpr T lsr(const T& val, const size_type shift) { +constexpr T lsr(const T val, const size_type shift) { return static_cast(static_cast>(val) >> shift); } +template +constexpr exact_ceil_integral_t lsr(const T val, const size_type shift) { + return static_cast>(static_cast>>(val) >> shift); +} + enum class _mask_len { unknown, in_range @@ -572,6 +620,7 @@ 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 @@ -691,6 +740,7 @@ constexpr T _byteswap(T src, X...) noexcept { return src; } // -------------------------------------------------------------------------- // +#endif // ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- // // Reverses the order of the bits with or without of compiler intrinsics @@ -752,18 +802,34 @@ constexpr T _bitswap() noexcept { // ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- // // Replaces bits of src0 by the ones of src1 where the mask is true -template -constexpr T _bitblend(T src0, T src1, T msk) noexcept { - static_assert(binary_digits::value, ""); + +template +constexpr exact_ceil_integral_t _bitblend( + const T src0_, + const U src1_, + const exact_ceil_integral_t msk) noexcept + requires(std::is_same_v, exact_ceil_integral_t>) +{ + static_assert(binary_digits>::value, ""); + const exact_ceil_integral_t src0 = static_cast>(src0_); + const exact_ceil_integral_t src1 = static_cast>(src1_); return src0 ^ ((src0 ^ src1) & msk); } // Replaces len bits of src0 by the ones of src1 starting at start -template -constexpr T _bitblend(T src0, T src1, T start, T len) noexcept { - static_assert(binary_digits::value, ""); - constexpr T digits = binary_digits::value; - const T msk = _mask(len) << start; +template +constexpr exact_ceil_integral_t _bitblend( + const T src0_, + const U src1_, + const exact_ceil_integral_t start, + const exact_ceil_integral_t len) noexcept + requires(std::is_same_v, exact_ceil_integral_t>) +{ + static_assert(binary_digits>::value, ""); + constexpr exact_ceil_integral_t digits = bitsof>(); + const exact_ceil_integral_t src0 = static_cast>(src0_); + const exact_ceil_integral_t src1 = static_cast>(src1_); + const exact_ceil_integral_t msk = _mask, _mask_len::unknown>(len) << start; return src0 ^ ((src0 ^ src1) & msk * (start < digits)); } // -------------------------------------------------------------------------- // @@ -951,7 +1017,7 @@ template constexpr T _mulx(T src0, T src1, T* hi) noexcept { static_assert(binary_digits::value, ""); - using wider_t = typename _wider_type::type; + using wider_t = ceil_integral() + bitsof()>; constexpr T digits = binary_digits::value; wider_t tmp = 0; T128 tmp128 = 0; @@ -994,7 +1060,50 @@ constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept } // -------------------------------------------------------------------------- // +template +constexpr auto with_bit_iterator_adapter( + bit_iterator first, + bit_iterator last, + bit_iterator d_first) { + using dst_word_type = typename bit_iterator::word_type; + using src_word_type = typename bit_iterator::word_type; + if constexpr (!std::is_same_v && bitsof() != bitsof()) { + if constexpr (bitsof() > bitsof()) { + bit_iterator> adapted_first( + bit_word_pointer_adapter(first.base(), first.position() / bitsof()), first.position() % bitsof()); + bit_iterator> adapted_last( + bit_word_pointer_adapter(last.base(), last.position() / bitsof()), last.position() % bitsof()); + return AlgoFunc{}(adapted_first, adapted_last, d_first); + } else { + bit_iterator> adapted_d_first( + bit_word_pointer_adapter(d_first.base(), d_first.position() / bitsof()), d_first.position() % bitsof()); + return AlgoFunc{}(first, last, adapted_d_first); + } + } else { + return AlgoFunc{}(first, last, d_first); + } +} +template +constexpr auto with_bit_iterator_adapter( + bit_iterator first, + bit_iterator last) { + using dst_word_type = typename bit_iterator::word_type; + using src_word_type = typename bit_iterator::word_type; + if constexpr (!std::is_same_v && bitsof() != bitsof()) { + if constexpr (bitsof() > bitsof()) { + bit_iterator> adapted_first( + bit_word_pointer_adapter(first.base(), first.position() / bitsof()), first.position() % bitsof()); + return AlgoFunc{}(adapted_first, last); + } else { + bit_iterator> adapted_last( + bit_word_pointer_adapter(last.base(), last.position() / bitsof()), last.position() % bitsof()); + return AlgoFunc{}(first, adapted_last); + } + } else { + return AlgoFunc{}(first, last); + } +} // ========================================================================== // } // namespace bit diff --git a/include/bitlib/bit-iterator/bit_iterator.hpp b/include/bitlib/bit-iterator/bit_iterator.hpp index f26fc8f2..458a4471 100644 --- a/include/bitlib/bit-iterator/bit_iterator.hpp +++ b/include/bitlib/bit-iterator/bit_iterator.hpp @@ -30,6 +30,10 @@ namespace bit { /* ****************************** BIT ITERATOR ****************************** */ + +template +class bit_word_pointer_adapter; + // Bit iterator class definition template class bit_iterator @@ -40,17 +44,17 @@ class bit_iterator // Assertions private: using _traits_t = _cv_iterator_traits; - static_assert(binary_digits::value, ""); + static_assert(binary_digits>::value, ""); // Types public: using iterator_type = Iterator; - using word_type = typename _traits_t::value_type; + using word_type = std::iter_value_t; using iterator_category = typename _traits_t::iterator_category; using value_type = bit_value; using difference_type = std::ptrdiff_t; using pointer = bit_pointer; - using reference = bit_reference; + using reference = bit_reference; // typename _traits_t::reference; using size_type = std::size_t; using mask_type = std::make_unsigned_t>; @@ -66,6 +70,9 @@ class bit_iterator explicit constexpr bit_iterator(const pointer& ptr) requires std::constructible_from; + template + constexpr bit_iterator(const bit_iterator>& other); + // Assignment public: constexpr bit_iterator& operator=(const bit_iterator& other); @@ -160,6 +167,13 @@ constexpr bit_iterator::bit_iterator(const pointer& ptr) : _current(ptr._current), _position(ptr.position()) { assert(_position < bitsof()); } + +template +template +constexpr bit_iterator::bit_iterator(const bit_iterator>& other) + : _current(other.base().base()), _position(other.base().index() * bitsof::value_type>() + other.position()) { + assert(_position < bitsof()); +} // -------------------------------------------------------------------------- // @@ -366,7 +380,7 @@ operator-(const bit_iterator& lhs, const bit_iterator& rhs) { } static_assert(bit_iterator_c>, "bit_iterator does not satisfy bit_iterator_c concept!"); -static_assert(bit_pointer_c, bit_reference>, "bit_pointer does not satisfy bit_pointer_c concept!"); +static_assert(bit_pointer_c, bit_reference>, "bit_pointer does not satisfy bit_pointer_c concept!"); static_assert(bit_iterator_c>, "bit_pointer does not satisfy bit_iterator_c concept!"); // ========================================================================== // diff --git a/include/bitlib/bit-iterator/bit_reference.hpp b/include/bitlib/bit-iterator/bit_reference.hpp index ee4327af..55fc4bf7 100644 --- a/include/bitlib/bit-iterator/bit_reference.hpp +++ b/include/bitlib/bit-iterator/bit_reference.hpp @@ -28,8 +28,12 @@ namespace bit { /* ***************************** BIT REFERENCE ****************************** */ // Bit reference class definition -template +template class bit_reference { + public: + using WordType = std::remove_reference_t; + + private: // Assertions static_assert(binary_digits::value, ""); @@ -46,11 +50,13 @@ class bit_reference { // Lifecycle public: template - requires (std::is_const_v == std::is_const_v) - constexpr bit_reference(const bit_reference& other) noexcept; + requires(std::is_const_v> == std::is_const_v>) + constexpr bit_reference(const bit_reference& other) noexcept + : _ref(other._ref), _mask(other._mask) { + } constexpr bit_reference(const bit_reference& other) noexcept; - explicit constexpr bit_reference(word_type& ref) noexcept; - constexpr bit_reference(word_type& ref, size_type pos); + explicit constexpr bit_reference(WordRef ref) noexcept; + constexpr bit_reference(WordRef ref, size_type pos); // Assignment public: @@ -96,7 +102,7 @@ class bit_reference { // Implementation details: data members private: - word_type& _ref; + WordRef _ref; const mask_type _mask; }; static_assert(bit_reference_c>, "bit_reference does not satisfy bit_reference_c concept!"); @@ -116,31 +122,22 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, bit_reference x); /* ************************************************************************** */ - - // ------------------------ BIT REFERENCE: LIFECYCLE ------------------------ // -// Implicitly constructs a bit reference from another bit reference -template -template -requires (std::is_const_v == std::is_const_v) -constexpr bit_reference::bit_reference(const bit_reference& other) noexcept - : _ref(other._ref), _mask(other._mask) { -} -template -constexpr bit_reference::bit_reference(const bit_reference& other) noexcept +template +constexpr bit_reference::bit_reference(const bit_reference& other) noexcept : _ref(other._ref), _mask(other._mask) { } // Explicitly constructs an aligned bit reference -template -constexpr bit_reference::bit_reference(word_type& ref) noexcept +template +constexpr bit_reference::bit_reference(WordRef ref) noexcept : _ref(ref), _mask(1) { } // Explicitly constructs an unaligned bit reference -template -constexpr bit_reference::bit_reference(word_type& ref, size_type pos) +template +constexpr bit_reference::bit_reference(WordRef ref, size_type pos) : _ref(ref), _mask(static_cast(1) << pos) { assert(pos < binary_digits::value); } @@ -150,40 +147,40 @@ constexpr bit_reference::bit_reference(word_type& ref, size_type pos) // ----------------------- BIT REFERENCE: ASSIGNMENT ------------------------ // // Copies a bit reference to the bit reference -template -constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { +template +constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { other ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns a bit reference to the bit reference -template +template template -constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { +constexpr bit_reference& bit_reference::operator=(const bit_reference& other) const noexcept { other ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns a bit value to the bit reference -template -constexpr bit_reference& bit_reference::operator=(const bit_value val) const noexcept { +template +constexpr bit_reference& bit_reference::operator=(const bit_value val) const noexcept { val ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns the aligned bit of a value to the bit reference -template -constexpr bit_reference& bit_reference::assign(word_type val) const noexcept { +template +constexpr bit_reference& bit_reference::assign(word_type val) const noexcept { val & 1 ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns an unaligned bit of a value to the bit reference -template -constexpr bit_reference& bit_reference::assign(word_type val, size_type pos) const { +template +constexpr bit_reference& bit_reference::assign(word_type val, size_type pos) const { assert(pos < binary_digits::value); ((val >> pos) & 1) ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // -------------------------------------------------------------------------- // @@ -191,24 +188,24 @@ constexpr bit_reference& bit_reference::assign(word_type val // -------------- BIT REFERENCE: BITWISE ASSIGNMENT OPERATORS --------------- // // Assigns the value of the referenced bit through a bitwise and operation -template -constexpr bit_reference& bit_reference::operator&=(bit_value other) const noexcept { +template +constexpr bit_reference& bit_reference::operator&=(bit_value other) const noexcept { _ref &= ~(_mask * static_cast(!other._value)); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns the value of the referenced bit through a bitwise or operation -template -constexpr bit_reference& bit_reference::operator|=(bit_value other) const noexcept { +template +constexpr bit_reference& bit_reference::operator|=(bit_value other) const noexcept { _ref |= _mask * static_cast(other._value); - return const_cast&>(*this); + return const_cast&>(*this); } // Assigns the value of the referenced bit through a bitwise xor operation -template -constexpr bit_reference& bit_reference::operator^=(bit_value other) const noexcept { +template +constexpr bit_reference& bit_reference::operator^=(bit_value other) const noexcept { _ref ^= _mask * static_cast(other._value); - return const_cast&>(*this); + return const_cast&>(*this); } // -------------------------------------------------------------------------- // @@ -216,8 +213,8 @@ constexpr bit_reference& bit_reference::operator^=(bit_value // ----------------------- BIT REFERENCE: CONVERSION ------------------------ // // Explicitly converts the bit reference to a boolean value -template -constexpr bit_reference::operator bool() const noexcept { +template +constexpr bit_reference::operator bool() const noexcept { return _ref & _mask; } // -------------------------------------------------------------------------- // @@ -226,8 +223,8 @@ constexpr bit_reference::operator bool() const noexcept { // ------------------------- BIT REFERENCE: ACCESS -------------------------- // // Gets a bit pointer from the bit reference -template -constexpr bit_pointer bit_reference::operator&() const noexcept { +template +constexpr bit_pointer> bit_reference::operator&() const noexcept { return bit_pointer(&_ref, _tzcnt(_mask)); } // -------------------------------------------------------------------------- // @@ -236,9 +233,9 @@ constexpr bit_pointer bit_reference::operator&() const noexc // ---------------------- BIT REFERENCE: SWAP MEMBERS ----------------------- // // Swaps the value of the referenced bit with another bit reference -template +template template -void bit_reference::swap(bit_reference other) const { +void bit_reference::swap(bit_reference other) const { if (other != *this) { flip(); other.flip(); @@ -246,8 +243,8 @@ void bit_reference::swap(bit_reference other) const { } // Swaps the value of the referenced bit with a bit value -template -void bit_reference::swap(bit_value& other) const { +template +void bit_reference::swap(bit_value& other) const { if (other != *this) { flip(); other.flip(); @@ -259,32 +256,32 @@ void bit_reference::swap(bit_value& other) const { // -------------------- BIT REFERENCE: BIT MANIPULATION --------------------- // // Sets the value of the referenced bit to the provided boolean value -template -constexpr bit_reference& bit_reference::set( +template +constexpr bit_reference& bit_reference::set( bool b) const noexcept { b ? set() : reset(); - return const_cast&>(*this); + return const_cast&>(*this); } // Sets the value of the referenced bit to 1 -template -constexpr bit_reference& bit_reference::set() const noexcept { +template +constexpr bit_reference& bit_reference::set() const noexcept { _ref |= _mask; - return const_cast&>(*this); + return const_cast&>(*this); } // Resets the value of the referenced bit to 0 -template -constexpr bit_reference& bit_reference::reset() const noexcept { +template +constexpr bit_reference& bit_reference::reset() const noexcept { _ref &= ~_mask; - return const_cast&>(*this); + return const_cast&>(*this); } // Flips the value of the referenced bit -template -constexpr bit_reference& bit_reference::flip() const noexcept { +template +constexpr bit_reference& bit_reference::flip() const noexcept { _ref ^= _mask; - return const_cast&>(*this); + return const_cast&>(*this); } // -------------------------------------------------------------------------- // @@ -379,14 +376,14 @@ std::basic_ostream& operator<<(std::basic_ostream& // -------- BIT REFERENCE: IMPLEMENTATION DETAILS: FUNCTION MEMBERS --------- // // Privately explicitly constructs an aligned bit reference from a pointer -template -constexpr bit_reference::bit_reference(word_type* ptr) noexcept +template +constexpr bit_reference::bit_reference(word_type* ptr) noexcept : _ref(*ptr), _mask(1) { } // Privately explicitly constructs an unaligned bit reference from a pointer -template -constexpr bit_reference::bit_reference(word_type* ptr, size_type pos) +template +constexpr bit_reference::bit_reference(word_type* ptr, size_type pos) : _ref(*ptr), _mask(static_cast(1) << pos) { assert(pos < binary_digits::value); } diff --git a/include/bitlib/bit-iterator/bit_value.hpp b/include/bitlib/bit-iterator/bit_value.hpp index f1c1ca11..02f7c9ba 100644 --- a/include/bitlib/bit-iterator/bit_value.hpp +++ b/include/bitlib/bit-iterator/bit_value.hpp @@ -48,14 +48,14 @@ class bit_value // Assignment public: - template - constexpr bit_value& operator=(bit_reference ref) noexcept; - template - constexpr bit_value& assign(WordType val) noexcept; - template - constexpr bit_value& assign(WordType val, size_type pos); - - // Bitwise assignment operators + template + constexpr bit_value& operator=(bit_reference ref) noexcept; + template + constexpr bit_value& assign(WordType val) noexcept; + template + constexpr bit_value& assign(WordType val, size_type pos); + + // Bitwise assignment operators public: constexpr bit_value& operator&=(bit_value other) noexcept; constexpr bit_value& operator|=(bit_value other) noexcept; diff --git a/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp b/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp new file mode 100644 index 00000000..797fcd1e --- /dev/null +++ b/include/bitlib/bit-iterator/bit_word_pointer_adapter.hpp @@ -0,0 +1,213 @@ +#ifndef _BIT_WORD_POINTER_ADAPTER_HPP_ +#define _BIT_WORD_POINTER_ADAPTER_HPP_ + +#include "bit_details.hpp" + +namespace bit { + +template +class bit_word_reference_adapter; + +template +class bit_word_pointer_adapter { + private: + // Assertions + using _traits_t = _cv_iterator_traits; + static_assert(binary_digits::value, ""); + + public: + using target_word = std::remove_const_t; + using target_word_ref = _traits_t::reference; + using source_word = typename _cv_iterator_traits::value_type; + using source_word_ref = std::add_lvalue_reference_t; + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = bit_word_reference_adapter; + using pointer = bit_word_pointer_adapter; + + using iterator_type = target_word_ptr; + using iterator_category = typename _cv_iterator_traits::iterator_category; + using value_type = target_word; + + private: + static_assert(std::is_integral_v, "source_word_ptr must be a pointer to a integral type"); + static_assert(std::is_integral_v, "target_word must be an integral type"); + + static constexpr bool is_small_to_big = sizeof(target_word) > sizeof(source_word); + static constexpr bool is_big_to_small = sizeof(target_word) < sizeof(source_word); + static constexpr size_t ratio = is_small_to_big ? sizeof(target_word) / sizeof(source_word) + : sizeof(source_word) / sizeof(target_word); + + source_word_ptr _source; + size_type _index; + + public: + explicit constexpr bit_word_pointer_adapter(source_word_ptr source) + requires(is_small_to_big) + : _source(source) { + } + explicit constexpr bit_word_pointer_adapter(source_word_ptr source, const size_type index = 0) + requires(is_big_to_small) + : _source(source), _index(index) { + } + + constexpr bit_word_pointer_adapter(const bit_word_pointer_adapter&) = default; + constexpr bit_word_pointer_adapter& operator=(const bit_word_pointer_adapter&) = default; + constexpr bit_word_pointer_adapter(bit_word_pointer_adapter&&) = default; + constexpr bit_word_pointer_adapter& operator=(bit_word_pointer_adapter&&) = default; + constexpr ~bit_word_pointer_adapter() = default; + + constexpr reference operator*() const noexcept { + if constexpr (is_small_to_big) { + return reference(*_source); + } else { + return reference(*_source, _index); + } + } + + constexpr bit_word_pointer_adapter operator->() const noexcept { + return bit_word_pointer_adapter(&*_source, _index); + } + + constexpr reference operator[](difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return reference(*std::next(_source, n*ratio)); + } else { + const difference_type sum = _index + n; + difference_type src_diff = sum / static_cast(ratio); + const size_type new_index = sum % ratio; + if (n < 0 && new_index > _index) { + --src_diff; + } + return reference(*std::next(_source, src_diff), new_index); + } + } + + constexpr bit_word_pointer_adapter& operator++() noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, ratio); + } else { + ++_index; + if (_index >= ratio) { + _source = std::next(_source); + _index -= ratio; + } + } + return *this; + } + constexpr bit_word_pointer_adapter operator++(int) noexcept { + bit_word_pointer_adapter old = *this; + ++(*this); + return old; + } + constexpr bit_word_pointer_adapter& operator--() noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, -ratio); + } else { + if ((_index--) == 0) { + _source = std::prev(_source); + _index += ratio; + } + } + return *this; + } + constexpr bit_word_pointer_adapter operator--(int) noexcept { + bit_word_pointer_adapter old = *this; + --(*this); + return old; + } + constexpr bit_word_pointer_adapter operator+(difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return bit_word_pointer_adapter(std::next(_source, n*ratio)); + } else { + const difference_type sum = _index + n; + difference_type src_diff = sum / static_cast(ratio); + const size_type new_index = sum % ratio; + if (n < 0 && new_index > _index) { + --src_diff; + } + return bit_word_pointer_adapter(std::next(_source, src_diff), new_index); + } + } + constexpr bit_word_pointer_adapter operator-(difference_type n) const noexcept { + if constexpr (is_small_to_big) { + return bit_word_pointer_adapter(std::next(_source, -n*ratio)); + } else { + const difference_type sum = _index - n; + difference_type src_diff = sum / static_cast(ratio); + const size_type new_index = sum % ratio; + if (n > 0 && new_index > _index) { + --src_diff; + } + return bit_word_pointer_adapter(std::next(_source, src_diff), new_index); + } + } + constexpr bit_word_pointer_adapter& operator+=(difference_type n) noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, n * ratio); + } else { + const difference_type sum = _index + n; + difference_type src_diff = sum / static_cast(ratio); + const size_type new_index = sum % ratio; + if (n < 0 && new_index > _index) { + --src_diff; + } + _source = std::next(_source, src_diff); + _index = new_index; + } + return *this; + } + constexpr bit_word_pointer_adapter& operator-=(difference_type n) noexcept { + if constexpr (is_small_to_big) { + _source = std::next(_source, -n * ratio); + } else { + const difference_type sum = _index - n; + difference_type src_diff = sum / static_cast(ratio); + const size_type new_index = sum % ratio; + if (n > 0 && new_index > _index) { + --src_diff; + } + _source = std::next(_source, src_diff); + _index = new_index; + } + return *this; + } + constexpr bool operator==(const bit_word_pointer_adapter&) const = default; + constexpr auto operator<=>(const bit_word_pointer_adapter&) const = default; + + constexpr size_type index() const noexcept { + return _index; + } + constexpr source_word_ptr base() const noexcept { + return _source; + } + template + friend constexpr auto operator-( + const bit_word_pointer_adapter& lhs, + const bit_word_pointer_adapter& rhs); +}; + +template +constexpr auto operator-( + const bit_word_pointer_adapter& lhs, + const bit_word_pointer_adapter& rhs) { + using lhs_type = typename bit_word_pointer_adapter::difference_type; + using rhs_type = typename bit_word_pointer_adapter::difference_type; + static_assert( + bit_word_pointer_adapter::ratio == + bit_word_pointer_adapter::ratio, + "Cannot subtract iterators with different ratios"); + if constexpr (bit_word_pointer_adapter::is_big_to_small) { + auto main = (lhs._source - rhs._source); + return main * static_cast>(bit_word_pointer_adapter::ratio) + (static_cast(lhs._index) - static_cast(rhs._index)); + } else { + // “small→large” mode: each base‐step is 1 small word, but difference is in big words: + auto small_diff = (lhs._source - rhs._source); + return small_diff / static_cast>(bit_word_pointer_adapter::ratio); + } +} + +} // namespace bit + +#endif // _BIT_WORD_POINTER_ADAPTER_HPP_ diff --git a/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp b/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp new file mode 100644 index 00000000..a768d270 --- /dev/null +++ b/include/bitlib/bit-iterator/bit_word_reference_adapter.hpp @@ -0,0 +1,119 @@ +#ifndef _BIT_WORD_REFERENCE_ADAPTER_HPP_ +#define _BIT_WORD_REFERENCE_ADAPTER_HPP_ + +namespace bit { + +template +class bit_word_pointer_adapter; + +template +class bit_word_reference_adapter { + private: + // Assertions + static_assert(std::is_reference_v, "source_word_ref must be a reference type"); + + using source_word = std::remove_reference_t; + using source_word_ptr = std::add_pointer_t; + using target_word = std::remove_cvref_t; + using target_word_ptr = std::add_pointer_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = bit_word_pointer_adapter; + + static_assert(std::is_integral_v, "source_word_ref must reference to a integral type"); + static_assert(std::is_integral_v, "target_word must be an integral type"); + + static constexpr bool is_small_to_big = sizeof(target_word) > sizeof(source_word); + static constexpr bool is_big_to_small = sizeof(target_word) < sizeof(source_word); + static constexpr size_t ratio = is_small_to_big ? sizeof(target_word) / sizeof(source_word) + : sizeof(source_word) / sizeof(target_word); + + source_word_ref _source; + size_type _index; + + public: + // Constructor from source + explicit constexpr bit_word_reference_adapter(source_word_ref source) + requires(is_small_to_big) + : _source(source), _index(0) { + } + constexpr bit_word_reference_adapter(source_word_ref source, const size_type index) + requires(is_big_to_small) + : _source(source), _index(index) { + } + explicit constexpr bit_word_reference_adapter(const bit_word_reference_adapter& other) { + if constexpr (is_small_to_big) { + *reinterpret_cast(&_source) = *reinterpret_cast(&_source); + } else { + *(reinterpret_cast(&_source) + _index) = *(reinterpret_cast(&_source) + other._index); + } + } + constexpr bit_word_reference_adapter& operator=(const bit_word_reference_adapter& other) { + if constexpr (is_small_to_big) { + *reinterpret_cast(&_source) = *reinterpret_cast(&_source); + } else { + *(reinterpret_cast(&_source) + _index) = *(reinterpret_cast(&_source) + other._index); + } + return *this; + } + + constexpr ~bit_word_reference_adapter() = default; + + constexpr pointer operator&() const noexcept { + if constexpr (is_small_to_big) { + return pointer(&_source); + } else { + return pointer(&_source, _index); + } + } + + // Assignment from value type. + // NOT safe for big-endian systems + constexpr bit_word_reference_adapter& operator=(const target_word& value) noexcept { + if constexpr (is_small_to_big) { + *reinterpret_cast(&_source) = value; + } else { + *(reinterpret_cast(&_source) + _index) = value; + } + return *this; + } + + // Implicit conversion to value type + // NOT safe for big-endian systems + constexpr operator target_word() const noexcept { + if constexpr (is_small_to_big) { + return *reinterpret_cast(&_source); + } else { + return *(reinterpret_cast(&_source) + _index); + } + } + +#if 0 + // Compound assignment (+=, -=, etc.) + bit_word_reference_adapter& operator+=(const target_word&); + bit_word_reference_adapter& operator-=(const target_word&); + // ... others like *=, /=, etc. + + // Increment/decrement + bit_word_reference_adapter& operator++(); // Prefix ++ + target_word operator++(int); // Postfix ++ + bit_word_reference_adapter& operator--(); // Prefix -- + target_word operator--(int); // Postfix -- +#endif + + // Swap support + + void swap(bit_word_reference_adapter& other) noexcept { + std::swap(_source, other._source); + std::swap(_index, other._index); + } + + friend void swap(bit_word_reference_adapter a, bit_word_reference_adapter b) { + std::swap(a._source, b._source); + std::swap(a._index, b._index); + } +}; + +} // namespace bit + +#endif // _BIT_WORD_REFERENCE_ADAPTER_HPP_ diff --git a/include/bitlib/bit_concepts.hpp b/include/bitlib/bit_concepts.hpp index 959b86f7..01f3c14d 100644 --- a/include/bitlib/bit_concepts.hpp +++ b/include/bitlib/bit_concepts.hpp @@ -5,7 +5,7 @@ #include #include -#if defined(__GNUC__) && (__GNUC__ < 14) && !defined(__clang__) +#if (defined(__GLIBCXX__) && (_GLIBCXX_RELEASE < 14)) namespace std { struct from_range_t { explicit from_range_t() = default; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d08332da..eda549e9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,6 +32,7 @@ target_sources(bitlib-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/test-equal.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/test-fill.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/test-find.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/test-iterator_adapter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/test-literal.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/test-move.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/test-reverse.cpp diff --git a/test/src/fixtures.hpp b/test/src/fixtures.hpp index f5b672f1..099d038f 100644 --- a/test/src/fixtures.hpp +++ b/test/src/fixtures.hpp @@ -19,15 +19,15 @@ #include #include // Project sources -#include "bitlib/bit-iterator/bit_iterator.hpp" #include "bitlib/bit-containers/bit-containers.hpp" +#include "bitlib/bit-containers/bit_bitsof.hpp" +#include "bitlib/bit-iterator/bit_iterator.hpp" #include "test_utils.hpp" // Third-party libraries #include "gtest/gtest.h" // Miscellaneous // ========================================================================== // - //TODO tests need a lot of cleanup. We should only copy what we need from random_vec //and also refactor the vec generation to reduce duplication @@ -193,6 +193,104 @@ class DoubleRangeTest : public testing::Test { } }; TYPED_TEST_SUITE(DoubleRangeTest, BaseTypes); +template +class MixedDoubleRangeTest : public testing::Test { + protected: + using FromWordType = typename FromToPair::first_type; + using ToWordType = typename FromToPair::second_type; + + static constexpr size_t digits = bit::binary_digits::value; + static constexpr size_t word_size = 4; + static constexpr size_t bit_size = word_size * digits; + static constexpr size_t big_size = 64 * 64 * 2; + + // test data + std::vector> random_bitvecs1; + std::vector> random_bitvecs2; + std::vector> random_boolvecs1, random_boolvecs2; + + void SetUp() override { + // templated lambda to fill any bit-vector / bool-vector pair + auto fill = [&]( + std::vector>& bitvecs, + std::vector>& boolvecs, + std::vector const& small_words, + std::vector const& big_words) { + const size_t digits = bit::bitsof(); + const size_t num_small_words = ((word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(W)); + const size_t num_big_words = ((big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(W)); + const size_t small_bit_size = num_small_words * digits; + const size_t big_bit_size = num_big_words * digits; + assert(small_words.size() == num_small_words); + assert(big_words.size() == num_big_words); + // small-size runs + for (size_t cont_size = 1; cont_size < small_bit_size; ++cont_size) { + bit::bit_vector bv(small_bit_size); + std::copy(small_words.begin(), small_words.end(), bv.begin().base()); + bv.resize(cont_size); + bitvecs.push_back(bv); + boolvecs.push_back(boolvec_from_bitvec(bv)); + } + // “big” runs around (big_size-1)*digits +/-4 + const auto max_digits = std::max(bit::bitsof(), bit::bitsof()); + for (int i = -4; i < 4; ++i) { + size_t cont_size = big_bit_size - max_digits + i; + bit::bit_vector bv(big_bit_size); + std::copy(big_words.begin(), big_words.end(), + bv.begin().base()); + bv.resize(cont_size); + bitvecs.push_back(bv); + boolvecs.push_back(boolvec_from_bitvec(bv)); + } + }; + + // — fill for FromWordType — + const auto small_words1 = (word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(FromWordType); + const auto big_words1 = (big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(FromWordType); + auto small1 = get_random_vec(small_words1); + auto big1 = get_random_vec(big_words1); + fill.template operator()( + random_bitvecs1, + random_boolvecs1, + small1, big1); + + // — fill for ToWordType — + const auto small_words2 = (word_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(ToWordType); + const auto big_words2 = (big_size * std::max(sizeof(FromWordType), sizeof(ToWordType))) / sizeof(ToWordType); + auto small2 = get_random_vec(small_words2); + auto big2 = get_random_vec(big_words2); + fill.template operator()( + random_bitvecs2, + random_boolvecs2, + small2, big2); + + // now random_bitvecs1.size() == random_bitvecs2.size() + } +}; + +// ----------------------------------------------------------------------------- +// Instantiate for the pairs you care about +// ----------------------------------------------------------------------------- +using TypePairs = ::testing::Types< + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair>; + +TYPED_TEST_SUITE(MixedDoubleRangeTest, TypePairs); + // ========================================================================== // #endif // _FIXTURES_HPP_INCLUDED // ========================================================================== // diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp index 578ce026..d7b60752 100644 --- a/test/src/test-array.cpp +++ b/test/src/test-array.cpp @@ -37,24 +37,24 @@ TEST(ArrayTest, BitsOf) { } TEST(ArrayTest, BasicIteration) { - // <-- LSB, apparently 🙄 - bit::bit_array barr("0110_0101_110"); - int i = 0; - for (const auto& bbit : barr) { - switch (10 - i++) { - case 0: EXPECT_EQ(bit::bit0, bbit); break; - case 1: EXPECT_EQ(bit::bit1, bbit); break; - case 2: EXPECT_EQ(bit::bit1, bbit); break; - case 3: EXPECT_EQ(bit::bit1, bbit); break; - case 4: EXPECT_EQ(bit::bit0, bbit); break; - case 5: EXPECT_EQ(bit::bit1, bbit); break; - case 6: EXPECT_EQ(bit::bit0, bbit); break; - case 7: EXPECT_EQ(bit::bit0, bbit); break; - case 8: EXPECT_EQ(bit::bit1, bbit); break; - case 9: EXPECT_EQ(bit::bit1, bbit); break; - case 10: EXPECT_EQ(bit::bit0, bbit); break; - } - } + // <-- LSB, apparently 🙄 + bit::bit_array barr("0110_0101_110"); + int i = 0; + for (const auto& bbit : barr) { + switch (10 - i++) { + case 0: EXPECT_EQ(bit::bit0, bbit); break; + case 1: EXPECT_EQ(bit::bit1, bbit); break; + case 2: EXPECT_EQ(bit::bit1, bbit); break; + case 3: EXPECT_EQ(bit::bit1, bbit); break; + case 4: EXPECT_EQ(bit::bit0, bbit); break; + case 5: EXPECT_EQ(bit::bit1, bbit); break; + case 6: EXPECT_EQ(bit::bit0, bbit); break; + case 7: EXPECT_EQ(bit::bit0, bbit); break; + case 8: EXPECT_EQ(bit::bit1, bbit); break; + case 9: EXPECT_EQ(bit::bit1, bbit); break; + case 10: EXPECT_EQ(bit::bit0, bbit); break; + } + } } TEST(ArrayTest, ZeroSize) { @@ -314,11 +314,11 @@ TEST(BitArrayDynamicTest, InitializerListWordTypeConstructorWorks) { // For this test, we assume that the initializer list for WordType initializes the underlying storage. // Here we use two bytes as an example. std::initializer_list init = {0b10101010, 0b01010101}; - bit::bit_array<> arr(init); + bit::bit_array arr(init); // Assuming each std::uint8_t provides 8 bits, we expect the size to be the number of initializer elements * 8. EXPECT_EQ(arr.size(), init.size() * 8u); // Check that the underlying storage matches the initializer values. - const std::uint8_t* data = arr.data(); + const std::uint8_t* data = reinterpret_cast(arr.data()); auto wordIt = init.begin(); for (std::size_t i = 0; i < init.size(); ++i) { EXPECT_EQ(data[i], *(wordIt++)); @@ -443,7 +443,7 @@ TEST(BitArrayDynamicTest, StructOfBitArray) { } TEST(BitArrayDynamicTest, StringConstructor) { - bit::bit_array<> arr{"01001101"}; + bit::bit_array<> arr("01001101"); EXPECT_EQ(arr.size(), 8); @@ -457,22 +457,6 @@ TEST(BitArrayDynamicTest, StringConstructor) { EXPECT_EQ(arr[0], bit::bit0); } -TEST(BitArrayDynamicTest, TwoDBitArraySizeValueConstructor) { - bit::bit_array> arr( - 16, - bit::bit_array<>(4, bit::bit1)); - EXPECT_EQ(arr.size(), 16); - EXPECT_EQ(arr[0].size(), 4); -} - -TEST(BitArrayDynamicTest, TwoDBitArraySizeConstructor) { - bit::bit_array> arr( - 16, - bit::bit_array<>(4)); - EXPECT_EQ(arr.size(), 16); - EXPECT_EQ(arr[0].size(), 4); -} - // Test comparison operators (== and !=). TEST(BitArrayDynamicTest, ComparisonOperators) { bit::bit_array ba1{bit::bit1, bit::bit1, bit::bit0, bit::bit0, bit::bit1}; diff --git a/test/src/test-array_ref.cpp b/test/src/test-array_ref.cpp index 4db45b17..fb100303 100644 --- a/test/src/test-array_ref.cpp +++ b/test/src/test-array_ref.cpp @@ -156,7 +156,7 @@ TEST(BitArrayRef, BitPointerConstructor) { original[63] = bit::bit1; // Create a bit_pointer to original's storage - bit::bit_pointer bit_ptr(original.data()); + bit::bit_pointer bit_ptr(original.data()); // Create a bit_array_ref using the bit_pointer bit::bit_array_ref<> ref(bit_ptr, original.size()); @@ -195,7 +195,7 @@ TEST(BitArrayRef, OffsetBitPointerConstructor) { original[13] = bit::bit1; // Create an offset bit_pointer (starting from the 3rd bit) - bit::bit_pointer bit_ptr(original.data(), 3); + bit::bit_pointer bit_ptr(original.data(), 3); // Create a bit_array_ref using the offset bit_pointer bit::bit_array_ref<> ref(bit_ptr, 32); diff --git a/test/src/test-copy.cpp b/test/src/test-copy.cpp index 225a0b4b..f1b3187b 100644 --- a/test/src/test-copy.cpp +++ b/test/src/test-copy.cpp @@ -1,11 +1,10 @@ // ============================= COPY TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for copy algorithms +// Description: Tests for copy algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -28,44 +27,44 @@ TYPED_TEST(DoubleRangeTest, Copy) { std::vector& boolvec1 = this->random_boolvecs1[idx]; std::vector& boolvec2 = this->random_boolvecs2[idx]; long long start1 = generate_random_number( - 0, - std::min(bitvec1.size() - 1, digits + 1)); + 0, + std::min(bitvec1.size() - 1, digits + 1)); long long start2 = generate_random_number( - 0, - std::min(bitvec2.size() - 1, digits + 1)); + 0, + std::min(bitvec2.size() - 1, digits + 1)); const auto min_range = (start2 > start1) ? start2 - start1 : 0; const auto max_range = std::max( - min_range, - std::min(digits, bitvec1.size() - start1)); + min_range, + std::min(digits, bitvec1.size() - start1)); long long end1 = generate_random_number(min_range, max_range); auto bitret = bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); auto boolret = std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ( - bit::distance(bitvec2.begin(), bitret), - std::distance(boolvec2.begin(), boolret)); + bit::distance(bitvec2.begin(), bitret), + std::distance(boolvec2.begin(), boolret)); EXPECT_TRUE(std::equal( bitvec2.begin(), bitvec2.end(), boolvec2.begin(), boolvec2.end(), comparator) ); start2 = generate_random_number(0, start1); bitret = bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec1.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec1.begin() + start2); boolret = std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec1.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec1.begin() + start2); EXPECT_EQ( - bit::distance(bitvec1.begin(), bitret), - std::distance(boolvec1.begin(), boolret)); + bit::distance(bitvec1.begin(), bitret), + std::distance(boolvec1.begin(), boolret)); EXPECT_TRUE(std::equal( bitvec1.begin(), bitvec1.end(), boolvec1.begin(), boolvec1.end(), comparator) @@ -73,4 +72,55 @@ TYPED_TEST(DoubleRangeTest, Copy) { } } +TYPED_TEST(MixedDoubleRangeTest, Copy) { + for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { + using FromWordType = typename TestFixture::FromWordType; + using ToWordType = typename TestFixture::ToWordType; + bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; + bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; + constexpr auto min_digits = std::min(bit::bitsof(), bit::bitsof()); + std::vector& boolvec1 = this->random_boolvecs1[idx]; + std::vector& boolvec2 = this->random_boolvecs2[idx]; + long long start1 = generate_random_number( + 0, + std::min(bitvec1.size() - 1, min_digits + 1)); + long long start2 = generate_random_number( + 0, + std::min(bitvec2.size() - 1, min_digits + 1)); + const auto min_range = (start2 > start1) ? start2 - start1 : 0; + const auto max_range = std::max( + min_range, + std::min(min_digits, bitvec1.size() - start1)); + long long end1 = generate_random_number(min_range, max_range); + auto bitret2 = bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + auto boolret = std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec2.begin(), bitret2), + std::distance(boolvec2.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + start2 = generate_random_number(0, start1); + auto bitret1 = bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec1.begin() + start2); + boolret = std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec1.begin() + start2); + EXPECT_EQ( + bit::distance(bitvec1.begin(), bitret1), + std::distance(boolvec1.begin(), boolret)); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + } +} diff --git a/test/src/test-equal.cpp b/test/src/test-equal.cpp index f8b09383..2a842aa5 100644 --- a/test/src/test-equal.cpp +++ b/test/src/test-equal.cpp @@ -1,11 +1,10 @@ // ============================= EQUAL TESTS =============================== // // Project: The Experimental Bit Algorithms Library -// Description: Tests for equal algorithms +// Description: Tests for equal algorithms // Contributor(s): Bryce Kille // License: BSD 3-Clause License // ========================================================================== // - // ============================== PREAMBLE ================================== // // C++ standard library #include @@ -28,46 +27,46 @@ TYPED_TEST(DoubleRangeTest, Equal) { std::vector& boolvec1 = this->random_boolvecs1[idx]; std::vector& boolvec2 = this->random_boolvecs2[idx]; long long start1 = generate_random_number( - 0, - std::min(bitvec1.size() - 1, digits + 1)); + 0, + std::min(bitvec1.size() - 1, digits + 1)); long long start2 = generate_random_number( - 0, - std::min(bitvec2.size() - 1, digits + 1)); + 0, + std::min(bitvec2.size() - 1, digits + 1)); const auto min_range = (start2 > start1) ? start2 - start1 : 0; const auto max_range = std::max( - min_range, - std::min(digits, bitvec1.size() - start1)); + min_range, + std::min(digits, bitvec1.size() - start1)); long long end1 = generate_random_number(min_range, max_range); auto bitret = bit::equal( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); auto boolret = std::equal( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ(boolret, bitret); EXPECT_TRUE(std::equal( bitvec2.begin(), bitvec2.end(), boolvec2.begin(), boolvec2.end(), comparator) ); bit::copy( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); std::copy( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); bitret = bit::equal( - bitvec1.begin() + start1, - bitvec1.end() - end1, - bitvec2.begin() + start2); + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); boolret = std::equal( - boolvec1.begin() + start1, - boolvec1.end() - end1, - boolvec2.begin() + start2); + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); EXPECT_EQ(boolret, bitret); EXPECT_TRUE(std::equal( bitvec1.begin(), bitvec1.end(), @@ -76,4 +75,64 @@ TYPED_TEST(DoubleRangeTest, Equal) { } } +TYPED_TEST(MixedDoubleRangeTest, Equal) { + for (size_t idx = 0; idx < this->random_bitvecs1.size(); ++idx) { + using FromWordType = typename TestFixture::FromWordType; + using ToWordType = typename TestFixture::ToWordType; + bit::bit_vector& bitvec1 = this->random_bitvecs1[idx]; + bit::bit_vector& bitvec2 = this->random_bitvecs2[idx]; + constexpr auto min_digits = std::min(bit::bitsof(), bit::bitsof()); + std::vector& boolvec1 = this->random_boolvecs1[idx]; + std::vector& boolvec2 = this->random_boolvecs2[idx]; + long long start1 = generate_random_number( + 0, + std::min(bitvec1.size() - 1, min_digits + 1)); + long long start2 = generate_random_number( + 0, + std::min(bitvec2.size() - 1, min_digits + 1)); + const auto min_range = (start2 > start1) ? start2 - start1 : 0; + const auto max_range = std::max( + min_range, + std::min(min_digits, bitvec1.size() - start1)); + long long end1 = generate_random_number(min_range, max_range); + auto bitret = bit::equal( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + auto boolret = std::equal( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ(boolret, bitret); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + bit::copy( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + std::copy( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_TRUE(std::equal( + bitvec2.begin(), bitvec2.end(), + boolvec2.begin(), boolvec2.end(), comparator)); + bitret = bit::equal( + bitvec1.begin() + start1, + bitvec1.end() - end1, + bitvec2.begin() + start2); + boolret = std::equal( + boolvec1.begin() + start1, + boolvec1.end() - end1, + boolvec2.begin() + start2); + EXPECT_EQ(boolret, bitret); + EXPECT_TRUE(std::equal( + bitvec1.begin(), bitvec1.end(), + boolvec1.begin(), boolvec1.end(), comparator)); + } +} diff --git a/test/src/test-iterator_adapter.cpp b/test/src/test-iterator_adapter.cpp new file mode 100644 index 00000000..f8be23d5 --- /dev/null +++ b/test/src/test-iterator_adapter.cpp @@ -0,0 +1,333 @@ +// ============================= TRANSFORM TESTS =============================== // +// Project: The Experimental Bit Algorithms Library +// Description: Tests for transform algorithms +// Contributor(s): Bryce Kille +// License: BSD 3-Clause License +// ========================================================================== // + + +// ============================== PREAMBLE ================================== // +// C++ standard library +#include +#include +#include +// Project sources +#include "bitlib/bit-algorithms/bit_algorithm.hpp" +#include "bitlib/bit-containers/bit-containers.hpp" +#include "fixtures.hpp" +// Third-party libraries +#include "gtest/gtest.h" +// Miscellaneous +// ========================================================================== // +#include "bitlib/bit-iterator/bit_word_pointer_adapter.hpp" + +// +// GoogleTest suite for bit::bit_word_pointer_adapter. +// Covers both “big‐to‐small” and “small‐to‐large” modes. +// + +// Assumes a little‐endian platform (so that reinterpret_cast<…> of bytes +// within larger words behaves predictably). If you compile on a big‐endian +// machine, these tests will fail. +// +// Verify that we are on a little‐endian machine: +static_assert(std::endian::native == std::endian::little, + "These tests assume little‐endian byte order"); + +TEST(IteratorAdapter, Basic) { + std::vector base{0x1234, 0x2345, 0x3456, 0x4567}; + bit::bit_word_pointer_adapter::iterator> adapter(base.begin()); + EXPECT_EQ(*adapter, 0x34); + EXPECT_EQ(*(++adapter), 0x12); + EXPECT_EQ(*(adapter++), 0x12); + EXPECT_EQ(*adapter, 0x45); + adapter += 2; + EXPECT_EQ(*adapter, 0x56); +} + +// ----------------------------------------------------------------------------- +// Helper: Convert a 32‐bit word into its 4 individual bytes (little‐endian) +// ----------------------------------------------------------------------------- +static void split_u32_le(uint32_t value, uint8_t out_bytes[4]) { + out_bytes[0] = static_cast(value & 0xFFu); + out_bytes[1] = static_cast((value >> 8) & 0xFFu); + out_bytes[2] = static_cast((value >> 16) & 0xFFu); + out_bytes[3] = static_cast((value >> 24) & 0xFFu); +} + +// ----------------------------------------------------------------------------- +// TEST SUITE: Big-to-Small Mode (BaseIterator.value_type > Iterator.value_type) +// ----------------------------------------------------------------------------- +TEST(BitIteratorAdapter_BigToSmall, DereferenceAndIncrement) { + // Base‐words are 32-bit => 4 × 8-bit subwords + constexpr size_t N = 2; + uint32_t base_arr[N] = {0x04030201u, 0x08070605u}; + // Bytes in memory (little‐endian): + // base_arr[0]: {0x01,0x02,0x03,0x04} + // base_arr[1]: {0x05,0x06,0x07,0x08} + + // Iterator type: iterate in 8-bit steps over a 32-bit array + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(base_arr, /*pos=*/0); + + // Check that *it sees the first byte = 0x01 + EXPECT_EQ(*it, 0x01u); + + // Advance 1: pos=1, still in base_arr[0] + ++it; + EXPECT_EQ(it.index(), 1u); + EXPECT_EQ(*it, 0x02u); + + // Advance to pos=3 + it += 2; + EXPECT_EQ(it.index(), 3u); + EXPECT_EQ(*it, 0x04u); + + // Next increment should roll over to base_arr[1], pos=0 + ++it; + EXPECT_EQ(it.base(), base_arr + 1); + EXPECT_EQ(it.index(), 0u); + EXPECT_EQ(*it, 0x05u); + + // Further increments + ++it; // pos=1 + EXPECT_EQ(it.index(), 1u); + EXPECT_EQ(*it, 0x06u); + + ++it; // pos=2 + EXPECT_EQ(it.index(), 2u); + EXPECT_EQ(*it, 0x07u); + + ++it; // pos=3 + EXPECT_EQ(it.index(), 3u); + EXPECT_EQ(*it, 0x08u); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorStarAndArrow) { + // Use a single base word = 0xAABBCCDD + uint32_t base_val = 0xAABBCCDDu; + // Memory bytes: { DD, CC, BB, AA } little-endian + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(&base_val, /*pos=*/0); + + EXPECT_EQ(*it, 0xDDu); + // operator->() should return a pointer to the byte + auto p = *it; + EXPECT_EQ(p, 0xDDu); + + // Move to pos=2 + it += 2; + EXPECT_EQ(*it, 0xBBu); + EXPECT_EQ(it.index(), 2u); + + // operator->() at pos=2 + p = *it; + EXPECT_EQ(p, 0xBBu); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorPlusMinusAndDistance) { + // Fill two 32-bit words + uint32_t base_arr[2] = {0x11223344u, 0x55667788u}; + // Bytes: {44,33,22,11, 88,77,66,55} + + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(base_arr, /*pos=*/0); // points at first byte (0x44) + + // it + 3 => should point to 4th byte of base_arr[0] = 0x11 + auto it3 = it + 3; + EXPECT_EQ(it3.base(), base_arr); + EXPECT_EQ(it3.index(), 3u); + EXPECT_EQ(*it3, 0x11u); + + // it + 4 => moves into base_arr[1], pos=0 => should be 0x88 + auto it4 = it + 4; + EXPECT_EQ(it4.base(), base_arr + 1); + EXPECT_EQ(it4.index(), 0u); + EXPECT_EQ(*it4, 0x88u); + + // Now distance between it and it+7 = 7 + auto it7 = it + 7; + EXPECT_EQ(it7.base(), base_arr + 1); + EXPECT_EQ(it7.index(), 3u); + EXPECT_EQ(*it7, 0x55u); + + EXPECT_EQ(it7 - it, 7); + EXPECT_EQ(it - it, 0); + + // Test operator- (iterator difference) with reversed operands + EXPECT_EQ((it - it7), -7); + + // Advance it4 by -2 => (it4 - 2) = it + 2 => base_arr[0], pos=2 => 0x22 + auto it2 = it4 - 2; + EXPECT_EQ(it2.base(), base_arr); + EXPECT_EQ(it2.index(), 2u); + EXPECT_EQ(*it2, 0x22u); +} + +TEST(BitIteratorAdapter_BigToSmall, OperatorSubscript) { + // 1 uint32_t: 0xDEADBEEFu => bytes { EF, BE, AD, DE } + uint32_t base_val = 0xDEADBEEFu; + using Iter8 = uint8_t*; + using Base32 = uint32_t*; + using Adapter8_32 = bit::bit_word_pointer_adapter; + + Adapter8_32 it(&base_val, /*pos=*/0); + // it[0] == 0xEF, it[1] == 0xBE, it[2] == 0xAD, it[3] == 0xDE + EXPECT_EQ(it[0], static_cast(0xEFu)); + EXPECT_EQ(it[1], static_cast(0xBEu)); + EXPECT_EQ(it[2], static_cast(0xADu)); + EXPECT_EQ(it[3], static_cast(0xDEu)); + + // Negative index: it[ -1 ] => moves to previous base word; + // but since there's no previous word, behavior is UB. We skip testing negative here. + + // If we do it + 4, then subscript( it + 4 )[0] should be same as it[4] + auto it_shift = it + 4; // rolls into next base (out-of-bounds, but for test allocate two words) + // For safety, create two‐element array: + uint32_t arr2[2] = {0x01020304u, 0x05060708u}; + Adapter8_32 it2(arr2, 0); + EXPECT_EQ(it2[0], 0x04u); + EXPECT_EQ(it2[1], 0x03u); + EXPECT_EQ(it2[2], 0x02u); + EXPECT_EQ(it2[3], 0x01u); + EXPECT_EQ(it2[4], 0x08u); + EXPECT_EQ(it2[5], 0x07u); + EXPECT_EQ(it2[6], 0x06u); + EXPECT_EQ(it2[7], 0x05u); +} + +// ----------------------------------------------------------------------------- +// TEST SUITE: Small-to-Large Mode (BaseIterator.value_type < Iterator.value_type) +// ----------------------------------------------------------------------------- +TEST(BitIteratorAdapter_SmallToLarge, DereferenceAndIncrement) { + // Base: array of uint8_t; Iterator’s value_type = uint16_t => ratio = 2 + uint8_t base_arr[] = { + 0x11, 0x22, // first 16-bit chunk => 0x2211 + 0x33, 0x44, // second 16-bit chunk => 0x4433 + 0x55, 0x66 // third 16-bit chunk => 0x6655 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // *it: combine base[0] + (base[1] << 8) => 0x2211 + uint16_t first = *it; + EXPECT_EQ(first, static_cast(0x2211u)); + + // ++it => skip 2 bytes: now base points to base_arr+2 + ++it; + uint16_t second = *it; + EXPECT_EQ(second, static_cast(0x4433u)); + + // ++it again + ++it; + uint16_t third = *it; + EXPECT_EQ(third, static_cast(0x6655u)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorPlusMinusDistance) { + // Base: eight uint8_t => four uint16_t logical elements + uint8_t base_arr[8] = { + 0x10, 0x01, // 0x0110 + 0x20, 0x02, // 0x0220 + 0x30, 0x03, // 0x0330 + 0x40, 0x04 // 0x0440 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // it + 0 => *it == 0x0110 + EXPECT_EQ(*it, static_cast(0x0110u)); + + // it + 1 => combine bytes at indices [2,3] => 0x0220 + auto it1 = it + 1; + EXPECT_EQ(*it1, static_cast(0x0220u)); + + // it + 2 => 0x0330 + auto it2 = it + 2; + EXPECT_EQ(*it2, static_cast(0x0330u)); + + // it + 3 => 0x0440 + auto it3 = it + 3; + EXPECT_EQ(*it3, static_cast(0x0440u)); + + // Distance: it3 - it == 3 + EXPECT_EQ(it3 - it, 3); + EXPECT_EQ(it - it, 0); + + // it2 - it1 == 1 + EXPECT_EQ(it2 - it1, 1); + + // Subtraction reversed: + EXPECT_EQ(it - it3, -3); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorStarAndArrow) { + uint8_t base_arr[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + // *it => (0xBB << 8) | 0xAA = 0xBBAA + EXPECT_EQ(*it, static_cast(0xBBAAu)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorPlusEqualsAndMinusEquals) { + // Base: six uint8_t => three 16-bit words + uint8_t base_arr[6] = { + 0x01, 0xA0, // 0xA001 + 0x02, 0xB0, // 0xB002 + 0x03, 0xC0 // 0xC003 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it(base_arr); + + // it += 1 => now at second 16-bit chunk: 0xB002 + it += 1; + EXPECT_EQ(*it, static_cast(0xB002u)); + + // it += 1 => now at third: 0xC003 + it += 1; + EXPECT_EQ(*it, static_cast(0xC003u)); + + // it -= 2 => go back to first chunk: 0xA001 + it -= 2; + EXPECT_EQ(*it, static_cast(0xA001u)); +} + +TEST(BitIteratorAdapter_SmallToLarge, OperatorMinusBetweenDifferentIterators) { + // Base: eight uint8_t => four 16-bit words + uint8_t base_arr[8] = { + 0x01, 0x10, // 0x1001 + 0x02, 0x20, // 0x2002 + 0x03, 0x30, // 0x3003 + 0x04, 0x40 // 0x4004 + }; + using Iter16 = uint16_t*; + using Base8 = uint8_t*; + using Adapter16_8 = bit::bit_word_pointer_adapter; + + Adapter16_8 it0(base_arr); + Adapter16_8 it2(base_arr + 4); // same as (it0 + 2) + + EXPECT_EQ(it2 - it0, 2); + EXPECT_EQ(it0 - it2, -2); +} diff --git a/test/src/test-mdspan.cpp b/test/src/test-mdspan.cpp index 6e95ab7d..78729830 100644 --- a/test/src/test-mdspan.cpp +++ b/test/src/test-mdspan.cpp @@ -26,7 +26,7 @@ TEST(MdSpanTest, BitDefaultLayout) { bit::bit_value, std::dextents, std::layout_right, - bit::bit_default_accessor > + bit::bit_default_accessor > myspan(&dynarr[0], 5, 6, 7); for (size_t i = 0; i < myspan.extent(0); i++) {