1 // Copyright (c) 2016 Google Inc. 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 SOURCE_ENUM_SET_H_ 16 #define SOURCE_ENUM_SET_H_ 17 18 #include <cstdint> 19 #include <functional> 20 #include <memory> 21 #include <set> 22 #include <utility> 23 24 #include "source/latest_version_spirv_header.h" 25 #include "source/util/make_unique.h" 26 27 namespace spvtools { 28 29 // A set of values of a 32-bit enum type. 30 // It is fast and compact for the common case, where enum values 31 // are at most 63. But it can represent enums with larger values, 32 // as may appear in extensions. 33 template <typename EnumType> 34 class EnumSet { 35 private: 36 // The ForEach method will call the functor on enum values in 37 // enum value order (lowest to highest). To make that easier, use 38 // an ordered set for the overflow values. 39 using OverflowSetType = std::set<uint32_t>; 40 41 public: 42 // Construct an empty set. EnumSet()43 EnumSet() {} 44 // Construct an set with just the given enum value. EnumSet(EnumType c)45 explicit EnumSet(EnumType c) { Add(c); } 46 // Construct an set from an initializer list of enum values. EnumSet(std::initializer_list<EnumType> cs)47 EnumSet(std::initializer_list<EnumType> cs) { 48 for (auto c : cs) Add(c); 49 } EnumSet(uint32_t count,const EnumType * ptr)50 EnumSet(uint32_t count, const EnumType* ptr) { 51 for (uint32_t i = 0; i < count; ++i) Add(ptr[i]); 52 } 53 // Copy constructor. EnumSet(const EnumSet & other)54 EnumSet(const EnumSet& other) { *this = other; } 55 // Move constructor. The moved-from set is emptied. EnumSet(EnumSet && other)56 EnumSet(EnumSet&& other) { 57 mask_ = other.mask_; 58 overflow_ = std::move(other.overflow_); 59 other.mask_ = 0; 60 other.overflow_.reset(nullptr); 61 } 62 // Assignment operator. 63 EnumSet& operator=(const EnumSet& other) { 64 if (&other != this) { 65 mask_ = other.mask_; 66 overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_) 67 : nullptr); 68 } 69 return *this; 70 } 71 72 // Adds the given enum value to the set. This has no effect if the 73 // enum value is already in the set. Add(EnumType c)74 void Add(EnumType c) { AddWord(ToWord(c)); } 75 76 // Returns true if this enum value is in the set. Contains(EnumType c)77 bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); } 78 79 // Applies f to each enum in the set, in order from smallest enum 80 // value to largest. ForEach(std::function<void (EnumType)> f)81 void ForEach(std::function<void(EnumType)> f) const { 82 for (uint32_t i = 0; i < 64; ++i) { 83 if (mask_ & AsMask(i)) f(static_cast<EnumType>(i)); 84 } 85 if (overflow_) { 86 for (uint32_t c : *overflow_) f(static_cast<EnumType>(c)); 87 } 88 } 89 90 // Returns true if the set is empty. IsEmpty()91 bool IsEmpty() const { 92 if (mask_) return false; 93 if (overflow_ && !overflow_->empty()) return false; 94 return true; 95 } 96 97 // Returns true if the set contains ANY of the elements of |in_set|, 98 // or if |in_set| is empty. HasAnyOf(const EnumSet<EnumType> & in_set)99 bool HasAnyOf(const EnumSet<EnumType>& in_set) const { 100 if (in_set.IsEmpty()) return true; 101 102 if (mask_ & in_set.mask_) return true; 103 104 if (!overflow_ || !in_set.overflow_) return false; 105 106 for (uint32_t item : *in_set.overflow_) { 107 if (overflow_->find(item) != overflow_->end()) return true; 108 } 109 110 return false; 111 } 112 113 private: 114 // Adds the given enum value (as a 32-bit word) to the set. This has no 115 // effect if the enum value is already in the set. AddWord(uint32_t word)116 void AddWord(uint32_t word) { 117 if (auto new_bits = AsMask(word)) { 118 mask_ |= new_bits; 119 } else { 120 Overflow().insert(word); 121 } 122 } 123 124 // Returns true if the enum represented as a 32-bit word is in the set. ContainsWord(uint32_t word)125 bool ContainsWord(uint32_t word) const { 126 // We shouldn't call Overflow() since this is a const method. 127 if (auto bits = AsMask(word)) { 128 return (mask_ & bits) != 0; 129 } else if (auto overflow = overflow_.get()) { 130 return overflow->find(word) != overflow->end(); 131 } 132 // The word is large, but the set doesn't have large members, so 133 // it doesn't have an overflow set. 134 return false; 135 } 136 137 // Returns the enum value as a uint32_t. ToWord(EnumType value)138 uint32_t ToWord(EnumType value) const { 139 static_assert(sizeof(EnumType) <= sizeof(uint32_t), 140 "EnumType must statically castable to uint32_t"); 141 return static_cast<uint32_t>(value); 142 } 143 144 // Determines whether the given enum value can be represented 145 // as a bit in a uint64_t mask. If so, then returns that mask bit. 146 // Otherwise, returns 0. AsMask(uint32_t word)147 uint64_t AsMask(uint32_t word) const { 148 if (word > 63) return 0; 149 return uint64_t(1) << word; 150 } 151 152 // Ensures that overflow_set_ references a set. A new empty set is 153 // allocated if one doesn't exist yet. Returns overflow_set_. Overflow()154 OverflowSetType& Overflow() { 155 if (overflow_.get() == nullptr) { 156 overflow_ = MakeUnique<OverflowSetType>(); 157 } 158 return *overflow_; 159 } 160 161 // Enums with values up to 63 are stored as bits in this mask. 162 uint64_t mask_ = 0; 163 // Enums with values larger than 63 are stored in this set. 164 // This set should normally be empty or very small. 165 std::unique_ptr<OverflowSetType> overflow_ = {}; 166 }; 167 168 // A set of SpvCapability, optimized for small capability values. 169 using CapabilitySet = EnumSet<SpvCapability>; 170 171 } // namespace spvtools 172 173 #endif // SOURCE_ENUM_SET_H_ 174