1 // Copyright 2021 The Tint Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SRC_UTILS_ENUM_SET_H_ 16 #define SRC_UTILS_ENUM_SET_H_ 17 18 #include <cstdint> 19 #include <functional> 20 #include <ostream> 21 #include <type_traits> 22 #include <utility> 23 24 namespace tint { 25 namespace utils { 26 27 /// EnumSet is a set of enum values. 28 /// @note As the EnumSet is backed by a single uint64_t value, it can only hold 29 /// enum values in the range [0 .. 63]. 30 template <typename ENUM> 31 struct EnumSet { 32 public: 33 /// Enum is the enum type this EnumSet wraps 34 using Enum = ENUM; 35 36 /// Constructor. Initializes the EnumSet with zero. 37 constexpr EnumSet() = default; 38 39 /// Copy constructor. 40 /// @param s the set to copy 41 constexpr EnumSet(const EnumSet& s) = default; 42 43 /// Constructor. Initializes the EnumSet with the given values. 44 /// @param values the enumerator values to construct the set with 45 template <typename... VALUES> EnumSetEnumSet46 explicit constexpr EnumSet(VALUES... values) : set(Union(values...)) {} 47 48 /// Copy assignment operator. 49 /// @param set the set to assign to this set 50 /// @return this set so calls can be chained 51 inline EnumSet& operator=(const EnumSet& set) = default; 52 53 /// Copy assignment operator. 54 /// @param e the enum value 55 /// @return this set so calls can be chained 56 inline EnumSet& operator=(Enum e) { return *this = EnumSet{e}; } 57 58 /// Adds all the given values to this set 59 /// @param values the values to add 60 /// @return this set so calls can be chained 61 template <typename... VALUES> AddEnumSet62 inline EnumSet& Add(VALUES... values) { 63 return Add(EnumSet(std::forward<VALUES>(values)...)); 64 } 65 66 /// Removes all the given values from this set 67 /// @param values the values to remove 68 /// @return this set so calls can be chained 69 template <typename... VALUES> RemoveEnumSet70 inline EnumSet& Remove(VALUES... values) { 71 return Remove(EnumSet(std::forward<VALUES>(values)...)); 72 } 73 74 /// Adds all of s to this set 75 /// @param s the enum value 76 /// @return this set so calls can be chained AddEnumSet77 inline EnumSet& Add(EnumSet s) { return (*this = *this + s); } 78 79 /// Removes all of s from this set 80 /// @param s the enum value 81 /// @return this set so calls can be chained RemoveEnumSet82 inline EnumSet& Remove(EnumSet s) { return (*this = *this - s); } 83 84 /// @param e the enum value 85 /// @returns a copy of this set with e added 86 inline EnumSet operator+(Enum e) const { 87 EnumSet out; 88 out.set = set | Bit(e); 89 return out; 90 } 91 92 /// @param e the enum value 93 /// @returns a copy of this set with e removed 94 inline EnumSet operator-(Enum e) const { 95 EnumSet out; 96 out.set = set & ~Bit(e); 97 return out; 98 } 99 100 /// @param s the other set 101 /// @returns the union of this set with s (this ∪ rhs) 102 inline EnumSet operator+(EnumSet s) const { 103 EnumSet out; 104 out.set = set | s.set; 105 return out; 106 } 107 108 /// @param s the other set 109 /// @returns the set of entries found in this but not in s (this \ s) 110 inline EnumSet operator-(EnumSet s) const { 111 EnumSet out; 112 out.set = set & ~s.set; 113 return out; 114 } 115 116 /// @param s the other set 117 /// @returns the intersection of this set with s (this ∩ rhs) 118 inline EnumSet operator&(EnumSet s) const { 119 EnumSet out; 120 out.set = set & s.set; 121 return out; 122 } 123 124 /// @param e the enum value 125 /// @return true if the set contains `e` ContainsEnumSet126 inline bool Contains(Enum e) const { return (set & Bit(e)) != 0; } 127 128 /// @return true if the set is empty EmptyEnumSet129 inline bool Empty() const { return set == 0; } 130 131 /// Equality operator 132 /// @param rhs the other EnumSet to compare this to 133 /// @return true if this EnumSet is equal to rhs 134 inline bool operator==(EnumSet rhs) const { return set == rhs.set; } 135 136 /// Inequality operator 137 /// @param rhs the other EnumSet to compare this to 138 /// @return true if this EnumSet is not equal to rhs 139 inline bool operator!=(EnumSet rhs) const { return set != rhs.set; } 140 141 /// Equality operator 142 /// @param rhs the enum to compare this to 143 /// @return true if this EnumSet only contains `rhs` 144 inline bool operator==(Enum rhs) const { return set == Bit(rhs); } 145 146 /// Inequality operator 147 /// @param rhs the enum to compare this to 148 /// @return false if this EnumSet only contains `rhs` 149 inline bool operator!=(Enum rhs) const { return set != Bit(rhs); } 150 151 /// @return the underlying value for the EnumSet ValueEnumSet152 inline uint64_t Value() const { return set; } 153 154 /// Iterator provides read-only, unidirectional iterator over the enums of an 155 /// EnumSet. 156 class Iterator { 157 static constexpr int8_t kEnd = 63; 158 IteratorEnumSet159 Iterator(uint64_t s, int8_t b) : set(s), pos(b) {} 160 161 /// Make the constructor accessible to the EnumSet. 162 friend struct EnumSet; 163 164 public: 165 /// @return the Enum value at this point in the iterator 166 Enum operator*() const { return static_cast<Enum>(pos); } 167 168 /// Increments the iterator 169 /// @returns this iterator 170 Iterator& operator++() { 171 while (pos < kEnd) { 172 pos++; 173 if (set & (static_cast<uint64_t>(1) << static_cast<uint64_t>(pos))) { 174 break; 175 } 176 } 177 return *this; 178 } 179 180 /// Equality operator 181 /// @param rhs the Iterator to compare this to 182 /// @return true if the two iterators are equal 183 bool operator==(const Iterator& rhs) const { 184 return set == rhs.set && pos == rhs.pos; 185 } 186 187 /// Inequality operator 188 /// @param rhs the Iterator to compare this to 189 /// @return true if the two iterators are different 190 bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } 191 192 private: 193 const uint64_t set; 194 int8_t pos; 195 }; 196 197 /// @returns an read-only iterator to the beginning of the set beginEnumSet198 Iterator begin() { 199 auto it = Iterator{set, -1}; 200 ++it; // Move to first set bit 201 return it; 202 } 203 204 /// @returns an iterator to the beginning of the set endEnumSet205 Iterator end() { return Iterator{set, Iterator::kEnd}; } 206 207 private: BitEnumSet208 static constexpr uint64_t Bit(Enum value) { 209 return static_cast<uint64_t>(1) << static_cast<uint64_t>(value); 210 } 211 UnionEnumSet212 static constexpr uint64_t Union() { return 0; } 213 214 template <typename FIRST, typename... VALUES> UnionEnumSet215 static constexpr uint64_t Union(FIRST first, VALUES... values) { 216 return Bit(first) | Union(values...); 217 } 218 219 uint64_t set = 0; 220 }; 221 222 /// Writes the EnumSet to the std::ostream. 223 /// @param out the std::ostream to write to 224 /// @param set the EnumSet to write 225 /// @returns out so calls can be chained 226 template <typename ENUM> 227 inline std::ostream& operator<<(std::ostream& out, EnumSet<ENUM> set) { 228 out << "{"; 229 bool first = true; 230 for (auto e : set) { 231 if (!first) { 232 out << ", "; 233 } 234 first = false; 235 out << e; 236 } 237 return out << "}"; 238 } 239 240 } // namespace utils 241 } // namespace tint 242 243 namespace std { 244 245 /// Custom std::hash specialization for tint::utils::EnumSet<T> 246 template <typename T> 247 class hash<tint::utils::EnumSet<T>> { 248 public: 249 /// @param e the EnumSet to create a hash for 250 /// @return the hash value operator()251 inline std::size_t operator()(const tint::utils::EnumSet<T>& e) const { 252 return std::hash<uint64_t>()(e.Value()); 253 } 254 }; 255 256 } // namespace std 257 258 #endif // SRC_UTILS_ENUM_SET_H_ 259