mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1927417 - Part 1: Add mozilla::BitSet methods to efficiently find the next and previous bits set r=glandium
Differential Revision: https://phabricator.services.mozilla.com/D227039
This commit is contained in:
parent
72924760ab
commit
aeab420bef
@ -26,6 +26,7 @@ template <size_t N, typename Word = size_t>
|
||||
class BitSet {
|
||||
static_assert(std::is_unsigned_v<Word>,
|
||||
"The Word type must be an unsigned integral type");
|
||||
static_assert(N != 0);
|
||||
|
||||
private:
|
||||
static constexpr size_t kBitsPerWord = 8 * sizeof(Word);
|
||||
@ -167,6 +168,55 @@ class BitSet {
|
||||
ResetPaddingBits();
|
||||
}
|
||||
|
||||
// Return the position of the first bit set, or SIZE_MAX if none.
|
||||
size_t FindFirst() const { return FindNext(0); }
|
||||
|
||||
// Return the position of the next bit set starting from |aFromPos| inclusive,
|
||||
// or SIZE_MAX if none.
|
||||
size_t FindNext(size_t aFromPos) const {
|
||||
MOZ_ASSERT(aFromPos < N);
|
||||
size_t wordIndex = aFromPos / kBitsPerWord;
|
||||
size_t bitIndex = aFromPos % kBitsPerWord;
|
||||
|
||||
Word word = mStorage[wordIndex];
|
||||
// Mask word containing |aFromPos|.
|
||||
word &= (Word(-1) << bitIndex);
|
||||
while (word == 0) {
|
||||
wordIndex++;
|
||||
if (wordIndex == kNumWords) {
|
||||
return SIZE_MAX;
|
||||
}
|
||||
word = mStorage[wordIndex];
|
||||
}
|
||||
|
||||
uint_fast8_t pos = CountTrailingZeroes(word);
|
||||
return wordIndex * kBitsPerWord + pos;
|
||||
}
|
||||
|
||||
size_t FindLast() const { return FindPrev(Size() - 1); }
|
||||
|
||||
// Return the position of the previous bit set starting from |aFromPos|
|
||||
// inclusive, or SIZE_MAX if none.
|
||||
size_t FindPrev(size_t aFromPos) const {
|
||||
MOZ_ASSERT(aFromPos < N);
|
||||
size_t wordIndex = aFromPos / kBitsPerWord;
|
||||
size_t bitIndex = aFromPos % kBitsPerWord;
|
||||
|
||||
Word word = mStorage[wordIndex];
|
||||
// Mask word containing |aFromPos|.
|
||||
word &= Word(-1) >> (kBitsPerWord - 1 - bitIndex);
|
||||
while (word == 0) {
|
||||
if (wordIndex == 0) {
|
||||
return SIZE_MAX;
|
||||
}
|
||||
wordIndex--;
|
||||
word = mStorage[wordIndex];
|
||||
}
|
||||
|
||||
uint_fast8_t pos = FindMostSignificantBit(word);
|
||||
return wordIndex * kBitsPerWord + pos;
|
||||
}
|
||||
|
||||
Span<Word> Storage() { return mStorage; }
|
||||
|
||||
Span<const Word> Storage() const { return mStorage; }
|
||||
|
@ -318,28 +318,24 @@ inline uint_fast8_t CeilingLog2Size(size_t aValue) {
|
||||
return CeilingLog2(aValue);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, size_t Size = sizeof(T)>
|
||||
class FloorLog2;
|
||||
|
||||
/**
|
||||
* Compute the bit position of the most significant bit set in
|
||||
* |aValue|. Requires that |aValue| is non-zero.
|
||||
*/
|
||||
template <typename T>
|
||||
class FloorLog2<T, 4> {
|
||||
public:
|
||||
static uint_fast8_t compute(const T aValue) {
|
||||
return 31u - CountLeadingZeroes32(aValue | 1);
|
||||
inline uint_fast8_t FindMostSignificantBit(T aValue) {
|
||||
static_assert(sizeof(T) <= 8);
|
||||
static_assert(std::is_integral_v<T>);
|
||||
MOZ_ASSERT(aValue != 0);
|
||||
// This casts to 32-bits
|
||||
if constexpr (sizeof(T) <= 4) {
|
||||
return 31u - CountLeadingZeroes32(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class FloorLog2<T, 8> {
|
||||
public:
|
||||
static uint_fast8_t compute(const T aValue) {
|
||||
return 63u - CountLeadingZeroes64(aValue | 1);
|
||||
// This doesn't
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
return 63u - CountLeadingZeroes64(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the log of the greatest power of 2 less than or equal to |aValue|.
|
||||
@ -351,7 +347,7 @@ class FloorLog2<T, 8> {
|
||||
*/
|
||||
template <typename T>
|
||||
inline constexpr uint_fast8_t FloorLog2(const T aValue) {
|
||||
return detail::FloorLog2<T>::compute(aValue);
|
||||
return FindMostSignificantBit(aValue | 1);
|
||||
}
|
||||
|
||||
/** A FloorLog2 variant that accepts only size_t. */
|
||||
|
@ -101,10 +101,72 @@ class BitSetSuite {
|
||||
}
|
||||
}
|
||||
|
||||
void testFindBits() {
|
||||
TestBitSet<kBitsPerWord * 5 + 2> bitset;
|
||||
size_t size = bitset.Size();
|
||||
MOZ_RELEASE_ASSERT(bitset.IsEmpty());
|
||||
MOZ_RELEASE_ASSERT(bitset.FindFirst() == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindLast() == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(0) == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(size - 1) == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(0) == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(size - 1) == SIZE_MAX);
|
||||
|
||||
// Test with single bit set.
|
||||
for (size_t i = 0; i < size; i += 5) {
|
||||
bitset[i] = true;
|
||||
MOZ_RELEASE_ASSERT(bitset.FindFirst() == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindLast() == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(i) == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(i) == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(0) == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(size - 1) == i);
|
||||
if (i != 0) {
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(i - 1) == i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(i - 1) == SIZE_MAX);
|
||||
}
|
||||
if (i != size - 1) {
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(i + 1) == SIZE_MAX);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(i + 1) == i);
|
||||
}
|
||||
bitset[i] = false;
|
||||
}
|
||||
|
||||
// Test with multiple bits set.
|
||||
//
|
||||
// This creates bits pattern with every |i|th bit set and checks the result
|
||||
// of calling FindNext/FindPrev at and around each set bit.
|
||||
for (size_t i = 3; i < size; i += 5) {
|
||||
bitset.ResetAll();
|
||||
for (size_t j = 0; j < size; j += i) {
|
||||
bitset[j] = true;
|
||||
}
|
||||
for (size_t j = 0; j < size; j += i) {
|
||||
// Test FindNext/FindPrev on the current bit.
|
||||
MOZ_RELEASE_ASSERT(bitset[j]);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(j) == j);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(j) == j);
|
||||
// Test FindNext/FindPrev on the previous bit.
|
||||
if (j != 0) {
|
||||
MOZ_RELEASE_ASSERT(bitset[j - i]);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(j - 1) == j);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(j - 1) == j - i);
|
||||
}
|
||||
// Test FindNext/FindPrev on the next bit.
|
||||
if (j + i < size) {
|
||||
MOZ_RELEASE_ASSERT(bitset[j + i]);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindNext(j + 1) == j + i);
|
||||
MOZ_RELEASE_ASSERT(bitset.FindPrev(j + 1) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void runTests() {
|
||||
testLength();
|
||||
testConstruct();
|
||||
testSetBit();
|
||||
testFindBits();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user