diff --git a/mfbt/BitSet.h b/mfbt/BitSet.h index 76d5672fac1d..7c1ac33efdd6 100644 --- a/mfbt/BitSet.h +++ b/mfbt/BitSet.h @@ -26,6 +26,7 @@ template class BitSet { static_assert(std::is_unsigned_v, "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 Storage() { return mStorage; } Span Storage() const { return mStorage; } diff --git a/mfbt/MathAlgorithms.h b/mfbt/MathAlgorithms.h index 66aa1b9f7124..eeaaca2eee11 100644 --- a/mfbt/MathAlgorithms.h +++ b/mfbt/MathAlgorithms.h @@ -318,28 +318,24 @@ inline uint_fast8_t CeilingLog2Size(size_t aValue) { return CeilingLog2(aValue); } -namespace detail { - -template -class FloorLog2; - +/** + * Compute the bit position of the most significant bit set in + * |aValue|. Requires that |aValue| is non-zero. + */ template -class FloorLog2 { - 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); + MOZ_ASSERT(aValue != 0); + // This casts to 32-bits + if constexpr (sizeof(T) <= 4) { + return 31u - CountLeadingZeroes32(aValue); } -}; - -template -class FloorLog2 { - 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 { */ template inline constexpr uint_fast8_t FloorLog2(const T aValue) { - return detail::FloorLog2::compute(aValue); + return FindMostSignificantBit(aValue | 1); } /** A FloorLog2 variant that accepts only size_t. */ diff --git a/mfbt/tests/TestBitSet.cpp b/mfbt/tests/TestBitSet.cpp index 2bd1923a1565..6eb38819b7b9 100644 --- a/mfbt/tests/TestBitSet.cpp +++ b/mfbt/tests/TestBitSet.cpp @@ -101,10 +101,72 @@ class BitSetSuite { } } + void testFindBits() { + TestBitSet 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(); } };