Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dbb09e7
Remove alignment. Expose algorithm issues with differing types
PeterCDMcLean May 25, 2025
fe1e01c
Add iterator adapter class (big to small)
PeterCDMcLean May 29, 2025
ee244bd
Use new iterator adapter. Set more default words to uintptr_t
PeterCDMcLean May 30, 2025
fdf8756
Add dispatch to workflow trigger options
PeterCDMcLean Jun 1, 2025
d36806d
Dummy change
PeterCDMcLean Jun 1, 2025
4e9ec3c
Try rerranging
PeterCDMcLean Jun 1, 2025
0194c16
Remove distinct iterator adapter test target
PeterCDMcLean Jun 1, 2025
6140dbf
Try GLIBCXX_RELEASE macro
PeterCDMcLean Jun 1, 2025
b85ad74
Fix type for test
PeterCDMcLean Jun 1, 2025
8d70aac
bit_reference now templated on ref type to allow for it to wrap proxy…
PeterCDMcLean Jun 2, 2025
e92bd04
Now with proxy reference and proxy pointer
PeterCDMcLean Jun 5, 2025
04ac760
Fix some ops
PeterCDMcLean Jun 5, 2025
63b4863
Remove wider_t narrower_t
PeterCDMcLean Jun 9, 2025
7a4e981
Small buffer optimized dynamic bit array
PeterCDMcLean Jun 9, 2025
ba72a99
Clean up more of bit_details
PeterCDMcLean Jun 9, 2025
d781487
small improvement to bitsof
PeterCDMcLean Jun 9, 2025
9518b66
Don't measure coverage on tests (too error prone)
PeterCDMcLean Jun 9, 2025
bbb531a
Adapter prelude wip
PeterCDMcLean Jun 9, 2025
c4534fd
Add test for mixed type equals
PeterCDMcLean Jun 11, 2025
00e6a40
Adapted copy
PeterCDMcLean Jun 12, 2025
99c7240
Test adapted copy
PeterCDMcLean Jun 12, 2025
69bc51a
Fix conversion from adapter back to iterator
PeterCDMcLean Jun 12, 2025
a82bc64
Remove unnecessary return type template parameter
PeterCDMcLean Jun 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ignore:
- "**/libpopcnt.h"
- "test/inc/*"
- "test/**"
- "benchmark/**"
- "example/**"
2 changes: 2 additions & 0 deletions .github/workflows/cmake-multi-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
name: CMake on multiple platforms

on:
workflow_dispatch:
push:
branches: [ "master" ]
paths-ignore:
Expand All @@ -12,6 +13,7 @@ on:
paths-ignore:
- '**/*.md'


