diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index 6e83f9412f61..9ca056c92ec2 100644 --- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -65,7 +65,7 @@ namespace mozilla { * the modes we provide below, or not relevant for the CPUs we support * in Gecko. These three modes are confusing enough as it is! */ -enum MemoryOrdering { +enum MemoryOrdering : uint8_t { /* * Relaxed ordering is the simplest memory ordering: none at all. * When the result of a write is observed, nothing may be inferred diff --git a/mfbt/BitSet.h b/mfbt/BitSet.h index 7d8354e44527..9e2264a6e30c 100644 --- a/mfbt/BitSet.h +++ b/mfbt/BitSet.h @@ -8,24 +8,47 @@ #define mozilla_BitSet_h #include "mozilla/Array.h" -#include "mozilla/ArrayUtils.h" #include "mozilla/MathAlgorithms.h" -#include "mozilla/PodOperations.h" #include "mozilla/Span.h" namespace mozilla { +enum MemoryOrdering : uint8_t; +template +class Atomic; + +namespace detail { + +template +struct UnwrapMaybeAtomic { + using Type = T; +}; +template +struct UnwrapMaybeAtomic> { + using Type = T; +}; + +} // namespace detail + /** * An object like std::bitset but which provides access to the underlying * storage. * + * The type |StorageType| must be an unsigned integer or a mozilla::Atomic + * wrapping an unsigned integer. Use of atomic types makes word access atomic, + * but does not make operations that operate on the whole bitset atomic. + * * The limited API is due to expedience only; feel free to flesh out any * std::bitset-like members. */ -template +template class BitSet { - static_assert(std::is_unsigned_v, - "The Word type must be an unsigned integral type"); + public: + using Word = typename detail::UnwrapMaybeAtomic::Type; + static_assert(sizeof(Word) == sizeof(StorageType)); + static_assert( + std::is_unsigned_v, + "StorageType must be an unsigned integral type, or equivalent Atomic"); static_assert(N != 0); private: @@ -35,7 +58,7 @@ class BitSet { static constexpr Word kPaddingMask = Word(-1) >> kPaddingBits; // The zeroth bit in the bitset is the least significant bit of mStorage[0]. - Array mStorage; + Array mStorage; constexpr void ResetPaddingBits() { if constexpr (kPaddingBits != 0) { @@ -46,20 +69,24 @@ class BitSet { public: class Reference { public: - Reference(BitSet& aBitSet, size_t aPos) + Reference(BitSet& aBitSet, size_t aPos) : mBitSet(aBitSet), mPos(aPos) {} Reference& operator=(bool aValue) { auto bit = Word(1) << (mPos % kBitsPerWord); auto& word = mBitSet.mStorage[mPos / kBitsPerWord]; - word = (word & ~bit) | (aValue ? bit : 0); + if (aValue) { + word |= bit; + } else { + word &= ~bit; + } return *this; } MOZ_IMPLICIT operator bool() const { return mBitSet.test(mPos); } private: - BitSet& mBitSet; + BitSet& mBitSet; size_t mPos; }; @@ -68,12 +95,16 @@ class BitSet { BitSet(const BitSet& aOther) { *this = aOther; } BitSet& operator=(const BitSet& aOther) { - PodCopy(mStorage.begin(), aOther.mStorage.begin(), kNumWords); + for (size_t i = 0; i < std::size(mStorage); i++) { + mStorage[i] = Word(aOther.mStorage[i]); + } return *this; } - explicit BitSet(Span aStorage) { - PodCopy(mStorage.begin(), aStorage.Elements(), kNumWords); + explicit BitSet(Span aStorage) { + for (size_t i = 0; i < std::size(mStorage); i++) { + mStorage[i] = Word(aStorage[i]); + } } static constexpr size_t size() { return N; } @@ -84,7 +115,7 @@ class BitSet { } constexpr bool IsEmpty() const { - for (const Word& word : mStorage) { + for (const StorageType& word : mStorage) { if (word) { return false; } @@ -101,13 +132,13 @@ class BitSet { return {*this, aPos}; } - BitSet operator|(const BitSet& aOther) { + BitSet operator|(const BitSet& aOther) { BitSet result = *this; result |= aOther; return result; } - BitSet& operator|=(const BitSet& aOther) { + BitSet& operator|=(const BitSet& aOther) { for (size_t i = 0; i < std::size(mStorage); i++) { mStorage[i] |= aOther.mStorage[i]; } @@ -120,27 +151,27 @@ class BitSet { return result; } - BitSet& operator&=(const BitSet& aOther) { + BitSet& operator&=(const BitSet& aOther) { for (size_t i = 0; i < std::size(mStorage); i++) { mStorage[i] &= aOther.mStorage[i]; } return *this; } - BitSet operator&(const BitSet& aOther) const { + BitSet operator&(const BitSet& aOther) const { BitSet result = *this; result &= aOther; return result; } - bool operator==(const BitSet& aOther) const { + bool operator==(const BitSet& aOther) const { return mStorage == aOther.mStorage; } size_t Count() const { size_t count = 0; - for (const Word& word : mStorage) { + for (const StorageType& word : mStorage) { if constexpr (kBitsPerWord > 32) { count += CountPopulation64(word); } else { @@ -152,16 +183,22 @@ class BitSet { } // Set all bits to false. - void ResetAll() { PodArrayZero(mStorage); } + void ResetAll() { + for (StorageType& word : mStorage) { + word = Word(0); + } + } // Set all bits to true. void SetAll() { - memset(mStorage.begin(), 0xff, kNumWords * sizeof(Word)); + for (StorageType& word : mStorage) { + word = ~Word(0); + } ResetPaddingBits(); } void Flip() { - for (Word& word : mStorage) { + for (StorageType& word : mStorage) { word = ~word; } @@ -217,9 +254,9 @@ class BitSet { return wordIndex * kBitsPerWord + pos; } - Span Storage() { return mStorage; } + Span Storage() { return mStorage; } - Span Storage() const { return mStorage; } + Span Storage() const { return mStorage; } }; } // namespace mozilla diff --git a/mfbt/tests/TestBitSet.cpp b/mfbt/tests/TestBitSet.cpp index 9c2f9d5d8efe..29e54d8e8fe0 100644 --- a/mfbt/tests/TestBitSet.cpp +++ b/mfbt/tests/TestBitSet.cpp @@ -5,8 +5,10 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" #include "mozilla/BitSet.h" +using mozilla::Atomic; using mozilla::BitSet; template @@ -14,9 +16,11 @@ class BitSetSuite { template using TestBitSet = BitSet; + using Word = typename TestBitSet<1>::Word; + static constexpr size_t kBitsPerWord = sizeof(Storage) * 8; - static constexpr Storage kAllBitsSet = ~Storage{0}; + static constexpr Word kAllBitsSet = ~Word{0}; public: void testLength() { @@ -28,7 +32,7 @@ class BitSetSuite { MOZ_RELEASE_ASSERT(TestBitSet().Storage().Length() == 2); } - void testConstruct() { + void testConstructAndAssign() { MOZ_RELEASE_ASSERT(TestBitSet<1>().Storage()[0] == 0); MOZ_RELEASE_ASSERT(TestBitSet().Storage()[0] == 0); MOZ_RELEASE_ASSERT(TestBitSet().Storage()[0] == 0); @@ -63,6 +67,18 @@ class BitSetSuite { kAllBitsSet); MOZ_RELEASE_ASSERT( TestBitSet(bitsetW1.Storage()).Storage()[1] == 1); + + TestBitSet<1> bitset1Copy; + bitset1Copy = bitset1; + TestBitSet bitsetWCopy; + bitsetWCopy = bitsetW; + TestBitSet bitsetW1Copy; + bitsetW1Copy = bitsetW1; + + MOZ_RELEASE_ASSERT(bitset1Copy.Storage()[0] == 1); + MOZ_RELEASE_ASSERT(bitsetWCopy.Storage()[0] == kAllBitsSet); + MOZ_RELEASE_ASSERT(bitsetW1Copy.Storage()[0] == kAllBitsSet); + MOZ_RELEASE_ASSERT(bitsetW1Copy.Storage()[1] == 1); } void testSetBit() { @@ -164,7 +180,7 @@ class BitSetSuite { void runTests() { testLength(); - testConstruct(); + testConstructAndAssign(); testSetBit(); testFindBits(); } @@ -174,6 +190,8 @@ int main() { BitSetSuite().runTests(); BitSetSuite().runTests(); BitSetSuite().runTests(); + BitSetSuite>().runTests(); + BitSetSuite>().runTests(); return 0; }