BitSet.h (7064B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_BitSet_h 8 #define mozilla_BitSet_h 9 10 #include "mozilla/Array.h" 11 #include "mozilla/MathAlgorithms.h" 12 #include "mozilla/Span.h" 13 14 #include <climits> 15 #include <cstddef> 16 #include <cstdint> 17 #include <type_traits> 18 19 namespace mozilla { 20 21 enum MemoryOrdering : uint8_t; 22 template <typename T, MemoryOrdering Order, typename Enable> 23 class Atomic; 24 25 namespace detail { 26 27 template <typename T> 28 struct UnwrapMaybeAtomic { 29 using Type = T; 30 }; 31 template <typename T, MemoryOrdering Order, typename Enable> 32 struct UnwrapMaybeAtomic<mozilla::Atomic<T, Order, Enable>> { 33 using Type = T; 34 }; 35 36 } // namespace detail 37 38 /** 39 * An object like std::bitset but which provides access to the underlying 40 * storage. 41 * 42 * The type |StorageType| must be an unsigned integer or a mozilla::Atomic 43 * wrapping an unsigned integer. Use of atomic types makes word access atomic, 44 * but does not make operations that operate on the whole bitset atomic. 45 * 46 * The limited API is due to expedience only; feel free to flesh out any 47 * std::bitset-like members. 48 */ 49 template <size_t N, typename StorageType = size_t> 50 class BitSet { 51 public: 52 using Word = typename detail::UnwrapMaybeAtomic<StorageType>::Type; 53 static_assert(sizeof(Word) == sizeof(StorageType)); 54 static_assert( 55 std::is_unsigned_v<Word>, 56 "StorageType must be an unsigned integral type, or equivalent Atomic"); 57 static_assert(N != 0); 58 59 private: 60 static constexpr size_t kBitsPerWord = 8 * sizeof(Word); 61 static constexpr size_t kNumWords = (N + kBitsPerWord - 1) / kBitsPerWord; 62 static constexpr size_t kPaddingBits = (kNumWords * kBitsPerWord) - N; 63 static constexpr Word kPaddingMask = Word(-1) >> kPaddingBits; 64 65 // The zeroth bit in the bitset is the least significant bit of mStorage[0]. 66 Array<StorageType, kNumWords> mStorage; 67 68 constexpr void ResetPaddingBits() { 69 if constexpr (kPaddingBits != 0) { 70 mStorage[kNumWords - 1] &= kPaddingMask; 71 } 72 } 73 74 public: 75 class Reference { 76 public: 77 Reference(BitSet<N, StorageType>& aBitSet, size_t aPos) 78 : mBitSet(aBitSet), mPos(aPos) {} 79 80 Reference& operator=(bool aValue) { 81 auto bit = Word(1) << (mPos % kBitsPerWord); 82 auto& word = mBitSet.mStorage[mPos / kBitsPerWord]; 83 if (aValue) { 84 word |= bit; 85 } else { 86 word &= ~bit; 87 } 88 return *this; 89 } 90 91 MOZ_IMPLICIT operator bool() const { return mBitSet.test(mPos); } 92 93 private: 94 BitSet<N, StorageType>& mBitSet; 95 size_t mPos; 96 }; 97 98 constexpr BitSet() : mStorage() {} 99 100 BitSet(const BitSet& aOther) { *this = aOther; } 101 102 BitSet& operator=(const BitSet& aOther) { 103 for (size_t i = 0; i < std::size(mStorage); i++) { 104 mStorage[i] = Word(aOther.mStorage[i]); 105 } 106 return *this; 107 } 108 109 explicit BitSet(Span<StorageType, kNumWords> aStorage) { 110 for (size_t i = 0; i < std::size(mStorage); i++) { 111 mStorage[i] = Word(aStorage[i]); 112 } 113 } 114 115 static constexpr size_t size() { return N; } 116 117 constexpr bool test(size_t aPos) const { 118 MOZ_ASSERT(aPos < N); 119 return mStorage[aPos / kBitsPerWord] & (Word(1) << (aPos % kBitsPerWord)); 120 } 121 122 constexpr bool IsEmpty() const { 123 for (const StorageType& word : mStorage) { 124 if (word) { 125 return false; 126 } 127 } 128 return true; 129 } 130 131 explicit constexpr operator bool() { return !IsEmpty(); } 132 133 constexpr bool operator[](size_t aPos) const { return test(aPos); } 134 135 Reference operator[](size_t aPos) { 136 MOZ_ASSERT(aPos < N); 137 return {*this, aPos}; 138 } 139 140 BitSet operator|(const BitSet<N, StorageType>& aOther) { 141 BitSet result = *this; 142 result |= aOther; 143 return result; 144 } 145 146 BitSet& operator|=(const BitSet<N, StorageType>& aOther) { 147 for (size_t i = 0; i < std::size(mStorage); i++) { 148 mStorage[i] |= aOther.mStorage[i]; 149 } 150 return *this; 151 } 152 153 BitSet operator~() const { 154 BitSet result = *this; 155 result.Flip(); 156 return result; 157 } 158 159 BitSet& operator&=(const BitSet<N, StorageType>& aOther) { 160 for (size_t i = 0; i < std::size(mStorage); i++) { 161 mStorage[i] &= aOther.mStorage[i]; 162 } 163 return *this; 164 } 165 166 BitSet operator&(const BitSet<N, StorageType>& aOther) const { 167 BitSet result = *this; 168 result &= aOther; 169 return result; 170 } 171 172 bool operator==(const BitSet<N, StorageType>& aOther) const { 173 return mStorage == aOther.mStorage; 174 } 175 bool operator!=(const BitSet<N, StorageType>& aOther) const { 176 return !(*this == aOther); 177 } 178 179 size_t Count() const { 180 size_t count = 0; 181 182 for (const Word word : mStorage) { 183 if constexpr (kBitsPerWord > 32) { 184 count += CountPopulation64(word); 185 } else { 186 count += CountPopulation32(word); 187 } 188 } 189 190 return count; 191 } 192 193 // Set all bits to false. 194 void ResetAll() { 195 for (StorageType& word : mStorage) { 196 word = Word(0); 197 } 198 } 199 200 // Set all bits to true. 201 void SetAll() { 202 for (StorageType& word : mStorage) { 203 word = ~Word(0); 204 } 205 ResetPaddingBits(); 206 } 207 208 void Flip() { 209 for (StorageType& word : mStorage) { 210 word = ~word; 211 } 212 213 ResetPaddingBits(); 214 } 215 216 // Return the position of the first bit set, or SIZE_MAX if none. 217 size_t FindFirst() const { return FindNext(0); } 218 219 // Return the position of the next bit set starting from |aFromPos| inclusive, 220 // or SIZE_MAX if none. 221 size_t FindNext(size_t aFromPos) const { 222 MOZ_ASSERT(aFromPos < N); 223 size_t wordIndex = aFromPos / kBitsPerWord; 224 size_t bitIndex = aFromPos % kBitsPerWord; 225 226 Word word = mStorage[wordIndex]; 227 // Mask word containing |aFromPos|. 228 word &= (Word(-1) << bitIndex); 229 while (word == 0) { 230 wordIndex++; 231 if (wordIndex == kNumWords) { 232 return SIZE_MAX; 233 } 234 word = mStorage[wordIndex]; 235 } 236 237 uint_fast8_t pos = CountTrailingZeroes(word); 238 return wordIndex * kBitsPerWord + pos; 239 } 240 241 size_t FindLast() const { return FindPrev(size() - 1); } 242 243 // Return the position of the previous bit set starting from |aFromPos| 244 // inclusive, or SIZE_MAX if none. 245 size_t FindPrev(size_t aFromPos) const { 246 MOZ_ASSERT(aFromPos < N); 247 size_t wordIndex = aFromPos / kBitsPerWord; 248 size_t bitIndex = aFromPos % kBitsPerWord; 249 250 Word word = mStorage[wordIndex]; 251 // Mask word containing |aFromPos|. 252 word &= Word(-1) >> (kBitsPerWord - 1 - bitIndex); 253 while (word == 0) { 254 if (wordIndex == 0) { 255 return SIZE_MAX; 256 } 257 wordIndex--; 258 word = mStorage[wordIndex]; 259 } 260 261 uint_fast8_t pos = FindMostSignificantBit(word); 262 return wordIndex * kBitsPerWord + pos; 263 } 264 265 Span<StorageType> Storage() { return mStorage; } 266 267 Span<const StorageType> Storage() const { return mStorage; } 268 }; 269 270 } // namespace mozilla 271 272 #endif // mozilla_BitSet_h