tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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