Bug 1733963 - Part 1: Make BitSet work as storage for EnumSet. r=glandium

Differential Revision: https://phabricator.services.mozilla.com/D127447
This commit is contained in:
Andreas Farre 2021-11-09 12:34:51 +00:00
Родитель 8f4fabfec4
Коммит f31ef6faa9
3 изменённых файлов: 113 добавлений и 23 удалений

Просмотреть файл

@ -29,7 +29,7 @@ class Array {
using ElementType = T;
static constexpr size_t Length = _Length;
Array() = default;
constexpr Array() = default;
template <typename... Args>
MOZ_IMPLICIT constexpr Array(Args&&... aArgs)

Просмотреть файл

@ -8,6 +8,8 @@
#define mozilla_BitSet_h
#include "mozilla/Array.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Span.h"
@ -28,10 +30,18 @@ class BitSet {
private:
static constexpr size_t kBitsPerWord = 8 * sizeof(Word);
static constexpr size_t kNumWords = (N + kBitsPerWord - 1) / kBitsPerWord;
static constexpr size_t kPaddingBits = (kNumWords * kBitsPerWord) - N;
static constexpr Word kPaddingMask = Word(-1) >> kPaddingBits;
// The zeroth bit in the bitset is the least significant bit of mStorage[0].
Array<Word, kNumWords> mStorage;
constexpr void ResetPaddingBits() {
if constexpr (kPaddingBits != 0) {
mStorage[kNumWords - 1] &= kPaddingMask;
}
}
public:
class Reference {
public:
@ -52,7 +62,7 @@ class BitSet {
size_t mPos;
};
BitSet() { ResetAll(); }
constexpr BitSet() : mStorage() {}
BitSet(const BitSet& aOther) { *this = aOther; }
@ -72,6 +82,17 @@ class BitSet {
return mStorage[aPos / kBitsPerWord] & (Word(1) << (aPos % kBitsPerWord));
}
constexpr bool IsEmpty() const {
for (const Word& word : mStorage) {
if (word) {
return false;
}
}
return true;
}
explicit constexpr operator bool() { return !IsEmpty(); }
constexpr bool operator[](size_t aPos) const { return Test(aPos); }
Reference operator[](size_t aPos) {
@ -92,17 +113,58 @@ class BitSet {
return *this;
}
BitSet operator~() const {
BitSet result = *this;
result.Flip();
return result;
}
BitSet& operator&=(const BitSet<N, Word>& aOther) {
for (size_t i = 0; i < ArrayLength(mStorage); i++) {
mStorage[i] &= aOther.mStorage[i];
}
return *this;
}
BitSet operator&(const BitSet<N, Word>& aOther) const {
BitSet result = *this;
result &= aOther;
return result;
}
bool operator==(const BitSet<N, Word>& aOther) const {
return mStorage == aOther.mStorage;
}
size_t Count() const {
size_t count = 0;
for (const Word& word : mStorage) {
if constexpr (kBitsPerWord > 32) {
count += CountPopulation64(word);
} else {
count += CountPopulation32(word);
}
}
return count;
}
// Set all bits to false.
void ResetAll() { PodArrayZero(mStorage); }
// Set all bits to true.
void SetAll() {
memset(mStorage.begin(), 0xff, kNumWords * sizeof(Word));
constexpr size_t paddingBits = (kNumWords * kBitsPerWord) - N;
constexpr Word paddingMask = Word(-1) >> paddingBits;
if constexpr (paddingBits != 0) {
mStorage[kNumWords - 1] &= paddingMask;
ResetPaddingBits();
}
void Flip() {
for (Word& word : mStorage) {
word = ~word;
}
ResetPaddingBits();
}
Span<Word> Storage() { return mStorage; }

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/MathAlgorithms.h"
#include <initializer_list>
#include <type_traits>
@ -22,15 +23,16 @@ namespace mozilla {
/**
* EnumSet<T, U> is a set of values defined by an enumeration. It is implemented
* using a bit mask with the size of U for each value. It works both for enum
* and enum class types.
* and enum class types. EnumSet also works with U being a BitSet.
*/
template <typename T, typename Serialized = typename std::make_unsigned<
typename std::underlying_type<T>::type>::type>
class EnumSet {
public:
typedef T valueType;
using valueType = T;
using serializedType = Serialized;
constexpr EnumSet() : mBitField(0) {}
constexpr EnumSet() : mBitField() {}
constexpr MOZ_IMPLICIT EnumSet(T aEnum) : mBitField(bitFor(aEnum)) {}
@ -44,7 +46,7 @@ class EnumSet {
: mBitField(bitFor(aEnum1) | bitFor(aEnum2) | bitFor(aEnum3) |
bitFor(aEnum4)) {}
constexpr MOZ_IMPLICIT EnumSet(std::initializer_list<T> list) : mBitField(0) {
constexpr MOZ_IMPLICIT EnumSet(std::initializer_list<T> list) : mBitField() {
for (auto value : list) {
(*this) += value;
}
@ -133,7 +135,7 @@ class EnumSet {
*/
void clear() {
incVersion();
mBitField = 0;
mBitField = Serialized();
}
/**
@ -180,7 +182,9 @@ class EnumSet {
/**
* Test is an element is contained in the set.
*/
bool contains(T aEnum) const { return mBitField & bitFor(aEnum); }
bool contains(T aEnum) const {
return static_cast<bool>(mBitField & bitFor(aEnum));
}
/**
* Test if a set is contained in the set.
@ -192,17 +196,25 @@ class EnumSet {
/**
* Return the number of elements in the set.
*/
uint8_t size() const {
uint8_t count = 0;
for (Serialized bitField = mBitField; bitField; bitField >>= 1) {
if (bitField & 1) {
count++;
size_t size() const {
if constexpr (std::is_unsigned_v<Serialized>) {
if constexpr (kMaxBits > 32) {
return CountPopulation64(mBitField);
} else {
return CountPopulation32(mBitField);
}
} else {
return mBitField.Count();
}
return count;
}
bool isEmpty() const { return mBitField == 0; }
bool isEmpty() const {
if constexpr (std::is_unsigned_v<Serialized>) {
return mBitField == 0;
} else {
return mBitField.IsEmpty();
}
}
Serialized serialize() const { return mBitField; }
@ -230,7 +242,9 @@ class EnumSet {
mVersion = mSet->mVersion;
#endif
MOZ_ASSERT(aPos <= kMaxBits);
if (aPos != kMaxBits && !mSet->contains(T(mPos))) ++*this;
if (aPos != kMaxBits && !mSet->contains(T(mPos))) {
++*this;
}
}
ConstIterator(const ConstIterator& aOther)
@ -287,9 +301,15 @@ class EnumSet {
private:
constexpr static Serialized bitFor(T aEnum) {
auto bitNumber = static_cast<Serialized>(aEnum);
auto bitNumber = static_cast<size_t>(aEnum);
MOZ_DIAGNOSTIC_ASSERT(bitNumber < kMaxBits);
return static_cast<Serialized>(Serialized{1} << bitNumber);
if constexpr (std::is_unsigned_v<Serialized>) {
return static_cast<Serialized>(Serialized{1} << bitNumber);
} else {
Serialized bitField;
bitField[bitNumber] = true;
return bitField;
}
}
constexpr void incVersion() {
@ -298,7 +318,15 @@ class EnumSet {
#endif
}
static const size_t kMaxBits = sizeof(Serialized) * 8;
constexpr size_t MaxBits() const {
if constexpr (std::is_unsigned_v<Serialized>) {
return sizeof(Serialized) * 8;
} else {
return mBitField.Size();
}
};
static constexpr size_t kMaxBits = EnumSet().MaxBits();
Serialized mBitField;