/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* A set abstraction for enumeration values. */ #ifndef mozilla_EnumSet_h #define mozilla_EnumSet_h #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include <initializer_list> #include <stdint.h> namespace mozilla { /** * EnumSet<T> is a set of values defined by an enumeration. It is implemented * using a 32 bit mask for each value so it will only work for enums with an int * representation less than 32. It works both for enum and enum class types. */ template<typename T> class EnumSet { public: EnumSet() : mBitField(0) { initVersion(); } MOZ_IMPLICIT EnumSet(T aEnum) : mBitField(bitFor(aEnum)) { } EnumSet(T aEnum1, T aEnum2) : mBitField(bitFor(aEnum1) | bitFor(aEnum2)) { initVersion(); } EnumSet(T aEnum1, T aEnum2, T aEnum3) : mBitField(bitFor(aEnum1) | bitFor(aEnum2) | bitFor(aEnum3)) { initVersion(); } EnumSet(T aEnum1, T aEnum2, T aEnum3, T aEnum4) : mBitField(bitFor(aEnum1) | bitFor(aEnum2) | bitFor(aEnum3) | bitFor(aEnum4)) { initVersion(); } MOZ_IMPLICIT EnumSet(std::initializer_list<T> list) : mBitField(0) { for (auto value : list) { (*this) += value; } initVersion(); } EnumSet(const EnumSet& aEnumSet) : mBitField(aEnumSet.mBitField) { initVersion(); } /** * Add an element */ void operator+=(T aEnum) { incVersion(); mBitField |= bitFor(aEnum); } /** * Add an element */ EnumSet<T> operator+(T aEnum) const { EnumSet<T> result(*this); result += aEnum; return result; } /** * Union */ void operator+=(const EnumSet<T> aEnumSet) { incVersion(); mBitField |= aEnumSet.mBitField; } /** * Union */ EnumSet<T> operator+(const EnumSet<T> aEnumSet) const { EnumSet<T> result(*this); result += aEnumSet; return result; } /** * Remove an element */ void operator-=(T aEnum) { incVersion(); mBitField &= ~(bitFor(aEnum)); } /** * Remove an element */ EnumSet<T> operator-(T aEnum) const { EnumSet<T> result(*this); result -= aEnum; return result; } /** * Remove a set of elements */ void operator-=(const EnumSet<T> aEnumSet) { incVersion(); mBitField &= ~(aEnumSet.mBitField); } /** * Remove a set of elements */ EnumSet<T> operator-(const EnumSet<T> aEnumSet) const { EnumSet<T> result(*this); result -= aEnumSet; return result; } /** * Clear */ void clear() { incVersion(); mBitField = 0; } /** * Intersection */ void operator&=(const EnumSet<T> aEnumSet) { incVersion(); mBitField &= aEnumSet.mBitField; } /** * Intersection */ EnumSet<T> operator&(const EnumSet<T> aEnumSet) const { EnumSet<T> result(*this); result &= aEnumSet; return result; } /** * Equality */ bool operator==(const EnumSet<T> aEnumSet) const { return mBitField == aEnumSet.mBitField; } /** * Test is an element is contained in the set. */ bool contains(T aEnum) const { return mBitField & bitFor(aEnum); } /** * Return the number of elements in the set. */ uint8_t size() const { uint8_t count = 0; for (uint32_t bitField = mBitField; bitField; bitField >>= 1) { if (bitField & 1) { count++; } } return count; } bool isEmpty() const { return mBitField == 0; } uint32_t serialize() const { return mBitField; } void deserialize(uint32_t aValue) { incVersion(); mBitField = aValue; } class ConstIterator { const EnumSet<T>* mSet; uint32_t mPos; #ifdef DEBUG uint64_t mVersion; #endif void checkVersion() { // Check that the set has not been modified while being iterated. MOZ_ASSERT_IF(mSet, mSet->mVersion == mVersion); } public: ConstIterator(const EnumSet<T>& aSet, uint32_t aPos) : mSet(&aSet), mPos(aPos) { #ifdef DEBUG mVersion = mSet->mVersion; #endif MOZ_ASSERT(aPos <= kMaxBits); if (aPos != kMaxBits && !mSet->contains(T(mPos))) ++*this; } ConstIterator(const ConstIterator& aOther) : mSet(aOther.mSet), mPos(aOther.mPos) { #ifdef DEBUG mVersion = aOther.mVersion; checkVersion(); #endif } ConstIterator(ConstIterator&& aOther) : mSet(aOther.mSet), mPos(aOther.mPos) { #ifdef DEBUG mVersion = aOther.mVersion; checkVersion(); #endif aOther.mSet = nullptr; } ~ConstIterator() { checkVersion(); } bool operator==(const ConstIterator& other) { MOZ_ASSERT(mSet == other.mSet); checkVersion(); return mPos == other.mPos; } bool operator!=(const ConstIterator& other) { return !(*this == other); } T operator*() { MOZ_ASSERT(mSet); MOZ_ASSERT(mPos < kMaxBits); MOZ_ASSERT(mSet->contains(T(mPos))); checkVersion(); return T(mPos); } ConstIterator& operator++() { MOZ_ASSERT(mSet); MOZ_ASSERT(mPos < kMaxBits); checkVersion(); do { mPos++; } while (mPos < kMaxBits && !mSet->contains(T(mPos))); return *this; } }; ConstIterator begin() const { return ConstIterator(*this, 0); } ConstIterator end() const { return ConstIterator(*this, kMaxBits); } private: static uint32_t bitFor(T aEnum) { uint32_t bitNumber = (uint32_t)aEnum; MOZ_ASSERT(bitNumber < kMaxBits); return 1U << bitNumber; } void initVersion() { #ifdef DEBUG mVersion = 0; #endif } void incVersion() { #ifdef DEBUG mVersion++; #endif } static const size_t kMaxBits = 32; uint32_t mBitField; #ifdef DEBUG uint64_t mVersion = 0; #endif }; } // namespace mozilla #endif /* mozilla_EnumSet_h_*/