// Copyright 2021 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef SRC_TINT_UTILS_ENUM_SET_H_ #define SRC_TINT_UTILS_ENUM_SET_H_ #include #include #include #include #include namespace tint::utils { /// EnumSet is a set of enum values. /// @note As the EnumSet is backed by a single uint64_t value, it can only hold /// enum values in the range [0 .. 63]. template struct EnumSet { public: /// Enum is the enum type this EnumSet wraps using Enum = ENUM; /// Constructor. Initializes the EnumSet with zero. constexpr EnumSet() = default; /// Copy constructor. /// @param s the set to copy constexpr EnumSet(const EnumSet& s) = default; /// Constructor. Initializes the EnumSet with the given values. /// @param values the enumerator values to construct the set with template explicit constexpr EnumSet(VALUES... values) : set(Union(values...)) {} /// Copy assignment operator. /// @param set the set to assign to this set /// @return this set so calls can be chained inline EnumSet& operator=(const EnumSet& set) = default; /// Copy assignment operator. /// @param e the enum value /// @return this set so calls can be chained inline EnumSet& operator=(Enum e) { return *this = EnumSet{e}; } /// Adds all the given values to this set /// @param values the values to add /// @return this set so calls can be chained template inline EnumSet& Add(VALUES... values) { return Add(EnumSet(std::forward(values)...)); } /// Removes all the given values from this set /// @param values the values to remove /// @return this set so calls can be chained template inline EnumSet& Remove(VALUES... values) { return Remove(EnumSet(std::forward(values)...)); } /// Adds all of s to this set /// @param s the enum value /// @return this set so calls can be chained inline EnumSet& Add(EnumSet s) { return (*this = *this + s); } /// Removes all of s from this set /// @param s the enum value /// @return this set so calls can be chained inline EnumSet& Remove(EnumSet s) { return (*this = *this - s); } /// @param e the enum value /// @returns a copy of this set with e added inline EnumSet operator+(Enum e) const { EnumSet out; out.set = set | Bit(e); return out; } /// @param e the enum value /// @returns a copy of this set with e removed inline EnumSet operator-(Enum e) const { EnumSet out; out.set = set & ~Bit(e); return out; } /// @param s the other set /// @returns the union of this set with s (this ∪ rhs) inline EnumSet operator+(EnumSet s) const { EnumSet out; out.set = set | s.set; return out; } /// @param s the other set /// @returns the set of entries found in this but not in s (this \ s) inline EnumSet operator-(EnumSet s) const { EnumSet out; out.set = set & ~s.set; return out; } /// @param s the other set /// @returns the intersection of this set with s (this ∩ rhs) inline EnumSet operator&(EnumSet s) const { EnumSet out; out.set = set & s.set; return out; } /// @param e the enum value /// @return true if the set contains `e` inline bool Contains(Enum e) const { return (set & Bit(e)) != 0; } /// @return true if the set is empty inline bool Empty() const { return set == 0; } /// Equality operator /// @param rhs the other EnumSet to compare this to /// @return true if this EnumSet is equal to rhs inline bool operator==(EnumSet rhs) const { return set == rhs.set; } /// Inequality operator /// @param rhs the other EnumSet to compare this to /// @return true if this EnumSet is not equal to rhs inline bool operator!=(EnumSet rhs) const { return set != rhs.set; } /// Equality operator /// @param rhs the enum to compare this to /// @return true if this EnumSet only contains `rhs` inline bool operator==(Enum rhs) const { return set == Bit(rhs); } /// Inequality operator /// @param rhs the enum to compare this to /// @return false if this EnumSet only contains `rhs` inline bool operator!=(Enum rhs) const { return set != Bit(rhs); } /// @return the underlying value for the EnumSet inline uint64_t Value() const { return set; } /// Iterator provides read-only, unidirectional iterator over the enums of an /// EnumSet. class Iterator { static constexpr int8_t kEnd = 63; Iterator(uint64_t s, int8_t b) : set(s), pos(b) {} /// Make the constructor accessible to the EnumSet. friend struct EnumSet; public: /// @return the Enum value at this point in the iterator Enum operator*() const { return static_cast(pos); } /// Increments the iterator /// @returns this iterator Iterator& operator++() { while (pos < kEnd) { pos++; if (set & (static_cast(1) << static_cast(pos))) { break; } } return *this; } /// Equality operator /// @param rhs the Iterator to compare this to /// @return true if the two iterators are equal bool operator==(const Iterator& rhs) const { return set == rhs.set && pos == rhs.pos; } /// Inequality operator /// @param rhs the Iterator to compare this to /// @return true if the two iterators are different bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } private: const uint64_t set; int8_t pos; }; /// @returns an read-only iterator to the beginning of the set Iterator begin() { auto it = Iterator{set, -1}; ++it; // Move to first set bit return it; } /// @returns an iterator to the beginning of the set Iterator end() { return Iterator{set, Iterator::kEnd}; } private: static constexpr uint64_t Bit(Enum value) { return static_cast(1) << static_cast(value); } static constexpr uint64_t Union() { return 0; } template static constexpr uint64_t Union(FIRST first, VALUES... values) { return Bit(first) | Union(values...); } uint64_t set = 0; }; /// Writes the EnumSet to the std::ostream. /// @param out the std::ostream to write to /// @param set the EnumSet to write /// @returns out so calls can be chained template inline std::ostream& operator<<(std::ostream& out, EnumSet set) { out << "{"; bool first = true; for (auto e : set) { if (!first) { out << ", "; } first = false; out << e; } return out << "}"; } } // namespace tint::utils namespace std { /// Custom std::hash specialization for tint::utils::EnumSet template class hash> { public: /// @param e the EnumSet to create a hash for /// @return the hash value inline std::size_t operator()(const tint::utils::EnumSet& e) const { return std::hash()(e.Value()); } }; } // namespace std #endif // SRC_TINT_UTILS_ENUM_SET_H_