jobs:
build:
runs-on: ${{ matrix.distro }}
Expand Down
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <string>
#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")
Expand Down
8 changes: 4 additions & 4 deletions include/bitlib/bit-algorithms/bit_algorithm_details.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <class T, class InputIt>
T get_word(bit_iterator<InputIt> first, size_t len=binary_digits<T>::value)
{
T get_word(const bit_iterator<InputIt>& first, size_t len = binary_digits<T>::value) {
using native_word_type = typename bit_iterator<InputIt>::word_type;
constexpr T digits = binary_digits<native_word_type>::value;
assert(digits >= len);
using non_const_T = std::remove_cv_t<T>;
using non_const_T = std::remove_cvref_t<T>;
non_const_T offset = digits - first.position();
non_const_T ret_word = lsr(*first.base(), first.position());

Expand Down Expand Up @@ -224,7 +224,7 @@ void write_word(src_type src, bit_iterator<OutputIt> dst_bit_it,
} else {
*dst_bit_it.base() = _bitblend<src_type>(
*dst_bit_it.base(),
src << dst_bit_it.position(),
static_cast<src_type>(src << dst_bit_it.position()),
dst_bit_it.position(),
std::min<src_type>(
dst_digits - dst_bit_it.position(),
Expand Down
74 changes: 37 additions & 37 deletions include/bitlib/bit-algorithms/copy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,64 +22,63 @@
namespace bit {
// ========================================================================== //

struct copy_impl;

// Status: Does not work for Input/Output iterators due to distance call
template <typename RandomAccessIt1, typename RandomAccessIt2>
constexpr bit_iterator<RandomAccessIt2> copy(
const bit_iterator<RandomAccessIt1>& first,
const bit_iterator<RandomAccessIt1>& last,
const bit_iterator<RandomAccessIt2>& d_first) {
return with_bit_iterator_adapter<copy_impl>(first, last, d_first);
}

// ---------------------------- Copy Algorithms ----------------------------- //

// Status: Does not work for Input/Output iterators due to distance call
template <class RandomAccessIt1, class RandomAccessIt2>
constexpr bit_iterator<RandomAccessIt2> copy(bit_iterator<RandomAccessIt1> first,
bit_iterator<RandomAccessIt1> last,
bit_iterator<RandomAccessIt2> d_first
)
{
struct copy_impl {
// Status: Does not work for Input/Output iterators due to distance call
template <typename RandomAccessIt1, typename RandomAccessIt2>
constexpr bit_iterator<RandomAccessIt2> operator()(
bit_iterator<RandomAccessIt1> first,
bit_iterator<RandomAccessIt1> last,
bit_iterator<RandomAccessIt2> d_first) {
// Types and constants
using dst_word_type = typename bit_iterator<RandomAccessIt2>::word_type;
using src_word_type = typename bit_iterator<RandomAccessIt1>::word_type;

// This checks for differing word types and uses an unoptimized copy in that event
if (!::std::is_same<dst_word_type, src_word_type>::value) {
while (first != last) {
*d_first = *first;
first++;
d_first++;
}
return d_first;
}
static_assert(std::is_same<dst_word_type, src_word_type>::value, "Both types must be the same");

using word_type = dst_word_type;
using size_type = typename bit_iterator<RandomAccessIt2>::size_type;
constexpr size_type digits = binary_digits<word_type>::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;
size_type total_bits_to_copy = distance(first, last);
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<word_type>(
get_word<word_type>(first, partial_bits_to_copy)
<< static_cast<word_type>(d_first.position())
),
static_cast<word_type>(d_first.position()),
static_cast<word_type>(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<word_type>(
get_word<word_type>(first, partial_bits_to_copy)
<< static_cast<word_type>(d_first.position())),
static_cast<word_type>(d_first.position()),
static_cast<word_type>(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) {
Expand Down Expand Up @@ -108,12 +107,13 @@ constexpr bit_iterator<RandomAccessIt2> copy(bit_iterator<RandomAccessIt1> first
}
}
return d_first + total_bits_to_copy;
}
}
};
// -------------------------------------------------------------------------- //



// ========================================================================== //
} // namespace bit
} // namespace bit
#endif // _COPY_HPP_INCLUDED
// ========================================================================== //
28 changes: 13 additions & 15 deletions include/bitlib/bit-algorithms/copy_backward.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,12 @@ constexpr bit_iterator<RandomAccessIt2> copy_backward(bit_iterator<RandomAccessI
d_last.position()
);
*it = _bitblend<word_type>(
*it,
static_cast<word_type>(
get_word<word_type>(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<word_type>(partial_bits_to_copy)
);
*it,
static_cast<word_type>(
get_word<word_type>(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<word_type>(partial_bits_to_copy));
remaining_bits_to_copy -= partial_bits_to_copy;
advance(last, -partial_bits_to_copy);
}
Expand All @@ -87,14 +86,13 @@ constexpr bit_iterator<RandomAccessIt2> copy_backward(bit_iterator<RandomAccessI
it--;
}
if (remaining_bits_to_copy > 0) {
*it = _bitblend<word_type>(
*it,
get_word<word_type>(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<word_type>(
*it,
static_cast<word_type>(get_word<word_type>(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;
Expand Down
105 changes: 60 additions & 45 deletions include/bitlib/bit-algorithms/equal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <type_traits>
Expand All @@ -20,30 +19,39 @@
// Miscellaneous
namespace bit {
// ========================================================================== //
struct equal_impl;


// Status: Does not work for Input/Output iterators due to distance call
template <typename RandomAccessIt1, typename RandomAccessIt2>
constexpr bool equal(
const bit_iterator<RandomAccessIt1>& first,
const bit_iterator<RandomAccessIt1>& last,
const bit_iterator<RandomAccessIt2>& d_first) {
return with_bit_iterator_adapter<equal_impl>(first, last, d_first);
}

// ---------------------------- Equal Algorithms ----------------------------- //

// Status: Does not work for Input/Output iterators due to distance call
template <class RandomAccessIt1, class RandomAccessIt2>
constexpr bool equal(
bit_iterator<RandomAccessIt1> first,
bit_iterator<RandomAccessIt1> last,
bit_iterator<RandomAccessIt2> d_first
)
{
struct equal_impl {
template <typename RandomAccessIt1, typename RandomAccessIt2>
constexpr bool operator()(
bit_iterator<RandomAccessIt1> first,
bit_iterator<RandomAccessIt1> last,
bit_iterator<RandomAccessIt2> d_first) {
// Types and constants
using dst_word_type = typename bit_iterator<RandomAccessIt2>::word_type;
using src_word_type = typename bit_iterator<RandomAccessIt1>::word_type;
static_assert(::std::is_same<dst_word_type, src_word_type>::value, "Underlying word types must be equal");
using word_type = dst_word_type;
using size_type = typename bit_iterator<RandomAccessIt2>::size_type;
constexpr size_type digits = binary_digits<word_type>::value;

// Assertions
_assert_range_viability(first, last);
static_assert(::std::is_same<dst_word_type, src_word_type>::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;
Expand All @@ -53,48 +61,55 @@

// 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<word_type>(partial_bits_to_check) << d_first.position();
const word_type comp = static_cast<word_type>(
get_word<word_type>(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<word_type>(partial_bits_to_check) << d_first.position();
const word_type comp = static_cast<word_type>(
get_word<word_type>(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<word_type>(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<word_type>(remaining_bits_to_check);
const word_type comp = get_word<word_type>(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<word_type>(first, digits)) {
return false;
}
remaining_bits_to_check -= digits;
it++;
advance(first, digits);
}

Check warning on line 100 in include/bitlib/bit-algorithms/equal.hpp

View check run for this annotation

Codecov / codecov/patch

include/bitlib/bit-algorithms/equal.hpp#L97-L100

Added lines #L97 - L100 were not covered by tests
}
if (remaining_bits_to_check > 0) {
const word_type mask = _mask<word_type>(remaining_bits_to_check);
const word_type comp = get_word<word_type>(first, remaining_bits_to_check);
if ((mask & *it) != (mask & comp)) {
return false;
}
}
}
return true;
}
}
};
// -------------------------------------------------------------------------- //


Expand Down
Loading
Loading