/src/spirv-tools/source/enum_set.h
Line | Count | Source |
1 | | // Copyright (c) 2023 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 | | #include <stddef.h> |
16 | | |
17 | | #include <algorithm> |
18 | | #include <cassert> |
19 | | #include <cstdint> |
20 | | #include <functional> |
21 | | #include <initializer_list> |
22 | | #include <iterator> |
23 | | #include <limits> |
24 | | #include <type_traits> |
25 | | #include <vector> |
26 | | |
27 | | #ifndef SOURCE_ENUM_SET_H_ |
28 | | #define SOURCE_ENUM_SET_H_ |
29 | | |
30 | | #include "source/latest_version_spirv_header.h" |
31 | | |
32 | | namespace spvtools { |
33 | | |
34 | | // This container is optimized to store and retrieve unsigned enum values. |
35 | | // The base model for this implementation is an open-addressing hashtable with |
36 | | // linear probing. For small enums (max index < 64), all operations are O(1). |
37 | | // |
38 | | // - Enums are stored in buckets (64 contiguous values max per bucket) |
39 | | // - Buckets ranges don't overlap, but don't have to be contiguous. |
40 | | // - Enums are packed into 64-bits buckets, using 1 bit per enum value. |
41 | | // |
42 | | // Example: |
43 | | // - MyEnum { A = 0, B = 1, C = 64, D = 65 } |
44 | | // - 2 buckets are required: |
45 | | // - bucket 0, storing values in the range [ 0; 64[ |
46 | | // - bucket 1, storing values in the range [64; 128[ |
47 | | // |
48 | | // - Buckets are stored in a sorted vector (sorted by bucket range). |
49 | | // - Retrieval is done by computing the theoretical bucket index using the enum |
50 | | // value, and |
51 | | // doing a linear scan from this position. |
52 | | // - Insertion is done by retrieving the bucket and either: |
53 | | // - inserting a new bucket in the sorted vector when no buckets has a |
54 | | // compatible range. |
55 | | // - setting the corresponding bit in the bucket. |
56 | | // This means insertion in the middle/beginning can cause a memmove when no |
57 | | // bucket is available. In our case, this happens at most 23 times for the |
58 | | // largest enum we have (Opcodes). |
59 | | template <typename T> |
60 | | class EnumSet { |
61 | | private: |
62 | | using BucketType = uint64_t; |
63 | | using ElementType = std::underlying_type_t<T>; |
64 | | static_assert(std::is_enum_v<T>, "EnumSets only works with enums."); |
65 | | static_assert(std::is_signed_v<ElementType> == false, |
66 | | "EnumSet doesn't supports signed enums."); |
67 | | |
68 | | // Each bucket can hold up to `kBucketSize` distinct, contiguous enum values. |
69 | | // The first value a bucket can hold must be aligned on `kBucketSize`. |
70 | | struct Bucket { |
71 | | // bit mask to store `kBucketSize` enums. |
72 | | BucketType data; |
73 | | // 1st enum this bucket can represent. |
74 | | T start; |
75 | | |
76 | 694k | bool operator==(const Bucket& other) const { |
77 | 694k | return start == other.start && data == other.data; |
78 | 694k | } spvtools::EnumSet<spv::Capability>::Bucket::operator==(spvtools::EnumSet<spv::Capability>::Bucket const&) const Line | Count | Source | 76 | 693k | bool operator==(const Bucket& other) const { | 77 | 693k | return start == other.start && data == other.data; | 78 | 693k | } |
spvtools::EnumSet<spvtools::Extension>::Bucket::operator==(spvtools::EnumSet<spvtools::Extension>::Bucket const&) const Line | Count | Source | 76 | 1.27k | bool operator==(const Bucket& other) const { | 77 | 1.27k | return start == other.start && data == other.data; | 78 | 1.27k | } |
|
79 | | }; |
80 | | |
81 | | // How many distinct values can a bucket hold? 1 bit per value. |
82 | | static constexpr size_t kBucketSize = sizeof(BucketType) * 8ULL; |
83 | | |
84 | | public: |
85 | | class Iterator { |
86 | | public: |
87 | | typedef Iterator self_type; |
88 | | typedef T value_type; |
89 | | typedef T& reference; |
90 | | typedef T* pointer; |
91 | | typedef std::forward_iterator_tag iterator_category; |
92 | | typedef size_t difference_type; |
93 | | |
94 | | Iterator(const Iterator& other) |
95 | 3.66M | : set_(other.set_), |
96 | 3.66M | bucketIndex_(other.bucketIndex_), |
97 | 3.66M | bucketOffset_(other.bucketOffset_) {}spvtools::EnumSet<spv::Capability>::Iterator::Iterator(spvtools::EnumSet<spv::Capability>::Iterator const&) Line | Count | Source | 95 | 3.65M | : set_(other.set_), | 96 | 3.65M | bucketIndex_(other.bucketIndex_), | 97 | 3.65M | bucketOffset_(other.bucketOffset_) {} |
spvtools::EnumSet<spvtools::Extension>::Iterator::Iterator(spvtools::EnumSet<spvtools::Extension>::Iterator const&) Line | Count | Source | 95 | 6.54k | : set_(other.set_), | 96 | 6.54k | bucketIndex_(other.bucketIndex_), | 97 | 6.54k | bucketOffset_(other.bucketOffset_) {} |
|
98 | | |
99 | 1.34M | Iterator& operator++() { |
100 | 66.7M | do { |
101 | 66.7M | if (bucketIndex_ >= set_->buckets_.size()) { |
102 | 0 | bucketIndex_ = set_->buckets_.size(); |
103 | 0 | bucketOffset_ = 0; |
104 | 0 | break; |
105 | 0 | } |
106 | | |
107 | 66.7M | if (bucketOffset_ + 1 == kBucketSize) { |
108 | 1.04M | bucketOffset_ = 0; |
109 | 1.04M | ++bucketIndex_; |
110 | 65.6M | } else { |
111 | 65.6M | ++bucketOffset_; |
112 | 65.6M | } |
113 | | |
114 | 66.7M | } while (bucketIndex_ < set_->buckets_.size() && |
115 | 65.6M | !set_->HasEnumAt(bucketIndex_, bucketOffset_)); |
116 | 1.34M | return *this; |
117 | 1.34M | } spvtools::EnumSet<spv::Capability>::Iterator::operator++() Line | Count | Source | 99 | 1.34M | Iterator& operator++() { | 100 | 66.6M | do { | 101 | 66.6M | if (bucketIndex_ >= set_->buckets_.size()) { | 102 | 0 | bucketIndex_ = set_->buckets_.size(); | 103 | 0 | bucketOffset_ = 0; | 104 | 0 | break; | 105 | 0 | } | 106 | | | 107 | 66.6M | if (bucketOffset_ + 1 == kBucketSize) { | 108 | 1.04M | bucketOffset_ = 0; | 109 | 1.04M | ++bucketIndex_; | 110 | 65.6M | } else { | 111 | 65.6M | ++bucketOffset_; | 112 | 65.6M | } | 113 | | | 114 | 66.6M | } while (bucketIndex_ < set_->buckets_.size() && | 115 | 65.6M | !set_->HasEnumAt(bucketIndex_, bucketOffset_)); | 116 | 1.34M | return *this; | 117 | 1.34M | } |
spvtools::EnumSet<spvtools::Extension>::Iterator::operator++() Line | Count | Source | 99 | 864 | Iterator& operator++() { | 100 | 28.6k | do { | 101 | 28.6k | if (bucketIndex_ >= set_->buckets_.size()) { | 102 | 0 | bucketIndex_ = set_->buckets_.size(); | 103 | 0 | bucketOffset_ = 0; | 104 | 0 | break; | 105 | 0 | } | 106 | | | 107 | 28.6k | if (bucketOffset_ + 1 == kBucketSize) { | 108 | 447 | bucketOffset_ = 0; | 109 | 447 | ++bucketIndex_; | 110 | 28.1k | } else { | 111 | 28.1k | ++bucketOffset_; | 112 | 28.1k | } | 113 | | | 114 | 28.6k | } while (bucketIndex_ < set_->buckets_.size() && | 115 | 28.2k | !set_->HasEnumAt(bucketIndex_, bucketOffset_)); | 116 | 864 | return *this; | 117 | 864 | } |
|
118 | | |
119 | | Iterator operator++(int) { |
120 | | Iterator old = *this; |
121 | | operator++(); |
122 | | return old; |
123 | | } |
124 | | |
125 | 1.10M | T operator*() const { |
126 | 1.10M | assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && |
127 | 1.10M | "operator*() called on an invalid iterator."); |
128 | 1.10M | return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); |
129 | 1.10M | } spvtools::EnumSet<spv::Capability>::Iterator::operator*() const Line | Count | Source | 125 | 1.10M | T operator*() const { | 126 | 1.10M | assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && | 127 | 1.10M | "operator*() called on an invalid iterator."); | 128 | 1.10M | return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); | 129 | 1.10M | } |
spvtools::EnumSet<spvtools::Extension>::Iterator::operator*() const Line | Count | Source | 125 | 479 | T operator*() const { | 126 | 479 | assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && | 127 | 479 | "operator*() called on an invalid iterator."); | 128 | 479 | return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); | 129 | 479 | } |
|
130 | | |
131 | 3.09M | bool operator!=(const Iterator& other) const { |
132 | 3.09M | return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || |
133 | 2.79M | bucketIndex_ != other.bucketIndex_; |
134 | 3.09M | } spvtools::EnumSet<spv::Capability>::Iterator::operator!=(spvtools::EnumSet<spv::Capability>::Iterator const&) const Line | Count | Source | 131 | 3.09M | bool operator!=(const Iterator& other) const { | 132 | 3.09M | return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || | 133 | 2.79M | bucketIndex_ != other.bucketIndex_; | 134 | 3.09M | } |
spvtools::EnumSet<spvtools::Extension>::Iterator::operator!=(spvtools::EnumSet<spvtools::Extension>::Iterator const&) const Line | Count | Source | 131 | 871 | bool operator!=(const Iterator& other) const { | 132 | 871 | return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || | 133 | 403 | bucketIndex_ != other.bucketIndex_; | 134 | 871 | } |
|
135 | | |
136 | | bool operator==(const Iterator& other) const { |
137 | | return !(operator!=(other)); |
138 | | } |
139 | | |
140 | | Iterator& operator=(const Iterator& other) { |
141 | | set_ = other.set_; |
142 | | bucketIndex_ = other.bucketIndex_; |
143 | | bucketOffset_ = other.bucketOffset_; |
144 | | return *this; |
145 | | } |
146 | | |
147 | | private: |
148 | | Iterator(const EnumSet* set, size_t bucketIndex, ElementType bucketOffset) |
149 | 7.64M | : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {}spvtools::EnumSet<spv::Capability>::Iterator::Iterator(spvtools::EnumSet<spv::Capability> const*, unsigned long, unsigned int) Line | Count | Source | 149 | 7.63M | : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {} |
spvtools::EnumSet<spvtools::Extension>::Iterator::Iterator(spvtools::EnumSet<spvtools::Extension> const*, unsigned long, unsigned int) Line | Count | Source | 149 | 7.32k | : set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {} |
|
150 | | |
151 | | private: |
152 | | const EnumSet* set_ = nullptr; |
153 | | // Index of the bucket in the vector. |
154 | | size_t bucketIndex_ = 0; |
155 | | // Offset in bits in the current bucket. |
156 | | ElementType bucketOffset_ = 0; |
157 | | |
158 | | friend class EnumSet; |
159 | | }; |
160 | | |
161 | | // Required to allow the use of std::inserter. |
162 | | using value_type = T; |
163 | | using const_iterator = Iterator; |
164 | | using iterator = Iterator; |
165 | | |
166 | | public: |
167 | 1.99M | iterator cbegin() const noexcept { |
168 | 1.99M | auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); |
169 | 1.99M | if (buckets_.size() == 0) { |
170 | 950k | return it; |
171 | 950k | } |
172 | | |
173 | | // The iterator has the logic to find the next valid bit. If the value 0 |
174 | | // is not stored, use it to find the next valid bit. |
175 | 1.04M | if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { |
176 | 241k | ++it; |
177 | 241k | } |
178 | | |
179 | 1.04M | return it; |
180 | 1.99M | } spvtools::EnumSet<spv::Capability>::cbegin() const Line | Count | Source | 167 | 1.99M | iterator cbegin() const noexcept { | 168 | 1.99M | auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); | 169 | 1.99M | if (buckets_.size() == 0) { | 170 | 950k | return it; | 171 | 950k | } | 172 | | | 173 | | // The iterator has the logic to find the next valid bit. If the value 0 | 174 | | // is not stored, use it to find the next valid bit. | 175 | 1.04M | if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { | 176 | 240k | ++it; | 177 | 240k | } | 178 | | | 179 | 1.04M | return it; | 180 | 1.99M | } |
spvtools::EnumSet<spvtools::Extension>::cbegin() const Line | Count | Source | 167 | 392 | iterator cbegin() const noexcept { | 168 | 392 | auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); | 169 | 392 | if (buckets_.size() == 0) { | 170 | 0 | return it; | 171 | 0 | } | 172 | | | 173 | | // The iterator has the logic to find the next valid bit. If the value 0 | 174 | | // is not stored, use it to find the next valid bit. | 175 | 392 | if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { | 176 | 385 | ++it; | 177 | 385 | } | 178 | | | 179 | 392 | return it; | 180 | 392 | } |
|
181 | | |
182 | 1.99M | iterator begin() const noexcept { return cbegin(); }spvtools::EnumSet<spv::Capability>::begin() const Line | Count | Source | 182 | 1.99M | iterator begin() const noexcept { return cbegin(); } |
spvtools::EnumSet<spvtools::Extension>::begin() const Line | Count | Source | 182 | 392 | iterator begin() const noexcept { return cbegin(); } |
|
183 | | |
184 | 1.99M | iterator cend() const noexcept { |
185 | 1.99M | return iterator(this, buckets_.size(), /* bucketOffset= */ 0); |
186 | 1.99M | } spvtools::EnumSet<spv::Capability>::cend() const Line | Count | Source | 184 | 1.99M | iterator cend() const noexcept { | 185 | 1.99M | return iterator(this, buckets_.size(), /* bucketOffset= */ 0); | 186 | 1.99M | } |
spvtools::EnumSet<spvtools::Extension>::cend() const Line | Count | Source | 184 | 392 | iterator cend() const noexcept { | 185 | 392 | return iterator(this, buckets_.size(), /* bucketOffset= */ 0); | 186 | 392 | } |
|
187 | | |
188 | 1.99M | iterator end() const noexcept { return cend(); }spvtools::EnumSet<spv::Capability>::end() const Line | Count | Source | 188 | 1.99M | iterator end() const noexcept { return cend(); } |
spvtools::EnumSet<spvtools::Extension>::end() const Line | Count | Source | 188 | 392 | iterator end() const noexcept { return cend(); } |
|
189 | | |
190 | | // Creates an empty set. |
191 | 37.8M | EnumSet() : buckets_(0), size_(0) {}spvtools::EnumSet<spv::Capability>::EnumSet() Line | Count | Source | 191 | 21.6M | EnumSet() : buckets_(0), size_(0) {} |
spvtools::EnumSet<spvtools::Extension>::EnumSet() Line | Count | Source | 191 | 16.1M | EnumSet() : buckets_(0), size_(0) {} |
|
192 | | |
193 | | // Creates a set and store `value` in it. |
194 | | EnumSet(T value) : EnumSet() { insert(value); } |
195 | | |
196 | | // Creates a set and stores each `values` in it. |
197 | | EnumSet(std::initializer_list<T> values) : EnumSet() { |
198 | | for (auto item : values) { |
199 | | insert(item); |
200 | | } |
201 | | } |
202 | | |
203 | | // Creates a set, and insert `count` enum values pointed by `array` in it. |
204 | 1.94M | EnumSet(ElementType count, const T* array) : EnumSet() { |
205 | 2.93M | for (ElementType i = 0; i < count; i++) { |
206 | 991k | insert(array[i]); |
207 | 991k | } |
208 | 1.94M | } spvtools::EnumSet<spv::Capability>::EnumSet(unsigned int, spv::Capability const*) Line | Count | Source | 204 | 1.93M | EnumSet(ElementType count, const T* array) : EnumSet() { | 205 | 2.92M | for (ElementType i = 0; i < count; i++) { | 206 | 987k | insert(array[i]); | 207 | 987k | } | 208 | 1.93M | } |
spvtools::EnumSet<spvtools::Extension>::EnumSet(unsigned int, spvtools::Extension const*) Line | Count | Source | 204 | 3.97k | EnumSet(ElementType count, const T* array) : EnumSet() { | 205 | 8.02k | for (ElementType i = 0; i < count; i++) { | 206 | 4.05k | insert(array[i]); | 207 | 4.05k | } | 208 | 3.97k | } |
|
209 | | |
210 | | // Creates a set initialized with the content of the range [begin; end[. |
211 | | template <class InputIt> |
212 | 15.4M | EnumSet(InputIt begin, InputIt end) : EnumSet() { |
213 | 15.4M | for (; begin != end; ++begin) { |
214 | 69 | insert(*begin); |
215 | 69 | } |
216 | 15.4M | } Unexecuted instantiation: spvtools::EnumSet<spv::Capability>::EnumSet<spv::Capability const*>(spv::Capability const*, spv::Capability const*) spvtools::EnumSet<spvtools::Extension>::EnumSet<spvtools::Extension const*>(spvtools::Extension const*, spvtools::Extension const*) Line | Count | Source | 212 | 15.4M | EnumSet(InputIt begin, InputIt end) : EnumSet() { | 213 | 15.4M | for (; begin != end; ++begin) { | 214 | 69 | insert(*begin); | 215 | 69 | } | 216 | 15.4M | } |
|
217 | | |
218 | | // Copies the EnumSet `other` into a new EnumSet. |
219 | | EnumSet(const EnumSet& other) |
220 | | : buckets_(other.buckets_), size_(other.size_) {} |
221 | | |
222 | | // Moves the EnumSet `other` into a new EnumSet. |
223 | | EnumSet(EnumSet&& other) |
224 | 0 | : buckets_(std::move(other.buckets_)), size_(other.size_) {}Unexecuted instantiation: spvtools::EnumSet<spv::Capability>::EnumSet(spvtools::EnumSet<spv::Capability>&&) Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::EnumSet(spvtools::EnumSet<spvtools::Extension>&&) |
225 | | |
226 | | // Deep-copies the EnumSet `other` into this EnumSet. |
227 | 1.17M | EnumSet& operator=(const EnumSet& other) { |
228 | 1.17M | buckets_ = other.buckets_; |
229 | 1.17M | size_ = other.size_; |
230 | 1.17M | return *this; |
231 | 1.17M | } |
232 | | |
233 | | // Matches std::unordered_set::insert behavior. |
234 | 3.66M | std::pair<iterator, bool> insert(const T& value) { |
235 | 3.66M | const size_t index = FindBucketForValue(value); |
236 | 3.66M | const ElementType offset = ComputeBucketOffset(value); |
237 | | |
238 | 3.66M | if (index >= buckets_.size() || |
239 | 2.48M | buckets_[index].start != ComputeBucketStart(value)) { |
240 | 2.48M | size_ += 1; |
241 | 2.48M | InsertBucketFor(index, value); |
242 | 2.48M | return std::make_pair(Iterator(this, index, offset), true); |
243 | 2.48M | } |
244 | | |
245 | 1.18M | auto& bucket = buckets_[index]; |
246 | 1.18M | const auto mask = ComputeMaskForValue(value); |
247 | 1.18M | if (bucket.data & mask) { |
248 | 376 | return std::make_pair(Iterator(this, index, offset), false); |
249 | 376 | } |
250 | | |
251 | 1.18M | size_ += 1; |
252 | 1.18M | bucket.data |= ComputeMaskForValue(value); |
253 | 1.18M | return std::make_pair(Iterator(this, index, offset), true); |
254 | 1.18M | } spvtools::EnumSet<spv::Capability>::insert(spv::Capability const&) Line | Count | Source | 234 | 3.65M | std::pair<iterator, bool> insert(const T& value) { | 235 | 3.65M | const size_t index = FindBucketForValue(value); | 236 | 3.65M | const ElementType offset = ComputeBucketOffset(value); | 237 | | | 238 | 3.65M | if (index >= buckets_.size() || | 239 | 2.47M | buckets_[index].start != ComputeBucketStart(value)) { | 240 | 2.47M | size_ += 1; | 241 | 2.47M | InsertBucketFor(index, value); | 242 | 2.47M | return std::make_pair(Iterator(this, index, offset), true); | 243 | 2.47M | } | 244 | | | 245 | 1.18M | auto& bucket = buckets_[index]; | 246 | 1.18M | const auto mask = ComputeMaskForValue(value); | 247 | 1.18M | if (bucket.data & mask) { | 248 | 0 | return std::make_pair(Iterator(this, index, offset), false); | 249 | 0 | } | 250 | | | 251 | 1.18M | size_ += 1; | 252 | 1.18M | bucket.data |= ComputeMaskForValue(value); | 253 | 1.18M | return std::make_pair(Iterator(this, index, offset), true); | 254 | 1.18M | } |
spvtools::EnumSet<spvtools::Extension>::insert(spvtools::Extension const&) Line | Count | Source | 234 | 6.54k | std::pair<iterator, bool> insert(const T& value) { | 235 | 6.54k | const size_t index = FindBucketForValue(value); | 236 | 6.54k | const ElementType offset = ComputeBucketOffset(value); | 237 | | | 238 | 6.54k | if (index >= buckets_.size() || | 239 | 6.07k | buckets_[index].start != ComputeBucketStart(value)) { | 240 | 6.07k | size_ += 1; | 241 | 6.07k | InsertBucketFor(index, value); | 242 | 6.07k | return std::make_pair(Iterator(this, index, offset), true); | 243 | 6.07k | } | 244 | | | 245 | 474 | auto& bucket = buckets_[index]; | 246 | 474 | const auto mask = ComputeMaskForValue(value); | 247 | 474 | if (bucket.data & mask) { | 248 | 376 | return std::make_pair(Iterator(this, index, offset), false); | 249 | 376 | } | 250 | | | 251 | 98 | size_ += 1; | 252 | 98 | bucket.data |= ComputeMaskForValue(value); | 253 | 98 | return std::make_pair(Iterator(this, index, offset), true); | 254 | 474 | } |
|
255 | | |
256 | | // Inserts `value` in the set if possible. |
257 | | // Similar to `std::unordered_set::insert`, except the hint is ignored. |
258 | | // Returns an iterator to the inserted element, or the element preventing |
259 | | // insertion. |
260 | | iterator insert(const_iterator, const T& value) { |
261 | | return insert(value).first; |
262 | | } |
263 | | |
264 | | // Inserts `value` in the set if possible. |
265 | | // Similar to `std::unordered_set::insert`, except the hint is ignored. |
266 | | // Returns an iterator to the inserted element, or the element preventing |
267 | | // insertion. |
268 | | iterator insert(const_iterator, T&& value) { return insert(value).first; } |
269 | | |
270 | | // Inserts all the values in the range [`first`; `last[. |
271 | | // Similar to `std::unordered_set::insert`. |
272 | | template <class InputIt> |
273 | 0 | void insert(InputIt first, InputIt last) { |
274 | 0 | for (auto it = first; it != last; ++it) { |
275 | 0 | insert(*it); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | // Removes the value `value` into the set. |
280 | | // Similar to `std::unordered_set::erase`. |
281 | | // Returns the number of erased elements. |
282 | 0 | size_t erase(const T& value) { |
283 | 0 | const size_t index = FindBucketForValue(value); |
284 | 0 | if (index >= buckets_.size() || |
285 | 0 | buckets_[index].start != ComputeBucketStart(value)) { |
286 | 0 | return 0; |
287 | 0 | } |
288 | | |
289 | 0 | auto& bucket = buckets_[index]; |
290 | 0 | const auto mask = ComputeMaskForValue(value); |
291 | 0 | if (!(bucket.data & mask)) { |
292 | 0 | return 0; |
293 | 0 | } |
294 | | |
295 | 0 | size_ -= 1; |
296 | 0 | bucket.data &= ~mask; |
297 | 0 | if (bucket.data == 0) { |
298 | 0 | buckets_.erase(buckets_.cbegin() + index); |
299 | 0 | } |
300 | 0 | return 1; |
301 | 0 | } Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::erase(spvtools::Extension const&) Unexecuted instantiation: spvtools::EnumSet<spv::Capability>::erase(spv::Capability const&) |
302 | | |
303 | | // Returns true if `value` is present in the set. |
304 | 77.6M | bool contains(T value) const { |
305 | 77.6M | const size_t index = FindBucketForValue(value); |
306 | 77.6M | if (index >= buckets_.size() || |
307 | 43.2M | buckets_[index].start != ComputeBucketStart(value)) { |
308 | 43.2M | return false; |
309 | 43.2M | } |
310 | 34.3M | auto& bucket = buckets_[index]; |
311 | 34.3M | return bucket.data & ComputeMaskForValue(value); |
312 | 77.6M | } spvtools::EnumSet<spvtools::Extension>::contains(spvtools::Extension) const Line | Count | Source | 304 | 372k | bool contains(T value) const { | 305 | 372k | const size_t index = FindBucketForValue(value); | 306 | 372k | if (index >= buckets_.size() || | 307 | 370k | buckets_[index].start != ComputeBucketStart(value)) { | 308 | 370k | return false; | 309 | 370k | } | 310 | 1.62k | auto& bucket = buckets_[index]; | 311 | 1.62k | return bucket.data & ComputeMaskForValue(value); | 312 | 372k | } |
spvtools::EnumSet<spv::Capability>::contains(spv::Capability) const Line | Count | Source | 304 | 77.3M | bool contains(T value) const { | 305 | 77.3M | const size_t index = FindBucketForValue(value); | 306 | 77.3M | if (index >= buckets_.size() || | 307 | 42.9M | buckets_[index].start != ComputeBucketStart(value)) { | 308 | 42.9M | return false; | 309 | 42.9M | } | 310 | 34.3M | auto& bucket = buckets_[index]; | 311 | 34.3M | return bucket.data & ComputeMaskForValue(value); | 312 | 77.3M | } |
|
313 | | |
314 | | // Returns the 1 if `value` is present in the set, `0` otherwise. |
315 | | inline size_t count(T value) const { return contains(value) ? 1 : 0; } |
316 | | |
317 | | // Returns true if the set is holds no values. |
318 | 33.1M | inline bool empty() const { return size_ == 0; }spvtools::EnumSet<spv::Capability>::empty() const Line | Count | Source | 318 | 17.6M | inline bool empty() const { return size_ == 0; } |
spvtools::EnumSet<spvtools::Extension>::empty() const Line | Count | Source | 318 | 15.4M | inline bool empty() const { return size_ == 0; } |
|
319 | | |
320 | | // Returns the number of enums stored in this set. |
321 | 0 | size_t size() const { return size_; } |
322 | | |
323 | | // Returns true if this set contains at least one value contained in `in_set`. |
324 | | // Note: If `in_set` is empty, this function returns true. |
325 | 16.5M | bool HasAnyOf(const EnumSet<T>& in_set) const { |
326 | 16.5M | if (in_set.empty()) { |
327 | 15.9M | return true; |
328 | 15.9M | } |
329 | | |
330 | 633k | auto lhs = buckets_.cbegin(); |
331 | 633k | auto rhs = in_set.buckets_.cbegin(); |
332 | | |
333 | 634k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { |
334 | 633k | if (lhs->start == rhs->start) { |
335 | 632k | if (lhs->data & rhs->data) { |
336 | | // At least 1 bit is shared. Early return. |
337 | 632k | return true; |
338 | 632k | } |
339 | | |
340 | 284 | lhs++; |
341 | 284 | rhs++; |
342 | 284 | continue; |
343 | 632k | } |
344 | | |
345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. |
346 | 543 | if (lhs->start < rhs->start) { |
347 | 506 | lhs++; |
348 | 506 | continue; |
349 | 506 | } |
350 | | |
351 | | // Otherwise, RHS needs to catch up on LHS. |
352 | 37 | rhs++; |
353 | 37 | } |
354 | | |
355 | 1.10k | return false; |
356 | 633k | } spvtools::EnumSet<spv::Capability>::HasAnyOf(spvtools::EnumSet<spv::Capability> const&) const Line | Count | Source | 325 | 16.5M | bool HasAnyOf(const EnumSet<T>& in_set) const { | 326 | 16.5M | if (in_set.empty()) { | 327 | 15.9M | return true; | 328 | 15.9M | } | 329 | | | 330 | 629k | auto lhs = buckets_.cbegin(); | 331 | 629k | auto rhs = in_set.buckets_.cbegin(); | 332 | | | 333 | 630k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { | 334 | 629k | if (lhs->start == rhs->start) { | 335 | 629k | if (lhs->data & rhs->data) { | 336 | | // At least 1 bit is shared. Early return. | 337 | 628k | return true; | 338 | 628k | } | 339 | | | 340 | 255 | lhs++; | 341 | 255 | rhs++; | 342 | 255 | continue; | 343 | 629k | } | 344 | | | 345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. | 346 | 335 | if (lhs->start < rhs->start) { | 347 | 324 | lhs++; | 348 | 324 | continue; | 349 | 324 | } | 350 | | | 351 | | // Otherwise, RHS needs to catch up on LHS. | 352 | 11 | rhs++; | 353 | 11 | } | 354 | | | 355 | 709 | return false; | 356 | 629k | } |
spvtools::EnumSet<spvtools::Extension>::HasAnyOf(spvtools::EnumSet<spvtools::Extension> const&) const Line | Count | Source | 325 | 4.03k | bool HasAnyOf(const EnumSet<T>& in_set) const { | 326 | 4.03k | if (in_set.empty()) { | 327 | 0 | return true; | 328 | 0 | } | 329 | | | 330 | 4.03k | auto lhs = buckets_.cbegin(); | 331 | 4.03k | auto rhs = in_set.buckets_.cbegin(); | 332 | | | 333 | 4.27k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { | 334 | 3.87k | if (lhs->start == rhs->start) { | 335 | 3.67k | if (lhs->data & rhs->data) { | 336 | | // At least 1 bit is shared. Early return. | 337 | 3.64k | return true; | 338 | 3.64k | } | 339 | | | 340 | 29 | lhs++; | 341 | 29 | rhs++; | 342 | 29 | continue; | 343 | 3.67k | } | 344 | | | 345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. | 346 | 208 | if (lhs->start < rhs->start) { | 347 | 182 | lhs++; | 348 | 182 | continue; | 349 | 182 | } | 350 | | | 351 | | // Otherwise, RHS needs to catch up on LHS. | 352 | 26 | rhs++; | 353 | 26 | } | 354 | | | 355 | 392 | return false; | 356 | 4.03k | } |
|
357 | | |
358 | | private: |
359 | | // Returns the index of the last bucket in which `value` could be stored. |
360 | 193M | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { |
361 | 193M | return static_cast<size_t>(value) / kBucketSize; |
362 | 193M | } spvtools::EnumSet<spv::Capability>::ComputeLargestPossibleBucketIndexFor(spv::Capability) Line | Count | Source | 360 | 193M | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { | 361 | 193M | return static_cast<size_t>(value) / kBucketSize; | 362 | 193M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeLargestPossibleBucketIndexFor(spvtools::Extension) Line | Count | Source | 360 | 15.5k | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { | 361 | 15.5k | return static_cast<size_t>(value) / kBucketSize; | 362 | 15.5k | } |
|
363 | | |
364 | | // Returns the smallest enum value that could be contained in the same bucket |
365 | | // as `value`. |
366 | 115M | static constexpr inline T ComputeBucketStart(T value) { |
367 | 115M | return static_cast<T>(kBucketSize * |
368 | 115M | ComputeLargestPossibleBucketIndexFor(value)); |
369 | 115M | } spvtools::EnumSet<spv::Capability>::ComputeBucketStart(spv::Capability) Line | Count | Source | 366 | 115M | static constexpr inline T ComputeBucketStart(T value) { | 367 | 115M | return static_cast<T>(kBucketSize * | 368 | 115M | ComputeLargestPossibleBucketIndexFor(value)); | 369 | 115M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeBucketStart(spvtools::Extension) Line | Count | Source | 366 | 11.9k | static constexpr inline T ComputeBucketStart(T value) { | 367 | 11.9k | return static_cast<T>(kBucketSize * | 368 | 11.9k | ComputeLargestPossibleBucketIndexFor(value)); | 369 | 11.9k | } |
|
370 | | |
371 | | // Returns the index of the bit that corresponds to `value` in the bucket. |
372 | 42.9M | static constexpr inline ElementType ComputeBucketOffset(T value) { |
373 | 42.9M | return static_cast<ElementType>(value) % kBucketSize; |
374 | 42.9M | } spvtools::EnumSet<spv::Capability>::ComputeBucketOffset(spv::Capability) Line | Count | Source | 372 | 42.8M | static constexpr inline ElementType ComputeBucketOffset(T value) { | 373 | 42.8M | return static_cast<ElementType>(value) % kBucketSize; | 374 | 42.8M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeBucketOffset(spvtools::Extension) Line | Count | Source | 372 | 14.8k | static constexpr inline ElementType ComputeBucketOffset(T value) { | 373 | 14.8k | return static_cast<ElementType>(value) % kBucketSize; | 374 | 14.8k | } |
|
375 | | |
376 | | // Returns the bitmask used to represent the enum `value` in its bucket. |
377 | 36.7M | static constexpr inline BucketType ComputeMaskForValue(T value) { |
378 | 36.7M | return 1ULL << ComputeBucketOffset(value); |
379 | 36.7M | } spvtools::EnumSet<spv::Capability>::ComputeMaskForValue(spv::Capability) Line | Count | Source | 377 | 36.7M | static constexpr inline BucketType ComputeMaskForValue(T value) { | 378 | 36.7M | return 1ULL << ComputeBucketOffset(value); | 379 | 36.7M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeMaskForValue(spvtools::Extension) Line | Count | Source | 377 | 2.19k | static constexpr inline BucketType ComputeMaskForValue(T value) { | 378 | 2.19k | return 1ULL << ComputeBucketOffset(value); | 379 | 2.19k | } |
|
380 | | |
381 | | // Returns the `enum` stored in `bucket` at `offset`. |
382 | | // `offset` is the bit-offset in the bucket storage. |
383 | | static constexpr inline T GetValueFromBucket(const Bucket& bucket, |
384 | 1.10M | BucketType offset) { |
385 | 1.10M | return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); |
386 | 1.10M | } spvtools::EnumSet<spv::Capability>::GetValueFromBucket(spvtools::EnumSet<spv::Capability>::Bucket const&, unsigned long) Line | Count | Source | 384 | 1.10M | BucketType offset) { | 385 | 1.10M | return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); | 386 | 1.10M | } |
spvtools::EnumSet<spvtools::Extension>::GetValueFromBucket(spvtools::EnumSet<spvtools::Extension>::Bucket const&, unsigned long) Line | Count | Source | 384 | 479 | BucketType offset) { | 385 | 479 | return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); | 386 | 479 | } |
|
387 | | |
388 | | // For a given enum `value`, finds the bucket index that could contain this |
389 | | // value. If no such bucket is found, the index at which the new bucket should |
390 | | // be inserted is returned. |
391 | 81.3M | size_t FindBucketForValue(T value) const { |
392 | | // Set is empty, insert at 0. |
393 | 81.3M | if (buckets_.size() == 0) { |
394 | 3.57M | return 0; |
395 | 3.57M | } |
396 | | |
397 | 77.7M | const T wanted_start = ComputeBucketStart(value); |
398 | 77.7M | assert(buckets_.size() > 0 && |
399 | 77.7M | "Size must not be 0 here. Has the code above changed?"); |
400 | 77.7M | size_t index = std::min(buckets_.size() - 1, |
401 | 77.7M | ComputeLargestPossibleBucketIndexFor(value)); |
402 | | |
403 | | // This loops behaves like std::upper_bound with a reverse iterator. |
404 | | // Buckets are sorted. 3 main cases: |
405 | | // - The bucket matches |
406 | | // => returns the bucket index. |
407 | | // - The found bucket is larger |
408 | | // => scans left until it finds the correct bucket, or insertion point. |
409 | | // - The found bucket is smaller |
410 | | // => We are at the end, so we return past-end index for insertion. |
411 | 77.7M | for (; buckets_[index].start >= wanted_start; index--) { |
412 | 35.5M | if (index == 0) { |
413 | 35.5M | return 0; |
414 | 35.5M | } |
415 | 35.5M | } |
416 | | |
417 | 42.1M | return index + 1; |
418 | 77.7M | } spvtools::EnumSet<spv::Capability>::FindBucketForValue(spv::Capability) const Line | Count | Source | 391 | 80.9M | size_t FindBucketForValue(T value) const { | 392 | | // Set is empty, insert at 0. | 393 | 80.9M | if (buckets_.size() == 0) { | 394 | 3.19M | return 0; | 395 | 3.19M | } | 396 | | | 397 | 77.7M | const T wanted_start = ComputeBucketStart(value); | 398 | 77.7M | assert(buckets_.size() > 0 && | 399 | 77.7M | "Size must not be 0 here. Has the code above changed?"); | 400 | 77.7M | size_t index = std::min(buckets_.size() - 1, | 401 | 77.7M | ComputeLargestPossibleBucketIndexFor(value)); | 402 | | | 403 | | // This loops behaves like std::upper_bound with a reverse iterator. | 404 | | // Buckets are sorted. 3 main cases: | 405 | | // - The bucket matches | 406 | | // => returns the bucket index. | 407 | | // - The found bucket is larger | 408 | | // => scans left until it finds the correct bucket, or insertion point. | 409 | | // - The found bucket is smaller | 410 | | // => We are at the end, so we return past-end index for insertion. | 411 | 77.7M | for (; buckets_[index].start >= wanted_start; index--) { | 412 | 35.5M | if (index == 0) { | 413 | 35.5M | return 0; | 414 | 35.5M | } | 415 | 35.5M | } | 416 | | | 417 | 42.1M | return index + 1; | 418 | 77.7M | } |
spvtools::EnumSet<spvtools::Extension>::FindBucketForValue(spvtools::Extension) const Line | Count | Source | 391 | 379k | size_t FindBucketForValue(T value) const { | 392 | | // Set is empty, insert at 0. | 393 | 379k | if (buckets_.size() == 0) { | 394 | 375k | return 0; | 395 | 375k | } | 396 | | | 397 | 3.62k | const T wanted_start = ComputeBucketStart(value); | 398 | 3.62k | assert(buckets_.size() > 0 && | 399 | 3.62k | "Size must not be 0 here. Has the code above changed?"); | 400 | 3.62k | size_t index = std::min(buckets_.size() - 1, | 401 | 3.62k | ComputeLargestPossibleBucketIndexFor(value)); | 402 | | | 403 | | // This loops behaves like std::upper_bound with a reverse iterator. | 404 | | // Buckets are sorted. 3 main cases: | 405 | | // - The bucket matches | 406 | | // => returns the bucket index. | 407 | | // - The found bucket is larger | 408 | | // => scans left until it finds the correct bucket, or insertion point. | 409 | | // - The found bucket is smaller | 410 | | // => We are at the end, so we return past-end index for insertion. | 411 | 4.36k | for (; buckets_[index].start >= wanted_start; index--) { | 412 | 2.60k | if (index == 0) { | 413 | 1.86k | return 0; | 414 | 1.86k | } | 415 | 2.60k | } | 416 | | | 417 | 1.75k | return index + 1; | 418 | 3.62k | } |
|
419 | | |
420 | | // Creates a new bucket to store `value` and inserts it at `index`. |
421 | | // If the `index` is past the end, the bucket is inserted at the end of the |
422 | | // vector. |
423 | 2.48M | void InsertBucketFor(size_t index, T value) { |
424 | 2.48M | const T bucket_start = ComputeBucketStart(value); |
425 | 2.48M | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; |
426 | 2.48M | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); |
427 | | #if defined(NDEBUG) |
428 | | (void)it; // Silencing unused variable warning. |
429 | | #else |
430 | 2.48M | assert(std::next(it) == buckets_.end() || |
431 | 2.48M | std::next(it)->start > bucket_start); |
432 | 2.48M | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); |
433 | 2.48M | #endif |
434 | 2.48M | } spvtools::EnumSet<spv::Capability>::InsertBucketFor(unsigned long, spv::Capability) Line | Count | Source | 423 | 2.47M | void InsertBucketFor(size_t index, T value) { | 424 | 2.47M | const T bucket_start = ComputeBucketStart(value); | 425 | 2.47M | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; | 426 | 2.47M | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); | 427 | | #if defined(NDEBUG) | 428 | | (void)it; // Silencing unused variable warning. | 429 | | #else | 430 | 2.47M | assert(std::next(it) == buckets_.end() || | 431 | 2.47M | std::next(it)->start > bucket_start); | 432 | 2.47M | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); | 433 | 2.47M | #endif | 434 | 2.47M | } |
spvtools::EnumSet<spvtools::Extension>::InsertBucketFor(unsigned long, spvtools::Extension) Line | Count | Source | 423 | 6.07k | void InsertBucketFor(size_t index, T value) { | 424 | 6.07k | const T bucket_start = ComputeBucketStart(value); | 425 | 6.07k | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; | 426 | 6.07k | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); | 427 | | #if defined(NDEBUG) | 428 | | (void)it; // Silencing unused variable warning. | 429 | | #else | 430 | 6.07k | assert(std::next(it) == buckets_.end() || | 431 | 6.07k | std::next(it)->start > bucket_start); | 432 | 6.07k | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); | 433 | 6.07k | #endif | 434 | 6.07k | } |
|
435 | | |
436 | | // Returns true if the bucket at `bucketIndex/ stores the enum at |
437 | | // `bucketOffset`, false otherwise. |
438 | 67.8M | bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { |
439 | 67.8M | assert(bucketIndex < buckets_.size()); |
440 | 67.8M | assert(bucketOffset < kBucketSize); |
441 | 67.8M | return buckets_[bucketIndex].data & (1ULL << bucketOffset); |
442 | 67.8M | } spvtools::EnumSet<spv::Capability>::HasEnumAt(unsigned long, unsigned long) const Line | Count | Source | 438 | 67.7M | bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { | 439 | 67.7M | assert(bucketIndex < buckets_.size()); | 440 | 67.7M | assert(bucketOffset < kBucketSize); | 441 | 67.7M | return buckets_[bucketIndex].data & (1ULL << bucketOffset); | 442 | 67.7M | } |
spvtools::EnumSet<spvtools::Extension>::HasEnumAt(unsigned long, unsigned long) const Line | Count | Source | 438 | 29.0k | bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { | 439 | 29.0k | assert(bucketIndex < buckets_.size()); | 440 | 29.0k | assert(bucketOffset < kBucketSize); | 441 | 29.0k | return buckets_[bucketIndex].data & (1ULL << bucketOffset); | 442 | 29.0k | } |
|
443 | | |
444 | | // Returns true if `lhs` and `rhs` hold the exact same values. |
445 | 1.38M | friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { |
446 | 1.38M | if (lhs.size_ != rhs.size_) { |
447 | 0 | return false; |
448 | 0 | } |
449 | | |
450 | 1.38M | if (lhs.buckets_.size() != rhs.buckets_.size()) { |
451 | 0 | return false; |
452 | 0 | } |
453 | 1.38M | return lhs.buckets_ == rhs.buckets_; |
454 | 1.38M | } spvtools::operator==(spvtools::EnumSet<spv::Capability> const&, spvtools::EnumSet<spv::Capability> const&) Line | Count | Source | 445 | 692k | friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { | 446 | 692k | if (lhs.size_ != rhs.size_) { | 447 | 0 | return false; | 448 | 0 | } | 449 | | | 450 | 692k | if (lhs.buckets_.size() != rhs.buckets_.size()) { | 451 | 0 | return false; | 452 | 0 | } | 453 | 692k | return lhs.buckets_ == rhs.buckets_; | 454 | 692k | } |
spvtools::operator==(spvtools::EnumSet<spvtools::Extension> const&, spvtools::EnumSet<spvtools::Extension> const&) Line | Count | Source | 445 | 692k | friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { | 446 | 692k | if (lhs.size_ != rhs.size_) { | 447 | 0 | return false; | 448 | 0 | } | 449 | | | 450 | 692k | if (lhs.buckets_.size() != rhs.buckets_.size()) { | 451 | 0 | return false; | 452 | 0 | } | 453 | 692k | return lhs.buckets_ == rhs.buckets_; | 454 | 692k | } |
|
455 | | |
456 | | // Returns true if `lhs` and `rhs` hold at least 1 different value. |
457 | 1.38M | friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { |
458 | 1.38M | return !(lhs == rhs); |
459 | 1.38M | } spvtools::operator!=(spvtools::EnumSet<spv::Capability> const&, spvtools::EnumSet<spv::Capability> const&) Line | Count | Source | 457 | 692k | friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { | 458 | 692k | return !(lhs == rhs); | 459 | 692k | } |
spvtools::operator!=(spvtools::EnumSet<spvtools::Extension> const&, spvtools::EnumSet<spvtools::Extension> const&) Line | Count | Source | 457 | 692k | friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { | 458 | 692k | return !(lhs == rhs); | 459 | 692k | } |
|
460 | | |
461 | | // Storage for the buckets. |
462 | | std::vector<Bucket> buckets_; |
463 | | // How many enums is this set storing. |
464 | | size_t size_ = 0; |
465 | | }; |
466 | | |
467 | | // A set of spv::Capability. |
468 | | using CapabilitySet = EnumSet<spv::Capability>; |
469 | | |
470 | | } // namespace spvtools |
471 | | |
472 | | #endif // SOURCE_ENUM_SET_H_ |