From 24c5bef0380088fda64ddd4f985ba43e9e55615a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 20:53:21 +0100 Subject: [PATCH 01/24] initial span --- source/blender/blenlib/BLI_bit_vector.hh | 238 +++++++++++++++++------ 1 file changed, 174 insertions(+), 64 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 237bdce1452..a8d525ba097 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -174,6 +174,172 @@ class MutableBitRef { } }; +class BitIterator { + private: + const IntType *data_; + int64_t bit_index_; + + public: + BitIterator(const IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + { + } + + BitIterator &operator++() + { + bit_index_++; + return *this; + } + + BitRef operator*() const + { + return BitRef(data_, bit_index_); + } + + friend bool operator!=(const BitIterator &a, const BitIterator &b) + { + BLI_assert(a.data_ == b.data_); + return a.bit_index_ != b.bit_index_; + } +}; + +class MutableBitIterator { + private: + IntType *data_; + int64_t bit_index_; + + public: + MutableBitIterator(IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + { + } + + MutableBitIterator &operator++() + { + bit_index_++; + return *this; + } + + MutableBitRef operator*() const + { + return MutableBitRef(data_, bit_index_); + } + + friend bool operator!=(const MutableBitIterator &a, const MutableBitIterator &b) + { + BLI_assert(a.data_ == b.data_); + return a.bit_index_ != b.bit_index_; + } +}; + +class BitSpan { + private: + const IntType *data_ = nullptr; + IndexRange bit_range_ = {0, 0}; + + public: + BitSpan() = default; + + BitSpan(const IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + BitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return BitRef(data_, bit_range_.start() + index); + } + + BitSpan slice(const IndexRange range) const + { + return BitSpan(data_, bit_range_.slice(range)); + } + + const IntType *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + BitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + BitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } +}; + +class MutableBitSpan { + private: + IntType *data_ = nullptr; + IndexRange bit_range_ = {0, 0}; + + public: + MutableBitSpan() = default; + + MutableBitSpan(IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + MutableBitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return MutableBitRef(data_, bit_range_.start() + index); + } + + MutableBitSpan slice(const IndexRange range) const + { + return MutableBitSpan(data_, bit_range_.slice(range)); + } + + IntType *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + MutableBitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + MutableBitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } +}; + template< /** * Number of bits that can be stored in the vector without doing an allocation. @@ -352,80 +518,24 @@ class BitVector { size_in_bits_++; } - class Iterator { - private: - const BitVector *vector_; - int64_t index_; - - public: - Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index) - { - } - - Iterator &operator++() - { - index_++; - return *this; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) - { - BLI_assert(a.vector_ == b.vector_); - return a.index_ != b.index_; - } - - BitRef operator*() const - { - return (*vector_)[index_]; - } - }; - - class MutableIterator { - private: - BitVector *vector_; - int64_t index_; - - public: - MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index) - { - } - - MutableIterator &operator++() - { - index_++; - return *this; - } - - friend bool operator!=(const MutableIterator &a, const MutableIterator &b) - { - BLI_assert(a.vector_ == b.vector_); - return a.index_ != b.index_; - } - - MutableBitRef operator*() const - { - return (*vector_)[index_]; - } - }; - - Iterator begin() const + BitIterator begin() const { - return {*this, 0}; + return {data_, 0}; } - Iterator end() const + BitIterator end() const { - return {*this, size_in_bits_}; + return {data_, size_in_bits_}; } - MutableIterator begin() + MutableBitIterator begin() { - return {*this, 0}; + return {data_, 0}; } - MutableIterator end() + MutableBitIterator end() { - return {*this, size_in_bits_}; + return {data_, size_in_bits_}; } /** -- 2.30.2 From d7ec27e3ed0dfba004fe1a0b49a33ab16024a086 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 20:54:27 +0100 Subject: [PATCH 02/24] progress --- source/blender/blenlib/BLI_bit_vector.hh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index a8d525ba097..0e786ea296f 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -256,12 +256,12 @@ class BitSpan { { BLI_assert(index >= 0); BLI_assert(index < bit_range_.size()); - return BitRef(data_, bit_range_.start() + index); + return {data_, bit_range_.start() + index}; } BitSpan slice(const IndexRange range) const { - return BitSpan(data_, bit_range_.slice(range)); + return {data_, bit_range_.slice(range)}; } const IntType *data() const @@ -311,12 +311,12 @@ class MutableBitSpan { { BLI_assert(index >= 0); BLI_assert(index < bit_range_.size()); - return MutableBitRef(data_, bit_range_.start() + index); + return {data_, bit_range_.start() + index}; } MutableBitSpan slice(const IndexRange range) const { - return MutableBitSpan(data_, bit_range_.slice(range)); + return {data_, bit_range_.slice(range)}; } IntType *data() const @@ -338,6 +338,11 @@ class MutableBitSpan { { return {data_, bit_range_.one_after_last()}; } + + operator BitSpan() const + { + return {data_, bit_range_}; + } }; template< -- 2.30.2 From a84a65fe189f12ba2d7eae267ee8d1a66d7d44f3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 20:56:02 +0100 Subject: [PATCH 03/24] progress --- source/blender/blenlib/BLI_bit_vector.hh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 0e786ea296f..aee08c132af 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -474,6 +474,16 @@ class BitVector { return move_assign_container(*this, std::move(other)); } + operator BitSpan() const + { + return {data_, IndexRange(size_in_bits_)}; + } + + operator MutableBitSpan() + { + return {data_, IndexRange(size_in_bits_)}; + } + /** * Number of bits in the bit vector. */ -- 2.30.2 From 4906074b551d521c3659c0f6cc053e25cbba581b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 21:24:26 +0100 Subject: [PATCH 04/24] progress --- source/blender/blenlib/BLI_bit_vector.hh | 117 +++++++++++++++++------ 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index aee08c132af..17ab15148ca 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -52,6 +52,32 @@ static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + (sizeof(IntType) >= 8); static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; +static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 - 1); + +inline IntType mask_for_first_n_bits(const int64_t n) +{ + return (IntType(1) << n) - 1; +} + +inline IntType mask_for_last_n_bits(const int64_t n) +{ + return ~((IntType(1) << (BitsPerInt - n)) - 1); +} + +inline IntType mask_for_bit(const int64_t bit_index) +{ + return IntType(1) << bit_index; +} + +inline IntType *int_containing_bit(IntType *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} + +inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} /** * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but @@ -75,8 +101,8 @@ class BitRef { */ BitRef(const IntType *ptr, const int64_t bit_index) { - ptr_ = ptr + (bit_index >> BitToIntIndexShift); - mask_ = IntType(1) << (bit_index & BitIndexMask); + ptr_ = int_containing_bit(ptr, bit_index); + mask_ = mask_for_bit(bit_index & BitIndexMask); } /** @@ -114,8 +140,8 @@ class MutableBitRef { */ MutableBitRef(IntType *ptr, const int64_t bit_index) { - ptr_ = ptr + (bit_index >> BitToIntIndexShift); - mask_ = IntType(1) << IntType(bit_index & BitIndexMask); + ptr_ = int_containing_bit(ptr, bit_index); + mask_ = mask_for_bit(bit_index & BitIndexMask); } /** @@ -343,6 +369,58 @@ class MutableBitSpan { { return {data_, bit_range_}; } + + void set() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + IntType &first_int = *int_containing_bit(data_, bit_range_.start()); + const IntType first_int_mask = mask_for_last_n_bits(ranges.prefix.size()); + first_int |= first_int_mask; + } + { + IntType *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr IntType fill_value = IntType(-1); + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const IntType last_int_mask = mask_for_first_n_bits(ranges.suffix.size()); + last_int |= last_int_mask; + } + } + + void reset() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + IntType &first_int = *int_containing_bit(data_, bit_range_.start()); + const IntType first_int_mask = mask_for_first_n_bits(ranges.prefix.size()); + first_int &= first_int_mask; + } + { + IntType *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr IntType fill_value = 0; + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const IntType last_int_mask = mask_for_last_n_bits(ranges.suffix.size()); + last_int &= last_int_mask; + } + } + + void set(const bool value) + { + if (value) { + this->set(); + } + else { + this->reset(); + } + } }; template< @@ -566,31 +644,8 @@ class BitVector { } size_in_bits_ = new_size_in_bits; if (old_size_in_bits < new_size_in_bits) { - this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value); - } - } - - /** - * Set #value for every element in #range. - */ - void fill_range(const IndexRange range, const bool value) - { - const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerInt); - - /* Fill first few bits. */ - for (const int64_t i : aligned_ranges.prefix) { - (*this)[i].set(value); - } - - /* Fill entire ints at once. */ - const int64_t start_fill_int_index = aligned_ranges.aligned.start() / BitsPerInt; - const int64_t ints_to_fill = aligned_ranges.aligned.size() / BitsPerInt; - const IntType fill_value = value ? IntType(-1) : IntType(0); - initialized_fill_n(data_ + start_fill_int_index, ints_to_fill, fill_value); - - /* Fill bits in the end that don't cover a full int. */ - for (const int64_t i : aligned_ranges.suffix) { - (*this)[i].set(value); + MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits)) + .set(value); } } @@ -599,7 +654,7 @@ class BitVector { */ void fill(const bool value) { - this->fill_range(IndexRange(0, size_in_bits_), value); + MutableBitSpan(data_, size_in_bits_).set(value); } /** @@ -642,7 +697,7 @@ class BitVector { } BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits, - const IntType initial_value_for_new_ints = 0x00) + const IntType initial_value_for_new_ints = 0) { if (capacity_in_bits_ >= min_capacity_in_bits) { return; -- 2.30.2 From ba02d76f1ca32a7f2eb21c11ac2bb962bb7968f2 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 21:32:34 +0100 Subject: [PATCH 05/24] extract bit ref header --- source/blender/blenlib/BLI_bit_ref.hh | 170 +++++++++++++++++++++++ source/blender/blenlib/BLI_bit_vector.hh | 167 ++-------------------- 2 files changed, 179 insertions(+), 158 deletions(-) create mode 100644 source/blender/blenlib/BLI_bit_ref.hh diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh new file mode 100644 index 00000000000..233d5300a77 --- /dev/null +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_utildefines.h" + +namespace blender::bits { + +/** + * Using a large integer type is better because then it's easier to process many bits at once. + */ +using IntType = uint64_t; +static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); +static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + + (sizeof(IntType) >= 8); +static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; +static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 - 1); + +inline IntType mask_for_first_n_bits(const int64_t n) +{ + return (IntType(1) << n) - 1; +} + +inline IntType mask_for_last_n_bits(const int64_t n) +{ + return ~((IntType(1) << (BitsPerInt - n)) - 1); +} + +inline IntType mask_for_bit(const int64_t bit_index) +{ + return IntType(1) << bit_index; +} + +inline IntType *int_containing_bit(IntType *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} + +inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} + +/** + * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but + * not changed. + */ +class BitRef { + private: + /** Points to the integer that the bit is in. */ + const IntType *ptr_; + /** All zeros except for a single one at the bit that is referenced. */ + IntType mask_; + + friend class MutableBitRef; + + public: + BitRef() = default; + + /** + * Reference a specific bit in an array. Note that #ptr does *not* have to point to the + * exact integer the bit is in. + */ + BitRef(const IntType *ptr, const int64_t bit_index) + { + ptr_ = int_containing_bit(ptr, bit_index); + mask_ = mask_for_bit(bit_index & BitIndexMask); + } + + /** + * Return true when the bit is currently 1 and false otherwise. + */ + bool test() const + { + const IntType value = *ptr_; + const IntType masked_value = value & mask_; + return masked_value != 0; + } + + operator bool() const + { + return this->test(); + } +}; + +/** + * Similar to #BitRef, but also allows changing the referenced bit. + */ +class MutableBitRef { + private: + /** Points to the integer that the bit is in. */ + IntType *ptr_; + /** All zeros except for a single one at the bit that is referenced. */ + IntType mask_; + + public: + MutableBitRef() = default; + + /** + * Reference a specific bit in an array. Note that #ptr does *not* have to point to the + * exact int the bit is in. + */ + MutableBitRef(IntType *ptr, const int64_t bit_index) + { + ptr_ = int_containing_bit(ptr, bit_index); + mask_ = mask_for_bit(bit_index & BitIndexMask); + } + + /** + * Support implicitly casting to a read-only #BitRef. + */ + operator BitRef() const + { + BitRef bit_ref; + bit_ref.ptr_ = ptr_; + bit_ref.mask_ = mask_; + return bit_ref; + } + + /** + * Return true when the bit is currently 1 and false otherwise. + */ + bool test() const + { + const IntType value = *ptr_; + const IntType masked_value = value & mask_; + return masked_value != 0; + } + + operator bool() const + { + return this->test(); + } + + /** + * Change the bit to a 1. + */ + void set() + { + *ptr_ |= mask_; + } + + /** + * Change the bit to a 0. + */ + void reset() + { + *ptr_ &= ~mask_; + } + + /** + * Change the bit to a 1 if #value is true and 0 otherwise. + */ + void set(const bool value) + { + if (value) { + this->set(); + } + else { + this->reset(); + } + } +}; + +} // namespace blender::bits + +namespace blender { +using bits::BitRef; +using bits::MutableBitRef; +} // namespace blender diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 17ab15148ca..c53bf66c006 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -38,168 +38,13 @@ #include #include "BLI_allocator.hh" +#include "BLI_bit_ref.hh" #include "BLI_index_range.hh" #include "BLI_memory_utils.hh" #include "BLI_span.hh" namespace blender::bits { -/** - * Using a large integer type is better because then it's easier to process many bits at once. - */ -using IntType = uint64_t; -static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); -static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + - (sizeof(IntType) >= 8); -static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; -static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 - 1); - -inline IntType mask_for_first_n_bits(const int64_t n) -{ - return (IntType(1) << n) - 1; -} - -inline IntType mask_for_last_n_bits(const int64_t n) -{ - return ~((IntType(1) << (BitsPerInt - n)) - 1); -} - -inline IntType mask_for_bit(const int64_t bit_index) -{ - return IntType(1) << bit_index; -} - -inline IntType *int_containing_bit(IntType *data, const int64_t bit_index) -{ - return data + (bit_index >> BitToIntIndexShift); -} - -inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_index) -{ - return data + (bit_index >> BitToIntIndexShift); -} - -/** - * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but - * not changed. - */ -class BitRef { - private: - /** Points to the integer that the bit is in. */ - const IntType *ptr_; - /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; - - friend class MutableBitRef; - - public: - BitRef() = default; - - /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the - * exact integer the bit is in. - */ - BitRef(const IntType *ptr, const int64_t bit_index) - { - ptr_ = int_containing_bit(ptr, bit_index); - mask_ = mask_for_bit(bit_index & BitIndexMask); - } - - /** - * Return true when the bit is currently 1 and false otherwise. - */ - bool test() const - { - const IntType value = *ptr_; - const IntType masked_value = value & mask_; - return masked_value != 0; - } - - operator bool() const - { - return this->test(); - } -}; - -/** - * Similar to #BitRef, but also allows changing the referenced bit. - */ -class MutableBitRef { - private: - /** Points to the integer that the bit is in. */ - IntType *ptr_; - /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; - - public: - MutableBitRef() = default; - - /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the - * exact int the bit is in. - */ - MutableBitRef(IntType *ptr, const int64_t bit_index) - { - ptr_ = int_containing_bit(ptr, bit_index); - mask_ = mask_for_bit(bit_index & BitIndexMask); - } - - /** - * Support implicitly casting to a read-only #BitRef. - */ - operator BitRef() const - { - BitRef bit_ref; - bit_ref.ptr_ = ptr_; - bit_ref.mask_ = mask_; - return bit_ref; - } - - /** - * Return true when the bit is currently 1 and false otherwise. - */ - bool test() const - { - const IntType value = *ptr_; - const IntType masked_value = value & mask_; - return masked_value != 0; - } - - operator bool() const - { - return this->test(); - } - - /** - * Change the bit to a 1. - */ - void set() - { - *ptr_ |= mask_; - } - - /** - * Change the bit to a 0. - */ - void reset() - { - *ptr_ &= ~mask_; - } - - /** - * Change the bit to a 1 if #value is true and 0 otherwise. - */ - void set(const bool value) - { - if (value) { - this->set(); - } - else { - this->reset(); - } - } -}; - class BitIterator { private: const IntType *data_; @@ -264,6 +109,10 @@ class BitSpan { public: BitSpan() = default; + BitSpan(const IntType *data, const int64_t size) : data_(data), bit_range_(size) + { + } + BitSpan(const IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) { } @@ -319,6 +168,10 @@ class MutableBitSpan { public: MutableBitSpan() = default; + MutableBitSpan(IntType *data, const int64_t size) : data_(data), bit_range_(size) + { + } + MutableBitSpan(IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) { } @@ -742,7 +595,5 @@ class BitVector { } // namespace blender::bits namespace blender { -using bits::BitRef; using bits::BitVector; -using bits::MutableBitRef; } // namespace blender -- 2.30.2 From a1f9a3e02d090cecb027fee142fef5db632fa7b7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 21:36:16 +0100 Subject: [PATCH 06/24] extract bit span header --- source/blender/blenlib/BLI_bit_span.hh | 242 +++++++++++++++++++++++ source/blender/blenlib/BLI_bit_vector.hh | 235 +--------------------- 2 files changed, 243 insertions(+), 234 deletions(-) create mode 100644 source/blender/blenlib/BLI_bit_span.hh diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh new file mode 100644 index 00000000000..f4967a5dbe1 --- /dev/null +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_bit_ref.hh" +#include "BLI_index_range.hh" +#include "BLI_memory_utils.hh" + +namespace blender::bits { + +class BitIterator { + private: + const IntType *data_; + int64_t bit_index_; + + public: + BitIterator(const IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + { + } + + BitIterator &operator++() + { + bit_index_++; + return *this; + } + + BitRef operator*() const + { + return BitRef(data_, bit_index_); + } + + friend bool operator!=(const BitIterator &a, const BitIterator &b) + { + BLI_assert(a.data_ == b.data_); + return a.bit_index_ != b.bit_index_; + } +}; + +class MutableBitIterator { + private: + IntType *data_; + int64_t bit_index_; + + public: + MutableBitIterator(IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + { + } + + MutableBitIterator &operator++() + { + bit_index_++; + return *this; + } + + MutableBitRef operator*() const + { + return MutableBitRef(data_, bit_index_); + } + + friend bool operator!=(const MutableBitIterator &a, const MutableBitIterator &b) + { + BLI_assert(a.data_ == b.data_); + return a.bit_index_ != b.bit_index_; + } +}; + +class BitSpan { + private: + const IntType *data_ = nullptr; + IndexRange bit_range_ = {0, 0}; + + public: + BitSpan() = default; + + BitSpan(const IntType *data, const int64_t size) : data_(data), bit_range_(size) + { + } + + BitSpan(const IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + BitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return {data_, bit_range_.start() + index}; + } + + BitSpan slice(const IndexRange range) const + { + return {data_, bit_range_.slice(range)}; + } + + const IntType *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + BitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + BitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } +}; + +class MutableBitSpan { + private: + IntType *data_ = nullptr; + IndexRange bit_range_ = {0, 0}; + + public: + MutableBitSpan() = default; + + MutableBitSpan(IntType *data, const int64_t size) : data_(data), bit_range_(size) + { + } + + MutableBitSpan(IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + MutableBitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return {data_, bit_range_.start() + index}; + } + + MutableBitSpan slice(const IndexRange range) const + { + return {data_, bit_range_.slice(range)}; + } + + IntType *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + MutableBitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + MutableBitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } + + operator BitSpan() const + { + return {data_, bit_range_}; + } + + void set() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + IntType &first_int = *int_containing_bit(data_, bit_range_.start()); + const IntType first_int_mask = mask_for_last_n_bits(ranges.prefix.size()); + first_int |= first_int_mask; + } + { + IntType *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr IntType fill_value = IntType(-1); + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const IntType last_int_mask = mask_for_first_n_bits(ranges.suffix.size()); + last_int |= last_int_mask; + } + } + + void reset() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + IntType &first_int = *int_containing_bit(data_, bit_range_.start()); + const IntType first_int_mask = mask_for_first_n_bits(ranges.prefix.size()); + first_int &= first_int_mask; + } + { + IntType *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr IntType fill_value = 0; + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const IntType last_int_mask = mask_for_last_n_bits(ranges.suffix.size()); + last_int &= last_int_mask; + } + } + + void set(const bool value) + { + if (value) { + this->set(); + } + else { + this->reset(); + } + } +}; + +} // namespace blender::bits diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index c53bf66c006..97f5d1c73bc 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -38,244 +38,11 @@ #include #include "BLI_allocator.hh" -#include "BLI_bit_ref.hh" -#include "BLI_index_range.hh" -#include "BLI_memory_utils.hh" +#include "BLI_bit_span.hh" #include "BLI_span.hh" namespace blender::bits { -class BitIterator { - private: - const IntType *data_; - int64_t bit_index_; - - public: - BitIterator(const IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) - { - } - - BitIterator &operator++() - { - bit_index_++; - return *this; - } - - BitRef operator*() const - { - return BitRef(data_, bit_index_); - } - - friend bool operator!=(const BitIterator &a, const BitIterator &b) - { - BLI_assert(a.data_ == b.data_); - return a.bit_index_ != b.bit_index_; - } -}; - -class MutableBitIterator { - private: - IntType *data_; - int64_t bit_index_; - - public: - MutableBitIterator(IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) - { - } - - MutableBitIterator &operator++() - { - bit_index_++; - return *this; - } - - MutableBitRef operator*() const - { - return MutableBitRef(data_, bit_index_); - } - - friend bool operator!=(const MutableBitIterator &a, const MutableBitIterator &b) - { - BLI_assert(a.data_ == b.data_); - return a.bit_index_ != b.bit_index_; - } -}; - -class BitSpan { - private: - const IntType *data_ = nullptr; - IndexRange bit_range_ = {0, 0}; - - public: - BitSpan() = default; - - BitSpan(const IntType *data, const int64_t size) : data_(data), bit_range_(size) - { - } - - BitSpan(const IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) - { - } - - int64_t size() const - { - return bit_range_.size(); - } - - bool is_empty() const - { - return bit_range_.is_empty(); - } - - BitRef operator[](const int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < bit_range_.size()); - return {data_, bit_range_.start() + index}; - } - - BitSpan slice(const IndexRange range) const - { - return {data_, bit_range_.slice(range)}; - } - - const IntType *data() const - { - return data_; - } - - const IndexRange &bit_range() const - { - return bit_range_; - } - - BitIterator begin() const - { - return {data_, bit_range_.start()}; - } - - BitIterator end() const - { - return {data_, bit_range_.one_after_last()}; - } -}; - -class MutableBitSpan { - private: - IntType *data_ = nullptr; - IndexRange bit_range_ = {0, 0}; - - public: - MutableBitSpan() = default; - - MutableBitSpan(IntType *data, const int64_t size) : data_(data), bit_range_(size) - { - } - - MutableBitSpan(IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) - { - } - - int64_t size() const - { - return bit_range_.size(); - } - - bool is_empty() const - { - return bit_range_.is_empty(); - } - - MutableBitRef operator[](const int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < bit_range_.size()); - return {data_, bit_range_.start() + index}; - } - - MutableBitSpan slice(const IndexRange range) const - { - return {data_, bit_range_.slice(range)}; - } - - IntType *data() const - { - return data_; - } - - const IndexRange &bit_range() const - { - return bit_range_; - } - - MutableBitIterator begin() const - { - return {data_, bit_range_.start()}; - } - - MutableBitIterator end() const - { - return {data_, bit_range_.one_after_last()}; - } - - operator BitSpan() const - { - return {data_, bit_range_}; - } - - void set() - { - const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); - { - IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_for_last_n_bits(ranges.prefix.size()); - first_int |= first_int_mask; - } - { - IntType *start = int_containing_bit(data_, ranges.aligned.start()); - const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; - constexpr IntType fill_value = IntType(-1); - initialized_fill_n(start, ints_to_fill, fill_value); - } - { - IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_for_first_n_bits(ranges.suffix.size()); - last_int |= last_int_mask; - } - } - - void reset() - { - const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); - { - IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_for_first_n_bits(ranges.prefix.size()); - first_int &= first_int_mask; - } - { - IntType *start = int_containing_bit(data_, ranges.aligned.start()); - const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; - constexpr IntType fill_value = 0; - initialized_fill_n(start, ints_to_fill, fill_value); - } - { - IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_for_last_n_bits(ranges.suffix.size()); - last_int &= last_int_mask; - } - } - - void set(const bool value) - { - if (value) { - this->set(); - } - else { - this->reset(); - } - } -}; - template< /** * Number of bits that can be stored in the vector without doing an allocation. -- 2.30.2 From 9357cf782eb50298f2daaba52fe10ad679a85e6e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 22:08:20 +0100 Subject: [PATCH 07/24] add bit ref tests --- source/blender/blenlib/BLI_bit_ref.hh | 18 ++- source/blender/blenlib/BLI_bit_span.hh | 8 +- source/blender/blenlib/CMakeLists.txt | 1 + .../blender/blenlib/tests/BLI_bit_ref_test.cc | 119 ++++++++++++++++++ .../blenlib/tests/BLI_bit_vector_test.cc | 4 +- 5 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 source/blender/blenlib/tests/BLI_bit_ref_test.cc diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 233d5300a77..5e611705f56 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -16,18 +16,24 @@ static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (size static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 - 1); -inline IntType mask_for_first_n_bits(const int64_t n) +inline IntType mask_first_n_bits(const int64_t n) { + BLI_assert(n >= 0); + BLI_assert(n < BitsPerInt); return (IntType(1) << n) - 1; } -inline IntType mask_for_last_n_bits(const int64_t n) +inline IntType mask_last_n_bits(const int64_t n) { - return ~((IntType(1) << (BitsPerInt - n)) - 1); + BLI_assert(n > 0); + BLI_assert(n <= BitsPerInt); + return ~mask_first_n_bits(BitsPerInt - n); } -inline IntType mask_for_bit(const int64_t bit_index) +inline IntType mask_single_bit(const int64_t bit_index) { + BLI_assert(bit_index >= 0); + BLI_assert(bit_index < BitsPerInt); return IntType(1) << bit_index; } @@ -64,7 +70,7 @@ class BitRef { BitRef(const IntType *ptr, const int64_t bit_index) { ptr_ = int_containing_bit(ptr, bit_index); - mask_ = mask_for_bit(bit_index & BitIndexMask); + mask_ = mask_single_bit(bit_index & BitIndexMask); } /** @@ -103,7 +109,7 @@ class MutableBitRef { MutableBitRef(IntType *ptr, const int64_t bit_index) { ptr_ = int_containing_bit(ptr, bit_index); - mask_ = mask_for_bit(bit_index & BitIndexMask); + mask_ = mask_single_bit(bit_index & BitIndexMask); } /** diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index f4967a5dbe1..069400b8b10 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -191,7 +191,7 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_for_last_n_bits(ranges.prefix.size()); + const IntType first_int_mask = mask_last_n_bits(ranges.prefix.size()); first_int |= first_int_mask; } { @@ -202,7 +202,7 @@ class MutableBitSpan { } { IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_for_first_n_bits(ranges.suffix.size()); + const IntType last_int_mask = mask_first_n_bits(ranges.suffix.size()); last_int |= last_int_mask; } } @@ -212,7 +212,7 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_for_first_n_bits(ranges.prefix.size()); + const IntType first_int_mask = mask_first_n_bits(ranges.prefix.size()); first_int &= first_int_mask; } { @@ -223,7 +223,7 @@ class MutableBitSpan { } { IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_for_last_n_bits(ranges.suffix.size()); + const IntType last_int_mask = mask_last_n_bits(ranges.suffix.size()); last_int &= last_int_mask; } } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 7c24fb8b5e7..b8957e35606 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -457,6 +457,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bit_ref_test.cc tests/BLI_bit_vector_test.cc tests/BLI_bitmap_test.cc tests/BLI_bounds_test.cc diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc new file mode 100644 index 00000000000..01252c8c228 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "BLI_bit_ref.hh" + +#include "testing/testing.h" + +namespace blender::bits::tests { + +TEST(bit_ref, MaskFirstNBits) +{ + EXPECT_EQ(mask_first_n_bits(0), 0); + EXPECT_EQ(mask_first_n_bits(1), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001); + EXPECT_EQ(mask_first_n_bits(5), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); + EXPECT_EQ(mask_first_n_bits(63), + 0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + +TEST(bit_ref, MaskLastNBits) +{ + EXPECT_EQ(mask_last_n_bits(1), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_last_n_bits(5), + 0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_last_n_bits(63), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1110); + EXPECT_EQ(mask_last_n_bits(64), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + +TEST(bit_ref, MaskSingleBit) +{ + EXPECT_EQ(mask_single_bit(0), 1); + EXPECT_EQ(mask_single_bit(1), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010); + EXPECT_EQ(mask_single_bit(5), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); + EXPECT_EQ(mask_single_bit(63), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); +} + +TEST(bit_ref, IntContainingBit) +{ + std::array array; + uint64_t *data = array.data(); + EXPECT_EQ(int_containing_bit(data, 0), data); + EXPECT_EQ(int_containing_bit(data, 1), data); + EXPECT_EQ(int_containing_bit(data, 63), data); + EXPECT_EQ(int_containing_bit(data, 64), data + 1); + EXPECT_EQ(int_containing_bit(data, 65), data + 1); + EXPECT_EQ(int_containing_bit(data, 100), data + 1); + EXPECT_EQ(int_containing_bit(data, 127), data + 1); + EXPECT_EQ(int_containing_bit(data, 128), data + 2); + const uint64_t *data_const = data; + EXPECT_EQ(int_containing_bit(data_const, 0), data_const); + EXPECT_EQ(int_containing_bit(data_const, 1), data_const); + EXPECT_EQ(int_containing_bit(data_const, 63), data_const); + EXPECT_EQ(int_containing_bit(data_const, 64), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 65), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 100), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 127), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 128), data_const + 2); +} + +TEST(bit_ref, Test) +{ + uint64_t data = (1 << 3) | (1 << 7); + EXPECT_FALSE(BitRef(&data, 0).test()); + EXPECT_FALSE(BitRef(&data, 1).test()); + EXPECT_FALSE(BitRef(&data, 2).test()); + EXPECT_TRUE(BitRef(&data, 3).test()); + EXPECT_FALSE(BitRef(&data, 4)); + EXPECT_FALSE(BitRef(&data, 5)); + EXPECT_FALSE(BitRef(&data, 6)); + EXPECT_TRUE(BitRef(&data, 7)); + + EXPECT_FALSE(MutableBitRef(&data, 0).test()); + EXPECT_FALSE(MutableBitRef(&data, 1).test()); + EXPECT_FALSE(MutableBitRef(&data, 2).test()); + EXPECT_TRUE(MutableBitRef(&data, 3).test()); + EXPECT_FALSE(MutableBitRef(&data, 4)); + EXPECT_FALSE(MutableBitRef(&data, 5)); + EXPECT_FALSE(MutableBitRef(&data, 6)); + EXPECT_TRUE(MutableBitRef(&data, 7)); +} + +TEST(bit_ref, Set) +{ + uint64_t data = 0; + MutableBitRef(&data, 0).set(); + MutableBitRef(&data, 1).set(); + MutableBitRef(&data, 1).set(); + MutableBitRef(&data, 4).set(); + EXPECT_EQ(data, (1 << 0) | (1 << 1) | (1 << 4)); + MutableBitRef(&data, 5).set(true); + MutableBitRef(&data, 1).set(false); + EXPECT_EQ(data, (1 << 0) | (1 << 4) | (1 << 5)); +} + +TEST(bit_ref, Reset) +{ + uint64_t data = -1; + MutableBitRef(&data, 0).reset(); + MutableBitRef(&data, 2).reset(); + EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2)); +} + +TEST(bit_ref, Cast) +{ + uint64_t data = 0; + MutableBitRef mutable_ref(&data, 3); + BitRef ref = mutable_ref; + EXPECT_FALSE(ref); + mutable_ref.set(); + EXPECT_TRUE(ref); +} + +} // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_bit_vector_test.cc b/source/blender/blenlib/tests/BLI_bit_vector_test.cc index 210f2be012d..a8e199f6f64 100644 --- a/source/blender/blenlib/tests/BLI_bit_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_vector_test.cc @@ -6,7 +6,7 @@ #include "testing/testing.h" -namespace blender::tests { +namespace blender::bits::tests { TEST(bit_vector, DefaultConstructor) { @@ -183,4 +183,4 @@ TEST(bit_vector, AppendMany) EXPECT_TRUE(vec[5]); } -} // namespace blender::tests +} // namespace blender::bits::tests -- 2.30.2 From 1416501ca676b5b602afd2b1d3b2b9da8dc7b7f5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 23:18:23 +0100 Subject: [PATCH 08/24] add tests --- source/blender/blenlib/BLI_bit_ref.hh | 32 ++++- source/blender/blenlib/BLI_bit_span.hh | 40 +++++- source/blender/blenlib/CMakeLists.txt | 1 + .../blender/blenlib/tests/BLI_bit_ref_test.cc | 26 ++++ .../blenlib/tests/BLI_bit_span_test.cc | 131 ++++++++++++++++++ .../blenlib/tests/BLI_index_range_test.cc | 18 +++ 6 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 source/blender/blenlib/tests/BLI_bit_span_test.cc diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 5e611705f56..d5e9627b9a6 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -2,8 +2,11 @@ #pragma once +#include "BLI_index_range.hh" #include "BLI_utildefines.h" +#include + namespace blender::bits { /** @@ -19,17 +22,30 @@ static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 inline IntType mask_first_n_bits(const int64_t n) { BLI_assert(n >= 0); - BLI_assert(n < BitsPerInt); + BLI_assert(n <= BitsPerInt); + if (n == BitsPerInt) { + return IntType(-1); + } return (IntType(1) << n) - 1; } inline IntType mask_last_n_bits(const int64_t n) { - BLI_assert(n > 0); - BLI_assert(n <= BitsPerInt); return ~mask_first_n_bits(BitsPerInt - n); } +inline IntType mask_range_bits(const IndexRange range) +{ + const int64_t size = range.size(); + const int64_t start = range.start() & BitIndexMask; + const int64_t end = start + size; + BLI_assert(end <= BitsPerInt); + if (end == BitsPerInt) { + return mask_last_n_bits(size); + } + return ((IntType(1) << end) - 1) & ~((IntType(1) << start) - 1); +} + inline IntType mask_single_bit(const int64_t bit_index) { BLI_assert(bit_index >= 0); @@ -168,6 +184,16 @@ class MutableBitRef { } }; +inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit) +{ + return stream << (bit ? "1" : "0"); +} + +inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit) +{ + return stream << BitRef(bit); +} + } // namespace blender::bits namespace blender { diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 069400b8b10..8db77a6f2ee 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -90,6 +90,11 @@ class BitSpan { return bit_range_.is_empty(); } + IndexRange index_range() const + { + return IndexRange(bit_range_.size()); + } + BitRef operator[](const int64_t index) const { BLI_assert(index >= 0); @@ -149,6 +154,11 @@ class MutableBitSpan { return bit_range_.is_empty(); } + IndexRange index_range() const + { + return IndexRange(bit_range_.size()); + } + MutableBitRef operator[](const int64_t index) const { BLI_assert(index >= 0); @@ -191,7 +201,7 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_last_n_bits(ranges.prefix.size()); + const IntType first_int_mask = mask_range_bits(ranges.prefix); first_int |= first_int_mask; } { @@ -212,8 +222,8 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_first_n_bits(ranges.prefix.size()); - first_int &= first_int_mask; + const IntType first_int_mask = mask_range_bits(ranges.prefix); + first_int &= ~first_int_mask; } { IntType *start = int_containing_bit(data_, ranges.aligned.start()); @@ -223,8 +233,8 @@ class MutableBitSpan { } { IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_last_n_bits(ranges.suffix.size()); - last_int &= last_int_mask; + const IntType last_int_mask = mask_first_n_bits(ranges.suffix.size()); + last_int &= ~last_int_mask; } } @@ -239,4 +249,24 @@ class MutableBitSpan { } }; +inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span) +{ + stream << "(Size: " << span.size() << ", "; + for (const BitRef bit : span) { + stream << bit; + } + stream << ")"; + return stream; +} + +inline std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span) +{ + return stream << BitSpan(span); +} + } // namespace blender::bits + +namespace blender { +using bits::BitSpan; +using bits::MutableBitSpan; +} // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index b8957e35606..cf1045f1d36 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -458,6 +458,7 @@ if(WITH_GTESTS) tests/BLI_array_test.cc tests/BLI_array_utils_test.cc tests/BLI_bit_ref_test.cc + tests/BLI_bit_span_test.cc tests/BLI_bit_vector_test.cc tests/BLI_bitmap_test.cc tests/BLI_bounds_test.cc diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc index 01252c8c228..491fefe1db0 100644 --- a/source/blender/blenlib/tests/BLI_bit_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -15,10 +15,14 @@ TEST(bit_ref, MaskFirstNBits) 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); EXPECT_EQ(mask_first_n_bits(63), 0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); + EXPECT_EQ(mask_first_n_bits(64), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); } TEST(bit_ref, MaskLastNBits) { + EXPECT_EQ(mask_last_n_bits(0), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); EXPECT_EQ(mask_last_n_bits(1), 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); EXPECT_EQ(mask_last_n_bits(5), @@ -116,4 +120,26 @@ TEST(bit_ref, Cast) EXPECT_TRUE(ref); } +TEST(bit_ref, MaskRangeBits) +{ + EXPECT_EQ(mask_range_bits(IndexRange(0)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(1)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001); + EXPECT_EQ(mask_range_bits(IndexRange(5)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); + EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(0)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(1)), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(5)), + 0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(8, 3)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(8 + 64, 3)), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000); + EXPECT_EQ(mask_range_bits(IndexRange(64)), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + } // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_bit_span_test.cc b/source/blender/blenlib/tests/BLI_bit_span_test.cc new file mode 100644 index 00000000000..d6749545b2f --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bit_span_test.cc @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "BLI_bit_span.hh" + +#include "testing/testing.h" + +namespace blender::bits::tests { + +TEST(bit_span, DefaultConstructor) +{ + { + char buffer[sizeof(BitSpan)]; + memset(buffer, 0xff, sizeof(BitSpan)); + BitSpan &span = *new (buffer) BitSpan(); + EXPECT_TRUE(span.is_empty()); + EXPECT_EQ(span.size(), 0); + } + { + char buffer[sizeof(MutableBitSpan)]; + memset(buffer, 0xff, sizeof(MutableBitSpan)); + MutableBitSpan &span = *new (buffer) MutableBitSpan(); + EXPECT_TRUE(span.is_empty()); + EXPECT_EQ(span.size(), 0); + } +} + +TEST(bit_span, Iteration) +{ + uint64_t data = (1 << 2) | (1 << 3); + const BitSpan span(&data, 30); + EXPECT_EQ(span.size(), 30); + int index = 0; + for (const BitRef bit : span) { + EXPECT_EQ(bit.test(), ELEM(index, 2, 3)); + index++; + } +} + +TEST(bit_span, MutableIteration) +{ + uint64_t data = 0; + MutableBitSpan span(&data, 40); + EXPECT_EQ(span.size(), 40); + int index = 0; + for (MutableBitRef bit : span) { + bit.set(index % 4 == 0); + index++; + } + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0001'0001'0001'0001'0001'0001'0001'0001'0001'0001); +} + +TEST(bit_span, SubscriptOperator) +{ + uint64_t data[2] = {0, 0}; + MutableBitSpan mutable_span(data, 128); + BitSpan span = mutable_span; + + EXPECT_EQ(mutable_span.data(), data); + EXPECT_EQ(mutable_span.bit_range(), IndexRange(128)); + EXPECT_EQ(span.data(), data); + EXPECT_EQ(span.bit_range(), IndexRange(128)); + + EXPECT_FALSE(mutable_span[5].test()); + EXPECT_FALSE(span[5].test()); + mutable_span[5].set(5); + EXPECT_TRUE(mutable_span[5].test()); + EXPECT_TRUE(span[5].test()); + + EXPECT_FALSE(mutable_span[120].test()); + EXPECT_FALSE(span[120].test()); + mutable_span[120].set(120); + EXPECT_TRUE(mutable_span[120].test()); + EXPECT_TRUE(span[120].test()); + + EXPECT_EQ(data[0], + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); + EXPECT_EQ(data[1], + 0b0000'0001'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); +} + +TEST(bit_span, RangeConstructor) +{ + uint64_t data = 0; + MutableBitSpan mutable_span(&data, IndexRange(4, 3)); + BitSpan span = mutable_span; + + EXPECT_FALSE(mutable_span[1].test()); + EXPECT_FALSE(span[1].test()); + mutable_span[0].set(true); + mutable_span[1].set(true); + mutable_span[2].set(true); + mutable_span[0].set(false); + mutable_span[2].set(false); + EXPECT_TRUE(mutable_span[1].test()); + EXPECT_TRUE(span[1].test()); + + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); +} + +TEST(bit_span, Set) +{ + uint64_t data = 0; + MutableBitSpan(&data, 64).set(true); + EXPECT_EQ(data, uint64_t(-1)); + MutableBitSpan(&data, 64).set(false); + EXPECT_EQ(data, uint64_t(0)); + + MutableBitSpan(&data, IndexRange(4, 8)).set(true); + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000); + MutableBitSpan(&data, IndexRange(8, 30)).set(false); + + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000); +} + +TEST(bit_span, SetSliced) +{ + std::array data; + memset(data.data(), 0, sizeof(data)); + MutableBitSpan span{data.data(), 640}; + span.slice(IndexRange(5, 500)).set(true); + + for (const int64_t i : IndexRange(640)) { + EXPECT_EQ(span[i], i >= 5 && i < 505); + } +} + +} // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 8ec7ad85d9c..b76f597ded6 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -290,6 +290,24 @@ TEST(index_range, SplitByAlignment) EXPECT_EQ(ranges.aligned, IndexRange()); EXPECT_EQ(ranges.suffix, IndexRange()); } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64), 64); + EXPECT_EQ(ranges.prefix, IndexRange()); + EXPECT_EQ(ranges.aligned, IndexRange(64)); + EXPECT_EQ(ranges.suffix, IndexRange()); + } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 64), 64); + EXPECT_EQ(ranges.prefix, IndexRange()); + EXPECT_EQ(ranges.aligned, IndexRange(64, 64)); + EXPECT_EQ(ranges.suffix, IndexRange()); + } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(4, 8), 64); + EXPECT_EQ(ranges.prefix, IndexRange(4, 8)); + EXPECT_EQ(ranges.aligned, IndexRange()); + EXPECT_EQ(ranges.suffix, IndexRange()); + } } } // namespace blender::tests -- 2.30.2 From c08cb9f9bb93313a291067bb6e6db79c6cd4e97f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 23:24:27 +0100 Subject: [PATCH 09/24] more tests --- source/blender/blenlib/tests/BLI_bit_span_test.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenlib/tests/BLI_bit_span_test.cc b/source/blender/blenlib/tests/BLI_bit_span_test.cc index d6749545b2f..0ade4ffb114 100644 --- a/source/blender/blenlib/tests/BLI_bit_span_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_span_test.cc @@ -126,6 +126,12 @@ TEST(bit_span, SetSliced) for (const int64_t i : IndexRange(640)) { EXPECT_EQ(span[i], i >= 5 && i < 505); } + + span.slice(IndexRange(10, 190)).set(false); + + for (const int64_t i : IndexRange(640)) { + EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505)); + } } } // namespace blender::bits::tests -- 2.30.2 From 3ca68fb1629e67ed7c6c2933e1ad499afc6c56e8 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 12 Feb 2023 23:25:16 +0100 Subject: [PATCH 10/24] cleanup naming --- source/blender/blenlib/BLI_bit_ref.hh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index d5e9627b9a6..356fcadfa60 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -70,7 +70,7 @@ inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_ class BitRef { private: /** Points to the integer that the bit is in. */ - const IntType *ptr_; + const IntType *data_; /** All zeros except for a single one at the bit that is referenced. */ IntType mask_; @@ -80,12 +80,12 @@ class BitRef { BitRef() = default; /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the + * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact integer the bit is in. */ - BitRef(const IntType *ptr, const int64_t bit_index) + BitRef(const IntType *data, const int64_t bit_index) { - ptr_ = int_containing_bit(ptr, bit_index); + data_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } @@ -94,7 +94,7 @@ class BitRef { */ bool test() const { - const IntType value = *ptr_; + const IntType value = *data_; const IntType masked_value = value & mask_; return masked_value != 0; } @@ -111,7 +111,7 @@ class BitRef { class MutableBitRef { private: /** Points to the integer that the bit is in. */ - IntType *ptr_; + IntType *data_; /** All zeros except for a single one at the bit that is referenced. */ IntType mask_; @@ -119,12 +119,12 @@ class MutableBitRef { MutableBitRef() = default; /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the + * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact int the bit is in. */ - MutableBitRef(IntType *ptr, const int64_t bit_index) + MutableBitRef(IntType *data, const int64_t bit_index) { - ptr_ = int_containing_bit(ptr, bit_index); + data_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } @@ -134,7 +134,7 @@ class MutableBitRef { operator BitRef() const { BitRef bit_ref; - bit_ref.ptr_ = ptr_; + bit_ref.data_ = data_; bit_ref.mask_ = mask_; return bit_ref; } @@ -144,7 +144,7 @@ class MutableBitRef { */ bool test() const { - const IntType value = *ptr_; + const IntType value = *data_; const IntType masked_value = value & mask_; return masked_value != 0; } @@ -159,7 +159,7 @@ class MutableBitRef { */ void set() { - *ptr_ |= mask_; + *data_ |= mask_; } /** @@ -167,7 +167,7 @@ class MutableBitRef { */ void reset() { - *ptr_ &= ~mask_; + *data_ &= ~mask_; } /** -- 2.30.2 From 7872275678a67fc5fc74fc1ab851f4eced80a3cc Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 12:51:28 +0100 Subject: [PATCH 11/24] add include --- source/blender/blenlib/tests/BLI_bit_span_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenlib/tests/BLI_bit_span_test.cc b/source/blender/blenlib/tests/BLI_bit_span_test.cc index 0ade4ffb114..0e562663b20 100644 --- a/source/blender/blenlib/tests/BLI_bit_span_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_span_test.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 */ +#include + #include "BLI_bit_span.hh" #include "testing/testing.h" -- 2.30.2 From 41c5c1ee49c708708dd678a1456204d9e494e5b9 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 12:57:49 +0100 Subject: [PATCH 12/24] add comments --- source/blender/blenlib/BLI_bit_ref.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 356fcadfa60..0e5af9cb093 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -9,15 +9,15 @@ namespace blender::bits { -/** - * Using a large integer type is better because then it's easier to process many bits at once. - */ +/** Using a large integer type is better because then it's easier to process many bits at once. */ using IntType = uint64_t; +/** Number of bits that fit into #IntType. */ static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); +/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */ static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + (sizeof(IntType) >= 8); +/** Bit mask containing a 1 for the last few bits that index a bit inside of an #IntType. */ static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; -static constexpr IntType MostSignificantBit = IntType(1) << (sizeof(IntType) * 8 - 1); inline IntType mask_first_n_bits(const int64_t n) { -- 2.30.2 From 3a1592c5bb1c422b07392c1d08a1b09e935e366b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 12:58:37 +0100 Subject: [PATCH 13/24] add comment --- source/blender/blenlib/BLI_bit_ref.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 0e5af9cb093..712bf59ee0b 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -2,6 +2,10 @@ #pragma once +/** \file + * \ingroup bli + */ + #include "BLI_index_range.hh" #include "BLI_utildefines.h" -- 2.30.2 From 87a97fc5203a3adb8ccca4835b331146179c3e78 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 13:24:40 +0100 Subject: [PATCH 14/24] comment --- source/blender/blenlib/BLI_bit_ref.hh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 712bf59ee0b..5674bf5195a 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -4,6 +4,15 @@ /** \file * \ingroup bli + * + * This file provides the basis for processing "indexed bits" (i.e. every bit has an index). + * The main purpose of this file is to define how bits are indexed within a memory buffer. + * This is necessary, because there are many different ways to do it. For example, is the first bit + * the least or most significant bit and how does endianness affect the bit order. + * + * The order is defined as follows: + * - Every indexed bit is part of an #IntType. These ints are ordered by their address as usual. + * - Within each #IntType, the bits are ordered from least to most significant. */ #include "BLI_index_range.hh" -- 2.30.2 From 20943a825c1ad81adef6d799820d51292844f34a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 13:33:39 +0100 Subject: [PATCH 15/24] cleanup naming --- source/blender/blenlib/BLI_bit_ref.hh | 58 ++++++++++++------------ source/blender/blenlib/BLI_bit_span.hh | 48 ++++++++++---------- source/blender/blenlib/BLI_bit_vector.hh | 18 ++++---- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 5674bf5195a..ae5875b9c59 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -11,8 +11,8 @@ * the least or most significant bit and how does endianness affect the bit order. * * The order is defined as follows: - * - Every indexed bit is part of an #IntType. These ints are ordered by their address as usual. - * - Within each #IntType, the bits are ordered from least to most significant. + * - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual. + * - Within each #BitInt, the bits are ordered from least to most significant. */ #include "BLI_index_range.hh" @@ -23,31 +23,31 @@ namespace blender::bits { /** Using a large integer type is better because then it's easier to process many bits at once. */ -using IntType = uint64_t; -/** Number of bits that fit into #IntType. */ -static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); +using BitInt = uint64_t; +/** Number of bits that fit into #BitInt. */ +static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8); /** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */ -static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + - (sizeof(IntType) >= 8); -/** Bit mask containing a 1 for the last few bits that index a bit inside of an #IntType. */ -static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; +static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) + + (sizeof(BitInt) >= 8); +/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */ +static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1; -inline IntType mask_first_n_bits(const int64_t n) +inline BitInt mask_first_n_bits(const int64_t n) { BLI_assert(n >= 0); BLI_assert(n <= BitsPerInt); if (n == BitsPerInt) { - return IntType(-1); + return BitInt(-1); } - return (IntType(1) << n) - 1; + return (BitInt(1) << n) - 1; } -inline IntType mask_last_n_bits(const int64_t n) +inline BitInt mask_last_n_bits(const int64_t n) { return ~mask_first_n_bits(BitsPerInt - n); } -inline IntType mask_range_bits(const IndexRange range) +inline BitInt mask_range_bits(const IndexRange range) { const int64_t size = range.size(); const int64_t start = range.start() & BitIndexMask; @@ -56,22 +56,22 @@ inline IntType mask_range_bits(const IndexRange range) if (end == BitsPerInt) { return mask_last_n_bits(size); } - return ((IntType(1) << end) - 1) & ~((IntType(1) << start) - 1); + return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1); } -inline IntType mask_single_bit(const int64_t bit_index) +inline BitInt mask_single_bit(const int64_t bit_index) { BLI_assert(bit_index >= 0); BLI_assert(bit_index < BitsPerInt); - return IntType(1) << bit_index; + return BitInt(1) << bit_index; } -inline IntType *int_containing_bit(IntType *data, const int64_t bit_index) +inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index) { return data + (bit_index >> BitToIntIndexShift); } -inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_index) +inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index) { return data + (bit_index >> BitToIntIndexShift); } @@ -83,9 +83,9 @@ inline const IntType *int_containing_bit(const IntType *data, const int64_t bit_ class BitRef { private: /** Points to the integer that the bit is in. */ - const IntType *data_; + const BitInt *data_; /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; + BitInt mask_; friend class MutableBitRef; @@ -96,7 +96,7 @@ class BitRef { * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact integer the bit is in. */ - BitRef(const IntType *data, const int64_t bit_index) + BitRef(const BitInt *data, const int64_t bit_index) { data_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); @@ -107,8 +107,8 @@ class BitRef { */ bool test() const { - const IntType value = *data_; - const IntType masked_value = value & mask_; + const BitInt value = *data_; + const BitInt masked_value = value & mask_; return masked_value != 0; } @@ -124,9 +124,9 @@ class BitRef { class MutableBitRef { private: /** Points to the integer that the bit is in. */ - IntType *data_; + BitInt *data_; /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; + BitInt mask_; public: MutableBitRef() = default; @@ -135,7 +135,7 @@ class MutableBitRef { * Reference a specific bit in an array. Note that #data does *not* have to point to the * exact int the bit is in. */ - MutableBitRef(IntType *data, const int64_t bit_index) + MutableBitRef(BitInt *data, const int64_t bit_index) { data_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); @@ -157,8 +157,8 @@ class MutableBitRef { */ bool test() const { - const IntType value = *data_; - const IntType masked_value = value & mask_; + const BitInt value = *data_; + const BitInt masked_value = value & mask_; return masked_value != 0; } diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 8db77a6f2ee..59ef2d241fc 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -10,11 +10,11 @@ namespace blender::bits { class BitIterator { private: - const IntType *data_; + const BitInt *data_; int64_t bit_index_; public: - BitIterator(const IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + BitIterator(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) { } @@ -38,11 +38,11 @@ class BitIterator { class MutableBitIterator { private: - IntType *data_; + BitInt *data_; int64_t bit_index_; public: - MutableBitIterator(IntType *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + MutableBitIterator(BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) { } @@ -66,17 +66,17 @@ class MutableBitIterator { class BitSpan { private: - const IntType *data_ = nullptr; + const BitInt *data_ = nullptr; IndexRange bit_range_ = {0, 0}; public: BitSpan() = default; - BitSpan(const IntType *data, const int64_t size) : data_(data), bit_range_(size) + BitSpan(const BitInt *data, const int64_t size) : data_(data), bit_range_(size) { } - BitSpan(const IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) { } @@ -107,7 +107,7 @@ class BitSpan { return {data_, bit_range_.slice(range)}; } - const IntType *data() const + const BitInt *data() const { return data_; } @@ -130,17 +130,17 @@ class BitSpan { class MutableBitSpan { private: - IntType *data_ = nullptr; + BitInt *data_ = nullptr; IndexRange bit_range_ = {0, 0}; public: MutableBitSpan() = default; - MutableBitSpan(IntType *data, const int64_t size) : data_(data), bit_range_(size) + MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size) { } - MutableBitSpan(IntType *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) { } @@ -171,7 +171,7 @@ class MutableBitSpan { return {data_, bit_range_.slice(range)}; } - IntType *data() const + BitInt *data() const { return data_; } @@ -200,19 +200,19 @@ class MutableBitSpan { { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { - IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_range_bits(ranges.prefix); + BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); + const BitInt first_int_mask = mask_range_bits(ranges.prefix); first_int |= first_int_mask; } { - IntType *start = int_containing_bit(data_, ranges.aligned.start()); + BitInt *start = int_containing_bit(data_, ranges.aligned.start()); const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; - constexpr IntType fill_value = IntType(-1); + constexpr BitInt fill_value = BitInt(-1); initialized_fill_n(start, ints_to_fill, fill_value); } { - IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_first_n_bits(ranges.suffix.size()); + BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size()); last_int |= last_int_mask; } } @@ -221,19 +221,19 @@ class MutableBitSpan { { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { - IntType &first_int = *int_containing_bit(data_, bit_range_.start()); - const IntType first_int_mask = mask_range_bits(ranges.prefix); + BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); + const BitInt first_int_mask = mask_range_bits(ranges.prefix); first_int &= ~first_int_mask; } { - IntType *start = int_containing_bit(data_, ranges.aligned.start()); + BitInt *start = int_containing_bit(data_, ranges.aligned.start()); const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; - constexpr IntType fill_value = 0; + constexpr BitInt fill_value = 0; initialized_fill_n(start, ints_to_fill, fill_value); } { - IntType &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); - const IntType last_int_mask = mask_first_n_bits(ranges.suffix.size()); + BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size()); last_int &= ~last_int_mask; } } diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 97f5d1c73bc..1846ca68eaf 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -62,13 +62,13 @@ class BitVector { static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity); static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt; - static constexpr int64_t AllocationAlignment = alignof(IntType); + static constexpr int64_t AllocationAlignment = alignof(BitInt); /** * Points to the first integer used by the vector. It might point to the memory in the inline * buffer. */ - IntType *data_; + BitInt *data_; /** Current size of the vector in bits. */ int64_t size_in_bits_; @@ -80,7 +80,7 @@ class BitVector { BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** Contains the bits as long as the vector is small enough. */ - BLI_NO_UNIQUE_ADDRESS TypedBuffer inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer inline_buffer_; public: BitVector(Allocator allocator = {}) noexcept : allocator_(allocator) @@ -88,7 +88,7 @@ class BitVector { data_ = inline_buffer_; size_in_bits_ = 0; capacity_in_bits_ = BitsInInlineBuffer; - uninitialized_fill_n(data_, IntsInInlineBuffer, IntType(0)); + uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0)); } BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator) @@ -105,8 +105,8 @@ class BitVector { } else { /* Allocate a new array because the inline buffer is too small. */ - data_ = static_cast( - allocator_.allocate(ints_to_copy * sizeof(IntType), AllocationAlignment, __func__)); + data_ = static_cast( + allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__)); capacity_in_bits_ = ints_to_copy * BitsPerInt; } size_in_bits_ = other.size_in_bits_; @@ -317,7 +317,7 @@ class BitVector { } BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits, - const IntType initial_value_for_new_ints = 0) + const BitInt initial_value_for_new_ints = 0) { if (capacity_in_bits_ >= min_capacity_in_bits) { return; @@ -331,8 +331,8 @@ class BitVector { const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints); const int64_t ints_to_copy = this->used_ints_amount(); - IntType *new_data = static_cast(allocator_.allocate( - new_capacity_in_ints * sizeof(IntType), AllocationAlignment, __func__)); + BitInt *new_data = static_cast( + allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__)); uninitialized_copy_n(data_, ints_to_copy, new_data); /* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings * caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an -- 2.30.2 From c669cd2299806ab295054819ff300cb79ce105c7 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 13:34:01 +0100 Subject: [PATCH 16/24] cleanup --- source/blender/blenlib/BLI_bit_ref.hh | 6 +++--- source/blender/blenlib/BLI_bit_span.hh | 6 ++++-- .../blender/blenlib/tests/BLI_bit_ref_test.cc | 18 ++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index ae5875b9c59..da9e43be8af 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -47,10 +47,10 @@ inline BitInt mask_last_n_bits(const int64_t n) return ~mask_first_n_bits(BitsPerInt - n); } -inline BitInt mask_range_bits(const IndexRange range) +inline BitInt mask_range_bits(const int64_t start, const int64_t size) { - const int64_t size = range.size(); - const int64_t start = range.start() & BitIndexMask; + BLI_assert(start >= 0); + BLI_assert(size >= 0); const int64_t end = start + size; BLI_assert(end <= BitsPerInt); if (end == BitsPerInt) { diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 59ef2d241fc..d5b5b000e49 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -201,7 +201,8 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); - const BitInt first_int_mask = mask_range_bits(ranges.prefix); + const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask, + ranges.prefix.size()); first_int |= first_int_mask; } { @@ -222,7 +223,8 @@ class MutableBitSpan { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); - const BitInt first_int_mask = mask_range_bits(ranges.prefix); + const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask, + ranges.prefix.size()); first_int &= ~first_int_mask; } { diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc index 491fefe1db0..b9dfdc49d36 100644 --- a/source/blender/blenlib/tests/BLI_bit_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -122,23 +122,21 @@ TEST(bit_ref, Cast) TEST(bit_ref, MaskRangeBits) { - EXPECT_EQ(mask_range_bits(IndexRange(0)), + EXPECT_EQ(mask_range_bits(0, 0), 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(1)), + EXPECT_EQ(mask_range_bits(0, 1), 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001); - EXPECT_EQ(mask_range_bits(IndexRange(5)), + EXPECT_EQ(mask_range_bits(0, 5), 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); - EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(0)), + EXPECT_EQ(mask_range_bits(64, 0), 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(1)), + EXPECT_EQ(mask_range_bits(63, 1), 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(64).take_back(5)), + EXPECT_EQ(mask_range_bits(59, 5), 0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(8, 3)), + EXPECT_EQ(mask_range_bits(8, 3), 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(8 + 64, 3)), - 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000); - EXPECT_EQ(mask_range_bits(IndexRange(64)), + EXPECT_EQ(mask_range_bits(0, 64), 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); } -- 2.30.2 From 3490984ae1bf611ee670a84aabcce57edfbfc5f0 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 14:02:29 +0100 Subject: [PATCH 17/24] progress --- source/blender/blenlib/BLI_bit_ref.hh | 21 ++++++++++++++++++- .../blender/blenlib/tests/BLI_bit_ref_test.cc | 15 +++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index da9e43be8af..2385d05729b 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -184,7 +184,8 @@ class MutableBitRef { } /** - * Change the bit to a 1 if #value is true and 0 otherwise. + * Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable + * by the CPU branch predictor, it can be faster to use #set_branchless instead. */ void set(const bool value) { @@ -195,6 +196,24 @@ class MutableBitRef { this->reset(); } } + + /** + * Does the same as #set, but does not use a branch. This is faster when the input value is + * unpredictable for the CPU branch predictor (worst case is a uniform random distribution with + * 50% probability for true and false). If the value is predictable, this is likely slower than + * #set. + */ + void set_branchless(const bool value) + { + const BitInt value_int = BitInt(value); + BLI_assert(ELEM(value_int, 0, 1)); + const BitInt old = *data_; + *data_ = + /* Unset bit. */ + (~mask_ & old) + /* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */ + | (mask_ & ~(value_int - 1)); + } }; inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit) diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc index b9dfdc49d36..ce091e7d938 100644 --- a/source/blender/blenlib/tests/BLI_bit_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -110,6 +110,21 @@ TEST(bit_ref, Reset) EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2)); } +TEST(bit_ref, SetBranchless) +{ + uint64_t data = 0; + MutableBitRef(&data, 0).set_branchless(true); + EXPECT_EQ(data, 1); + MutableBitRef(&data, 0).set_branchless(false); + EXPECT_EQ(data, 0); + MutableBitRef(&data, 3).set_branchless(false); + MutableBitRef(&data, 4).set_branchless(true); + EXPECT_EQ(data, 16); + MutableBitRef(&data, 3).set_branchless(true); + MutableBitRef(&data, 4).set_branchless(true); + EXPECT_EQ(data, 24); +} + TEST(bit_ref, Cast) { uint64_t data = 0; -- 2.30.2 From 2b40992ac560c0c5040567d87bad1dacf2ed97ab Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 14:10:50 +0100 Subject: [PATCH 18/24] improve code reuse --- source/blender/blenlib/BLI_bit_span.hh | 43 ++++++++++---------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index d5b5b000e49..928689247b4 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -8,59 +8,50 @@ namespace blender::bits { -class BitIterator { - private: +class BitIteratorBase { + protected: const BitInt *data_; int64_t bit_index_; public: - BitIterator(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) { } - BitIterator &operator++() + BitIteratorBase &operator++() { bit_index_++; return *this; } - BitRef operator*() const - { - return BitRef(data_, bit_index_); - } - - friend bool operator!=(const BitIterator &a, const BitIterator &b) + friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b) { BLI_assert(a.data_ == b.data_); return a.bit_index_ != b.bit_index_; } }; -class MutableBitIterator { - private: - BitInt *data_; - int64_t bit_index_; - +class BitIterator : public BitIteratorBase { public: - MutableBitIterator(BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) { } - MutableBitIterator &operator++() + BitRef operator*() const + { + return BitRef(data_, bit_index_); + } +}; + +class MutableBitIterator : public BitIteratorBase { + public: + MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) { - bit_index_++; - return *this; } MutableBitRef operator*() const { - return MutableBitRef(data_, bit_index_); - } - - friend bool operator!=(const MutableBitIterator &a, const MutableBitIterator &b) - { - BLI_assert(a.data_ == b.data_); - return a.bit_index_ != b.bit_index_; + return MutableBitRef(const_cast(data_), bit_index_); } }; -- 2.30.2 From 2ea1e8b42ab5214e56ce5130db4aeff9cce15525 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 14:26:56 +0100 Subject: [PATCH 19/24] add comments --- source/blender/blenlib/BLI_bit_span.hh | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 928689247b4..fb9b53b701d 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -8,6 +8,7 @@ namespace blender::bits { +/** Base class for a const and non-const bit-iterator. */ class BitIteratorBase { protected: const BitInt *data_; @@ -31,6 +32,7 @@ class BitIteratorBase { } }; +/** Allows iterating over the bits in a memory buffer. */ class BitIterator : public BitIteratorBase { public: BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) @@ -43,6 +45,7 @@ class BitIterator : public BitIteratorBase { } }; +/** Allows iterating over the bits in a memory buffer. */ class MutableBitIterator : public BitIteratorBase { public: MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) @@ -55,15 +58,26 @@ class MutableBitIterator : public BitIteratorBase { } }; +/** + * Similar to #Span, but references a range of bits instead of normal C++ types (which are at least + * one byte large). Use #MutableBitSpan if the values are supposed to be modified. + * + * The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start + * and end at any bit. + */ class BitSpan { private: + /** Base pointer to the integers containing the bits. The actual bit span might start at a much + * higher address when `bit_range_.start()` is large. */ const BitInt *data_ = nullptr; + /** The range of referenced bits. */ IndexRange bit_range_ = {0, 0}; public: + /** Construct an empty span. */ BitSpan() = default; - BitSpan(const BitInt *data, const int64_t size) : data_(data), bit_range_(size) + BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits) { } @@ -71,7 +85,7 @@ class BitSpan { { } - int64_t size() const + int64_t size_in_bits() const { return bit_range_.size(); } @@ -119,6 +133,7 @@ class BitSpan { } }; +/** Same as #BitSpan, but also allows modifying the referenced bits. */ class MutableBitSpan { private: BitInt *data_ = nullptr; @@ -187,6 +202,7 @@ class MutableBitSpan { return {data_, bit_range_}; } + /** Sets all referenced bits to 1. */ void set() { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); @@ -209,6 +225,7 @@ class MutableBitSpan { } } + /** Sets all referenced bits to 0. */ void reset() { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); @@ -231,6 +248,7 @@ class MutableBitSpan { } } + /** Sets all referenced bits to either 0 or 1. */ void set(const bool value) { if (value) { -- 2.30.2 From 44e428e93b1b6857114371b64f63c48ae96643ba Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 14:31:10 +0100 Subject: [PATCH 20/24] fix --- source/blender/blenlib/BLI_bit_span.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index fb9b53b701d..c285ee9cb09 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -85,7 +85,8 @@ class BitSpan { { } - int64_t size_in_bits() const + /** Number of bits referenced by the span. */ + int64_t size() const { return bit_range_.size(); } -- 2.30.2 From 9ded8995cee0226b87ecf33b1b94683a879f969a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Feb 2023 14:48:43 +0100 Subject: [PATCH 21/24] add missing include --- source/blender/blenlib/tests/BLI_bit_ref_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc index ce091e7d938..2c880e4ea06 100644 --- a/source/blender/blenlib/tests/BLI_bit_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 */ +#include + #include "BLI_bit_ref.hh" #include "testing/testing.h" -- 2.30.2 From b30bd9d3a242dace10267d18ea6f050668807640 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 16 Feb 2023 23:32:54 +0100 Subject: [PATCH 22/24] cleanup --- source/blender/blenlib/BLI_bit_ref.hh | 34 +++++++++++++------------- source/blender/blenlib/BLI_bit_span.hh | 4 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh index 2385d05729b..09f17a0bb93 100644 --- a/source/blender/blenlib/BLI_bit_ref.hh +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -7,8 +7,8 @@ * * This file provides the basis for processing "indexed bits" (i.e. every bit has an index). * The main purpose of this file is to define how bits are indexed within a memory buffer. - * This is necessary, because there are many different ways to do it. For example, is the first bit - * the least or most significant bit and how does endianness affect the bit order. + * For example, one has to define whether the first bit is the least or most significant bit and + * how endianness affect the bit order. * * The order is defined as follows: * - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual. @@ -82,8 +82,8 @@ inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_in */ class BitRef { private: - /** Points to the integer that the bit is in. */ - const BitInt *data_; + /** Points to the exact integer that the bit is in. */ + const BitInt *int_; /** All zeros except for a single one at the bit that is referenced. */ BitInt mask_; @@ -98,7 +98,7 @@ class BitRef { */ BitRef(const BitInt *data, const int64_t bit_index) { - data_ = int_containing_bit(data, bit_index); + int_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } @@ -107,7 +107,7 @@ class BitRef { */ bool test() const { - const BitInt value = *data_; + const BitInt value = *int_; const BitInt masked_value = value & mask_; return masked_value != 0; } @@ -124,7 +124,7 @@ class BitRef { class MutableBitRef { private: /** Points to the integer that the bit is in. */ - BitInt *data_; + BitInt *int_; /** All zeros except for a single one at the bit that is referenced. */ BitInt mask_; @@ -137,7 +137,7 @@ class MutableBitRef { */ MutableBitRef(BitInt *data, const int64_t bit_index) { - data_ = int_containing_bit(data, bit_index); + int_ = int_containing_bit(data, bit_index); mask_ = mask_single_bit(bit_index & BitIndexMask); } @@ -147,7 +147,7 @@ class MutableBitRef { operator BitRef() const { BitRef bit_ref; - bit_ref.data_ = data_; + bit_ref.int_ = int_; bit_ref.mask_ = mask_; return bit_ref; } @@ -157,7 +157,7 @@ class MutableBitRef { */ bool test() const { - const BitInt value = *data_; + const BitInt value = *int_; const BitInt masked_value = value & mask_; return masked_value != 0; } @@ -172,7 +172,7 @@ class MutableBitRef { */ void set() { - *data_ |= mask_; + *int_ |= mask_; } /** @@ -180,7 +180,7 @@ class MutableBitRef { */ void reset() { - *data_ &= ~mask_; + *int_ &= ~mask_; } /** @@ -199,16 +199,16 @@ class MutableBitRef { /** * Does the same as #set, but does not use a branch. This is faster when the input value is - * unpredictable for the CPU branch predictor (worst case is a uniform random distribution with - * 50% probability for true and false). If the value is predictable, this is likely slower than - * #set. + * unpredictable for the CPU branch predictor (best case for this function is a uniform random + * distribution with 50% probability for true and false). If the value is predictable, this is + * likely slower than #set. */ void set_branchless(const bool value) { const BitInt value_int = BitInt(value); BLI_assert(ELEM(value_int, 0, 1)); - const BitInt old = *data_; - *data_ = + const BitInt old = *int_; + *int_ = /* Unset bit. */ (~mask_ & old) /* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */ diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index c285ee9cb09..3699ed0cf33 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -59,8 +59,8 @@ class MutableBitIterator : public BitIteratorBase { }; /** - * Similar to #Span, but references a range of bits instead of normal C++ types (which are at least - * one byte large). Use #MutableBitSpan if the values are supposed to be modified. + * Similar to #Span, but references a range of bits instead of normal C++ types (which must be at + * least one byte large). Use #MutableBitSpan if the values are supposed to be modified. * * The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start * and end at any bit. -- 2.30.2 From 93e369acd68d3101d958794d28c3ca2a646ccd12 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 16 Feb 2023 23:35:38 +0100 Subject: [PATCH 23/24] add _all suffix --- source/blender/blenlib/BLI_bit_span.hh | 16 +++++++++++----- source/blender/blenlib/BLI_bit_vector.hh | 4 ++-- .../blender/blenlib/tests/BLI_bit_span_test.cc | 12 ++++++------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 3699ed0cf33..0a499f8ae3a 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -204,7 +204,7 @@ class MutableBitSpan { } /** Sets all referenced bits to 1. */ - void set() + void set_all() { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { @@ -227,7 +227,7 @@ class MutableBitSpan { } /** Sets all referenced bits to 0. */ - void reset() + void reset_all() { const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); { @@ -250,15 +250,21 @@ class MutableBitSpan { } /** Sets all referenced bits to either 0 or 1. */ - void set(const bool value) + void set_all(const bool value) { if (value) { - this->set(); + this->set_all(); } else { - this->reset(); + this->reset_all(); } } + + /** Same as set_all to mirror #MutableSpan. */ + void fill(const bool value) + { + this->set_all(value); + } }; inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span) diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 1846ca68eaf..3c48d17703f 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -265,7 +265,7 @@ class BitVector { size_in_bits_ = new_size_in_bits; if (old_size_in_bits < new_size_in_bits) { MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits)) - .set(value); + .set_all(value); } } @@ -274,7 +274,7 @@ class BitVector { */ void fill(const bool value) { - MutableBitSpan(data_, size_in_bits_).set(value); + MutableBitSpan(data_, size_in_bits_).set_all(value); } /** diff --git a/source/blender/blenlib/tests/BLI_bit_span_test.cc b/source/blender/blenlib/tests/BLI_bit_span_test.cc index 0e562663b20..4ffaadd1ddb 100644 --- a/source/blender/blenlib/tests/BLI_bit_span_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_span_test.cc @@ -104,15 +104,15 @@ TEST(bit_span, RangeConstructor) TEST(bit_span, Set) { uint64_t data = 0; - MutableBitSpan(&data, 64).set(true); + MutableBitSpan(&data, 64).set_all(true); EXPECT_EQ(data, uint64_t(-1)); - MutableBitSpan(&data, 64).set(false); + MutableBitSpan(&data, 64).set_all(false); EXPECT_EQ(data, uint64_t(0)); - MutableBitSpan(&data, IndexRange(4, 8)).set(true); + MutableBitSpan(&data, IndexRange(4, 8)).set_all(true); EXPECT_EQ(data, 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000); - MutableBitSpan(&data, IndexRange(8, 30)).set(false); + MutableBitSpan(&data, IndexRange(8, 30)).set_all(false); EXPECT_EQ(data, 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000); @@ -123,13 +123,13 @@ TEST(bit_span, SetSliced) std::array data; memset(data.data(), 0, sizeof(data)); MutableBitSpan span{data.data(), 640}; - span.slice(IndexRange(5, 500)).set(true); + span.slice(IndexRange(5, 500)).set_all(true); for (const int64_t i : IndexRange(640)) { EXPECT_EQ(span[i], i >= 5 && i < 505); } - span.slice(IndexRange(10, 190)).set(false); + span.slice(IndexRange(10, 190)).set_all(false); for (const int64_t i : IndexRange(640)) { EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505)); -- 2.30.2 From 2f9e2f525702572e2d7012595cdac8fd6c0815da Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 16 Feb 2023 23:35:56 +0100 Subject: [PATCH 24/24] cleanup --- source/blender/blenlib/BLI_bit_span.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh index 0a499f8ae3a..0ab69b7cc67 100644 --- a/source/blender/blenlib/BLI_bit_span.hh +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -260,7 +260,7 @@ class MutableBitSpan { } } - /** Same as set_all to mirror #MutableSpan. */ + /** Same as #set_all to mirror #MutableSpan. */ void fill(const bool value) { this->set_all(value); -- 2.30.2