/src/shaderc/third_party/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 | 0 | friend bool operator==(const Bucket& lhs, const Bucket& rhs) { |
77 | 0 | return lhs.start == rhs.start && lhs.data == rhs.data; |
78 | 0 | } Unexecuted instantiation: spvtools::operator==(spvtools::EnumSet<spv::Capability>::Bucket const&, spvtools::EnumSet<spv::Capability>::Bucket const&) Unexecuted instantiation: spvtools::operator==(spvtools::EnumSet<spvtools::Extension>::Bucket const&, spvtools::EnumSet<spvtools::Extension>::Bucket const&) |
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 | 42.7k | : set_(other.set_), |
96 | 42.7k | bucketIndex_(other.bucketIndex_), |
97 | 42.7k | bucketOffset_(other.bucketOffset_) {} spvtools::EnumSet<spv::Capability>::Iterator::Iterator(spvtools::EnumSet<spv::Capability>::Iterator const&) Line | Count | Source | 95 | 39.4k | : set_(other.set_), | 96 | 39.4k | bucketIndex_(other.bucketIndex_), | 97 | 39.4k | bucketOffset_(other.bucketOffset_) {} |
spvtools::EnumSet<spvtools::Extension>::Iterator::Iterator(spvtools::EnumSet<spvtools::Extension>::Iterator const&) Line | Count | Source | 95 | 3.32k | : set_(other.set_), | 96 | 3.32k | bucketIndex_(other.bucketIndex_), | 97 | 3.32k | bucketOffset_(other.bucketOffset_) {} |
|
98 | | |
99 | 13.2k | Iterator& operator++() { |
100 | 435k | do { |
101 | 435k | if (bucketIndex_ >= set_->buckets_.size()) { |
102 | 0 | bucketIndex_ = set_->buckets_.size(); |
103 | 0 | bucketOffset_ = 0; |
104 | 0 | break; |
105 | 0 | } |
106 | | |
107 | 435k | if (bucketOffset_ + 1 == kBucketSize) { |
108 | 6.80k | bucketOffset_ = 0; |
109 | 6.80k | ++bucketIndex_; |
110 | 428k | } else { |
111 | 428k | ++bucketOffset_; |
112 | 428k | } |
113 | | |
114 | 435k | } while (bucketIndex_ < set_->buckets_.size() && |
115 | 429k | !set_->HasEnumAt(bucketIndex_, bucketOffset_)); |
116 | 13.2k | return *this; |
117 | 13.2k | } spvtools::EnumSet<spv::Capability>::Iterator::operator++() Line | Count | Source | 99 | 13.2k | Iterator& operator++() { | 100 | 435k | do { | 101 | 435k | if (bucketIndex_ >= set_->buckets_.size()) { | 102 | 0 | bucketIndex_ = set_->buckets_.size(); | 103 | 0 | bucketOffset_ = 0; | 104 | 0 | break; | 105 | 0 | } | 106 | | | 107 | 435k | if (bucketOffset_ + 1 == kBucketSize) { | 108 | 6.80k | bucketOffset_ = 0; | 109 | 6.80k | ++bucketIndex_; | 110 | 428k | } else { | 111 | 428k | ++bucketOffset_; | 112 | 428k | } | 113 | | | 114 | 435k | } while (bucketIndex_ < set_->buckets_.size() && | 115 | 429k | !set_->HasEnumAt(bucketIndex_, bucketOffset_)); | 116 | 13.2k | return *this; | 117 | 13.2k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::Iterator::operator++() |
118 | | |
119 | | Iterator operator++(int) { |
120 | | Iterator old = *this; |
121 | | operator++(); |
122 | | return old; |
123 | | } |
124 | | |
125 | 12.2k | T operator*() const { |
126 | 12.2k | assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && |
127 | 12.2k | "operator*() called on an invalid iterator."); |
128 | 12.2k | return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); |
129 | 12.2k | } spvtools::EnumSet<spv::Capability>::Iterator::operator*() const Line | Count | Source | 125 | 12.2k | T operator*() const { | 126 | | assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) && | 127 | 12.2k | "operator*() called on an invalid iterator."); | 128 | 12.2k | return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_); | 129 | 12.2k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::Iterator::operator*() const |
130 | | |
131 | 21.0k | bool operator!=(const Iterator& other) const { |
132 | 21.0k | return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || |
133 | 13.8k | bucketIndex_ != other.bucketIndex_; |
134 | 21.0k | } spvtools::EnumSet<spv::Capability>::Iterator::operator!=(spvtools::EnumSet<spv::Capability>::Iterator const&) const Line | Count | Source | 131 | 21.0k | bool operator!=(const Iterator& other) const { | 132 | 21.0k | return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ || | 133 | 13.8k | bucketIndex_ != other.bucketIndex_; | 134 | 21.0k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::Iterator::operator!=(spvtools::EnumSet<spvtools::Extension>::Iterator const&) const |
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 | 60.2k | : 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 | 56.9k | : 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 | 3.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 | 8.76k | iterator cbegin() const noexcept { |
168 | 8.76k | auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); |
169 | 8.76k | if (buckets_.size() == 0) { |
170 | 2.71k | return it; |
171 | 2.71k | } |
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 | 6.05k | if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { |
176 | 1.00k | ++it; |
177 | 1.00k | } |
178 | | |
179 | 6.05k | return it; |
180 | 8.76k | } spvtools::EnumSet<spv::Capability>::cbegin() const Line | Count | Source | 167 | 8.76k | iterator cbegin() const noexcept { | 168 | 8.76k | auto it = iterator(this, /* bucketIndex= */ 0, /* bucketOffset= */ 0); | 169 | 8.76k | if (buckets_.size() == 0) { | 170 | 2.71k | return it; | 171 | 2.71k | } | 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 | 6.05k | if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) { | 176 | 1.00k | ++it; | 177 | 1.00k | } | 178 | | | 179 | 6.05k | return it; | 180 | 8.76k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::cbegin() const |
181 | | |
182 | 8.76k | iterator begin() const noexcept { return cbegin(); } spvtools::EnumSet<spv::Capability>::begin() const Line | Count | Source | 182 | 8.76k | iterator begin() const noexcept { return cbegin(); } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::begin() const |
183 | | |
184 | 8.76k | iterator cend() const noexcept { |
185 | 8.76k | return iterator(this, buckets_.size(), /* bucketOffset= */ 0); |
186 | 8.76k | } spvtools::EnumSet<spv::Capability>::cend() const Line | Count | Source | 184 | 8.76k | iterator cend() const noexcept { | 185 | 8.76k | return iterator(this, buckets_.size(), /* bucketOffset= */ 0); | 186 | 8.76k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::cend() const |
187 | | |
188 | 8.76k | iterator end() const noexcept { return cend(); } spvtools::EnumSet<spv::Capability>::end() const Line | Count | Source | 188 | 8.76k | iterator end() const noexcept { return cend(); } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::end() const |
189 | | |
190 | | // Creates an empty set. |
191 | 586k | EnumSet() : buckets_(0), size_(0) {} spvtools::EnumSet<spv::Capability>::EnumSet() Line | Count | Source | 191 | 378k | EnumSet() : buckets_(0), size_(0) {} |
spvtools::EnumSet<spvtools::Extension>::EnumSet() Line | Count | Source | 191 | 207k | 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 | 7.54k | EnumSet(ElementType count, const T* array) : EnumSet() { |
205 | 12.9k | for (ElementType i = 0; i < count; i++) { |
206 | 5.39k | insert(array[i]); |
207 | 5.39k | } |
208 | 7.54k | } spvtools::EnumSet<spvtools::Extension>::EnumSet(unsigned int, spvtools::Extension const*) Line | Count | Source | 204 | 1.98k | EnumSet(ElementType count, const T* array) : EnumSet() { | 205 | 4.52k | for (ElementType i = 0; i < count; i++) { | 206 | 2.54k | insert(array[i]); | 207 | 2.54k | } | 208 | 1.98k | } |
spvtools::EnumSet<spv::Capability>::EnumSet(unsigned int, spv::Capability const*) Line | Count | Source | 204 | 5.56k | EnumSet(ElementType count, const T* array) : EnumSet() { | 205 | 8.41k | for (ElementType i = 0; i < count; i++) { | 206 | 2.85k | insert(array[i]); | 207 | 2.85k | } | 208 | 5.56k | } |
|
209 | | |
210 | | // Creates a set initialized with the content of the range [begin; end[. |
211 | | template <class InputIt> |
212 | 203k | EnumSet(InputIt begin, InputIt end) : EnumSet() { |
213 | 203k | for (; begin != end; ++begin) { |
214 | 8 | insert(*begin); |
215 | 8 | } |
216 | 203k | } spvtools::EnumSet<spvtools::Extension>::EnumSet<spvtools::Extension const*>(spvtools::Extension const*, spvtools::Extension const*) Line | Count | Source | 212 | 203k | EnumSet(InputIt begin, InputIt end) : EnumSet() { | 213 | 203k | for (; begin != end; ++begin) { | 214 | 8 | insert(*begin); | 215 | 8 | } | 216 | 203k | } |
Unexecuted instantiation: spvtools::EnumSet<spv::Capability>::EnumSet<spv::Capability const*>(spv::Capability const*, spv::Capability const*) |
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 | 42.1k | EnumSet& operator=(const EnumSet& other) { |
228 | 42.1k | buckets_ = other.buckets_; |
229 | 42.1k | size_ = other.size_; |
230 | 42.1k | return *this; |
231 | 42.1k | } |
232 | | |
233 | | // Matches std::unordered_set::insert behavior. |
234 | 42.7k | std::pair<iterator, bool> insert(const T& value) { |
235 | 42.7k | const size_t index = FindBucketForValue(value); |
236 | 42.7k | const ElementType offset = ComputeBucketOffset(value); |
237 | | |
238 | 42.7k | if (index >= buckets_.size() || |
239 | 37.6k | buckets_[index].start != ComputeBucketStart(value)) { |
240 | 37.6k | size_ += 1; |
241 | 37.6k | InsertBucketFor(index, value); |
242 | 37.6k | return std::make_pair(Iterator(this, index, offset), true); |
243 | 37.6k | } |
244 | | |
245 | 5.13k | auto& bucket = buckets_[index]; |
246 | 5.13k | const auto mask = ComputeMaskForValue(value); |
247 | 5.13k | if (bucket.data & mask) { |
248 | 0 | return std::make_pair(Iterator(this, index, offset), false); |
249 | 0 | } |
250 | | |
251 | 5.13k | size_ += 1; |
252 | 5.13k | bucket.data |= ComputeMaskForValue(value); |
253 | 5.13k | return std::make_pair(Iterator(this, index, offset), true); |
254 | 5.13k | } spvtools::EnumSet<spv::Capability>::insert(spv::Capability const&) Line | Count | Source | 234 | 39.4k | std::pair<iterator, bool> insert(const T& value) { | 235 | 39.4k | const size_t index = FindBucketForValue(value); | 236 | 39.4k | const ElementType offset = ComputeBucketOffset(value); | 237 | | | 238 | 39.4k | if (index >= buckets_.size() || | 239 | 34.4k | buckets_[index].start != ComputeBucketStart(value)) { | 240 | 34.4k | size_ += 1; | 241 | 34.4k | InsertBucketFor(index, value); | 242 | 34.4k | return std::make_pair(Iterator(this, index, offset), true); | 243 | 34.4k | } | 244 | | | 245 | 4.98k | auto& bucket = buckets_[index]; | 246 | 4.98k | const auto mask = ComputeMaskForValue(value); | 247 | 4.98k | if (bucket.data & mask) { | 248 | 0 | return std::make_pair(Iterator(this, index, offset), false); | 249 | 0 | } | 250 | | | 251 | 4.98k | size_ += 1; | 252 | 4.98k | bucket.data |= ComputeMaskForValue(value); | 253 | 4.98k | return std::make_pair(Iterator(this, index, offset), true); | 254 | 4.98k | } |
spvtools::EnumSet<spvtools::Extension>::insert(spvtools::Extension const&) Line | Count | Source | 234 | 3.32k | std::pair<iterator, bool> insert(const T& value) { | 235 | 3.32k | const size_t index = FindBucketForValue(value); | 236 | 3.32k | const ElementType offset = ComputeBucketOffset(value); | 237 | | | 238 | 3.32k | if (index >= buckets_.size() || | 239 | 3.17k | buckets_[index].start != ComputeBucketStart(value)) { | 240 | 3.17k | size_ += 1; | 241 | 3.17k | InsertBucketFor(index, value); | 242 | 3.17k | return std::make_pair(Iterator(this, index, offset), true); | 243 | 3.17k | } | 244 | | | 245 | 154 | auto& bucket = buckets_[index]; | 246 | 154 | const auto mask = ComputeMaskForValue(value); | 247 | 154 | if (bucket.data & mask) { | 248 | 0 | return std::make_pair(Iterator(this, index, offset), false); | 249 | 0 | } | 250 | | | 251 | 154 | size_ += 1; | 252 | 154 | bucket.data |= ComputeMaskForValue(value); | 253 | 154 | return std::make_pair(Iterator(this, index, offset), true); | 254 | 154 | } |
|
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 | 2.16M | bool contains(T value) const { |
305 | 2.16M | const size_t index = FindBucketForValue(value); |
306 | 2.16M | if (index >= buckets_.size() || |
307 | 1.40M | buckets_[index].start != ComputeBucketStart(value)) { |
308 | 772k | return false; |
309 | 772k | } |
310 | 1.39M | auto& bucket = buckets_[index]; |
311 | 1.39M | return bucket.data & ComputeMaskForValue(value); |
312 | 2.16M | } spvtools::EnumSet<spv::Capability>::contains(spv::Capability) const Line | Count | Source | 304 | 2.14M | bool contains(T value) const { | 305 | 2.14M | const size_t index = FindBucketForValue(value); | 306 | 2.14M | if (index >= buckets_.size() || | 307 | 1.40M | buckets_[index].start != ComputeBucketStart(value)) { | 308 | 754k | return false; | 309 | 754k | } | 310 | 1.38M | auto& bucket = buckets_[index]; | 311 | 1.38M | return bucket.data & ComputeMaskForValue(value); | 312 | 2.14M | } |
spvtools::EnumSet<spvtools::Extension>::contains(spvtools::Extension) const Line | Count | Source | 304 | 19.7k | bool contains(T value) const { | 305 | 19.7k | const size_t index = FindBucketForValue(value); | 306 | 19.7k | if (index >= buckets_.size() || | 307 | 18.1k | buckets_[index].start != ComputeBucketStart(value)) { | 308 | 18.1k | return false; | 309 | 18.1k | } | 310 | 1.60k | auto& bucket = buckets_[index]; | 311 | 1.60k | return bucket.data & ComputeMaskForValue(value); | 312 | 19.7k | } |
|
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 | 495k | inline bool empty() const { return size_ == 0; } spvtools::EnumSet<spvtools::Extension>::empty() const Line | Count | Source | 318 | 206k | inline bool empty() const { return size_ == 0; } |
spvtools::EnumSet<spv::Capability>::empty() const Line | Count | Source | 318 | 289k | 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 | 251k | bool HasAnyOf(const EnumSet<T>& in_set) const { |
326 | 251k | if (in_set.empty()) { |
327 | 225k | return true; |
328 | 225k | } |
329 | | |
330 | 25.1k | auto lhs = buckets_.cbegin(); |
331 | 25.1k | auto rhs = in_set.buckets_.cbegin(); |
332 | | |
333 | 31.6k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { |
334 | 31.6k | if (lhs->start == rhs->start) { |
335 | 25.9k | if (lhs->data & rhs->data) { |
336 | | // At least 1 bit is shared. Early return. |
337 | 25.1k | return true; |
338 | 25.1k | } |
339 | | |
340 | 762 | lhs++; |
341 | 762 | rhs++; |
342 | 762 | continue; |
343 | 25.9k | } |
344 | | |
345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. |
346 | 5.70k | if (lhs->start < rhs->start) { |
347 | 5.09k | lhs++; |
348 | 5.09k | continue; |
349 | 5.09k | } |
350 | | |
351 | | // Otherwise, RHS needs to catch up on LHS. |
352 | 608 | rhs++; |
353 | 608 | } |
354 | | |
355 | 0 | return false; |
356 | 25.1k | } spvtools::EnumSet<spv::Capability>::HasAnyOf(spvtools::EnumSet<spv::Capability> const&) const Line | Count | Source | 325 | 249k | bool HasAnyOf(const EnumSet<T>& in_set) const { | 326 | 249k | if (in_set.empty()) { | 327 | 225k | return true; | 328 | 225k | } | 329 | | | 330 | 23.2k | auto lhs = buckets_.cbegin(); | 331 | 23.2k | auto rhs = in_set.buckets_.cbegin(); | 332 | | | 333 | 29.0k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { | 334 | 29.0k | if (lhs->start == rhs->start) { | 335 | 24.0k | if (lhs->data & rhs->data) { | 336 | | // At least 1 bit is shared. Early return. | 337 | 23.2k | return true; | 338 | 23.2k | } | 339 | | | 340 | 762 | lhs++; | 341 | 762 | rhs++; | 342 | 762 | continue; | 343 | 24.0k | } | 344 | | | 345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. | 346 | 5.03k | if (lhs->start < rhs->start) { | 347 | 5.03k | lhs++; | 348 | 5.03k | continue; | 349 | 5.03k | } | 350 | | | 351 | | // Otherwise, RHS needs to catch up on LHS. | 352 | 0 | rhs++; | 353 | 0 | } | 354 | | | 355 | 0 | return false; | 356 | 23.2k | } |
spvtools::EnumSet<spvtools::Extension>::HasAnyOf(spvtools::EnumSet<spvtools::Extension> const&) const Line | Count | Source | 325 | 1.96k | bool HasAnyOf(const EnumSet<T>& in_set) const { | 326 | 1.96k | if (in_set.empty()) { | 327 | 76 | return true; | 328 | 76 | } | 329 | | | 330 | 1.88k | auto lhs = buckets_.cbegin(); | 331 | 1.88k | auto rhs = in_set.buckets_.cbegin(); | 332 | | | 333 | 2.55k | while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) { | 334 | 2.55k | if (lhs->start == rhs->start) { | 335 | 1.88k | if (lhs->data & rhs->data) { | 336 | | // At least 1 bit is shared. Early return. | 337 | 1.88k | return true; | 338 | 1.88k | } | 339 | | | 340 | 0 | lhs++; | 341 | 0 | rhs++; | 342 | 0 | continue; | 343 | 1.88k | } | 344 | | | 345 | | // LHS bucket is smaller than the current RHS bucket. Catching up on RHS. | 346 | 668 | if (lhs->start < rhs->start) { | 347 | 60 | lhs++; | 348 | 60 | continue; | 349 | 60 | } | 350 | | | 351 | | // Otherwise, RHS needs to catch up on LHS. | 352 | 608 | rhs++; | 353 | 608 | } | 354 | | | 355 | 0 | return false; | 356 | 1.88k | } |
|
357 | | |
358 | | private: |
359 | | // Returns the index of the last bucket in which `value` could be stored. |
360 | 5.76M | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { |
361 | 5.76M | return static_cast<size_t>(value) / kBucketSize; |
362 | 5.76M | } spvtools::EnumSet<spv::Capability>::ComputeLargestPossibleBucketIndexFor(spv::Capability) Line | Count | Source | 360 | 5.74M | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { | 361 | 5.74M | return static_cast<size_t>(value) / kBucketSize; | 362 | 5.74M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeLargestPossibleBucketIndexFor(spvtools::Extension) Line | Count | Source | 360 | 18.3k | static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) { | 361 | 18.3k | return static_cast<size_t>(value) / kBucketSize; | 362 | 18.3k | } |
|
363 | | |
364 | | // Returns the smallest enum value that could be contained in the same bucket |
365 | | // as `value`. |
366 | 3.60M | static constexpr inline T ComputeBucketStart(T value) { |
367 | 3.60M | return static_cast<T>(kBucketSize * |
368 | 3.60M | ComputeLargestPossibleBucketIndexFor(value)); |
369 | 3.60M | } spvtools::EnumSet<spv::Capability>::ComputeBucketStart(spv::Capability) Line | Count | Source | 366 | 3.59M | static constexpr inline T ComputeBucketStart(T value) { | 367 | 3.59M | return static_cast<T>(kBucketSize * | 368 | 3.59M | ComputeLargestPossibleBucketIndexFor(value)); | 369 | 3.59M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeBucketStart(spvtools::Extension) Line | Count | Source | 366 | 11.6k | static constexpr inline T ComputeBucketStart(T value) { | 367 | 11.6k | return static_cast<T>(kBucketSize * | 368 | 11.6k | ComputeLargestPossibleBucketIndexFor(value)); | 369 | 11.6k | } |
|
370 | | |
371 | | // Returns the index of the bit that corresponds to `value` in the bucket. |
372 | 1.48M | static constexpr inline ElementType ComputeBucketOffset(T value) { |
373 | 1.48M | return static_cast<ElementType>(value) % kBucketSize; |
374 | 1.48M | } spvtools::EnumSet<spv::Capability>::ComputeBucketOffset(spv::Capability) Line | Count | Source | 372 | 1.47M | static constexpr inline ElementType ComputeBucketOffset(T value) { | 373 | 1.47M | return static_cast<ElementType>(value) % kBucketSize; | 374 | 1.47M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeBucketOffset(spvtools::Extension) Line | Count | Source | 372 | 8.40k | static constexpr inline ElementType ComputeBucketOffset(T value) { | 373 | 8.40k | return static_cast<ElementType>(value) % kBucketSize; | 374 | 8.40k | } |
|
375 | | |
376 | | // Returns the bitmask used to represent the enum `value` in its bucket. |
377 | 1.40M | static constexpr inline BucketType ComputeMaskForValue(T value) { |
378 | 1.40M | return 1ULL << ComputeBucketOffset(value); |
379 | 1.40M | } spvtools::EnumSet<spv::Capability>::ComputeMaskForValue(spv::Capability) Line | Count | Source | 377 | 1.39M | static constexpr inline BucketType ComputeMaskForValue(T value) { | 378 | 1.39M | return 1ULL << ComputeBucketOffset(value); | 379 | 1.39M | } |
spvtools::EnumSet<spvtools::Extension>::ComputeMaskForValue(spvtools::Extension) Line | Count | Source | 377 | 1.91k | static constexpr inline BucketType ComputeMaskForValue(T value) { | 378 | 1.91k | return 1ULL << ComputeBucketOffset(value); | 379 | 1.91k | } |
|
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 | 12.2k | BucketType offset) { |
385 | 12.2k | return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); |
386 | 12.2k | } spvtools::EnumSet<spv::Capability>::GetValueFromBucket(spvtools::EnumSet<spv::Capability>::Bucket const&, unsigned long) Line | Count | Source | 384 | 12.2k | BucketType offset) { | 385 | 12.2k | return static_cast<T>(static_cast<ElementType>(bucket.start) + offset); | 386 | 12.2k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::GetValueFromBucket(spvtools::EnumSet<spvtools::Extension>::Bucket const&, unsigned long) |
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 | 2.20M | size_t FindBucketForValue(T value) const { |
392 | | // Set is empty, insert at 0. |
393 | 2.20M | if (buckets_.size() == 0) { |
394 | 47.6k | return 0; |
395 | 47.6k | } |
396 | | |
397 | 2.15M | const T wanted_start = ComputeBucketStart(value); |
398 | 2.15M | assert(buckets_.size() > 0 && |
399 | 2.15M | "Size must not be 0 here. Has the code above changed?"); |
400 | 2.15M | size_t index = std::min(buckets_.size() - 1, |
401 | 2.15M | 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 | 2.52M | for (; buckets_[index].start >= wanted_start; index--) { |
412 | 1.57M | if (index == 0) { |
413 | 1.19M | return 0; |
414 | 1.19M | } |
415 | 1.57M | } |
416 | | |
417 | 958k | return index + 1; |
418 | 2.15M | } spvtools::EnumSet<spv::Capability>::FindBucketForValue(spv::Capability) const Line | Count | Source | 391 | 2.18M | size_t FindBucketForValue(T value) const { | 392 | | // Set is empty, insert at 0. | 393 | 2.18M | if (buckets_.size() == 0) { | 394 | 31.2k | return 0; | 395 | 31.2k | } | 396 | | | 397 | 2.15M | const T wanted_start = ComputeBucketStart(value); | 398 | 2.15M | assert(buckets_.size() > 0 && | 399 | 2.15M | "Size must not be 0 here. Has the code above changed?"); | 400 | 2.15M | size_t index = std::min(buckets_.size() - 1, | 401 | 2.15M | 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 | 2.52M | for (; buckets_[index].start >= wanted_start; index--) { | 412 | 1.56M | if (index == 0) { | 413 | 1.19M | return 0; | 414 | 1.19M | } | 415 | 1.56M | } | 416 | | | 417 | 952k | return index + 1; | 418 | 2.15M | } |
spvtools::EnumSet<spvtools::Extension>::FindBucketForValue(spvtools::Extension) const Line | Count | Source | 391 | 23.1k | size_t FindBucketForValue(T value) const { | 392 | | // Set is empty, insert at 0. | 393 | 23.1k | if (buckets_.size() == 0) { | 394 | 16.4k | return 0; | 395 | 16.4k | } | 396 | | | 397 | 6.64k | const T wanted_start = ComputeBucketStart(value); | 398 | 6.64k | assert(buckets_.size() > 0 && | 399 | 6.64k | "Size must not be 0 here. Has the code above changed?"); | 400 | 6.64k | size_t index = std::min(buckets_.size() - 1, | 401 | 6.64k | 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 | 7.58k | for (; buckets_[index].start >= wanted_start; index--) { | 412 | 1.86k | if (index == 0) { | 413 | 920 | return 0; | 414 | 920 | } | 415 | 1.86k | } | 416 | | | 417 | 5.72k | return index + 1; | 418 | 6.64k | } |
|
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 | 37.6k | void InsertBucketFor(size_t index, T value) { |
424 | 37.6k | const T bucket_start = ComputeBucketStart(value); |
425 | 37.6k | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; |
426 | 37.6k | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); |
427 | 37.6k | #if defined(NDEBUG) |
428 | 37.6k | (void)it; // Silencing unused variable warning. |
429 | | #else |
430 | | assert(std::next(it) == buckets_.end() || |
431 | | std::next(it)->start > bucket_start); |
432 | | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); |
433 | | #endif |
434 | 37.6k | } spvtools::EnumSet<spv::Capability>::InsertBucketFor(unsigned long, spv::Capability) Line | Count | Source | 423 | 34.4k | void InsertBucketFor(size_t index, T value) { | 424 | 34.4k | const T bucket_start = ComputeBucketStart(value); | 425 | 34.4k | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; | 426 | 34.4k | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); | 427 | 34.4k | #if defined(NDEBUG) | 428 | 34.4k | (void)it; // Silencing unused variable warning. | 429 | | #else | 430 | | assert(std::next(it) == buckets_.end() || | 431 | | std::next(it)->start > bucket_start); | 432 | | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); | 433 | | #endif | 434 | 34.4k | } |
spvtools::EnumSet<spvtools::Extension>::InsertBucketFor(unsigned long, spvtools::Extension) Line | Count | Source | 423 | 3.17k | void InsertBucketFor(size_t index, T value) { | 424 | 3.17k | const T bucket_start = ComputeBucketStart(value); | 425 | 3.17k | Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start}; | 426 | 3.17k | auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket)); | 427 | 3.17k | #if defined(NDEBUG) | 428 | 3.17k | (void)it; // Silencing unused variable warning. | 429 | | #else | 430 | | assert(std::next(it) == buckets_.end() || | 431 | | std::next(it)->start > bucket_start); | 432 | | assert(it == buckets_.begin() || std::prev(it)->start < bucket_start); | 433 | | #endif | 434 | 3.17k | } |
|
435 | | |
436 | | // Returns true if the bucket at `bucketIndex/ stores the enum at |
437 | | // `bucketOffset`, false otherwise. |
438 | 435k | bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { |
439 | 435k | assert(bucketIndex < buckets_.size()); |
440 | 435k | assert(bucketOffset < kBucketSize); |
441 | 435k | return buckets_[bucketIndex].data & (1ULL << bucketOffset); |
442 | 435k | } spvtools::EnumSet<spv::Capability>::HasEnumAt(unsigned long, unsigned long) const Line | Count | Source | 438 | 435k | bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const { | 439 | 435k | assert(bucketIndex < buckets_.size()); | 440 | | assert(bucketOffset < kBucketSize); | 441 | 435k | return buckets_[bucketIndex].data & (1ULL << bucketOffset); | 442 | 435k | } |
Unexecuted instantiation: spvtools::EnumSet<spvtools::Extension>::HasEnumAt(unsigned long, unsigned long) const |
443 | | |
444 | | // Returns true if `lhs` and `rhs` hold the exact same values. |
445 | 0 | friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) { |
446 | 0 | if (lhs.size_ != rhs.size_) { |
447 | 0 | return false; |
448 | 0 | } |
449 | | |
450 | 0 | if (lhs.buckets_.size() != rhs.buckets_.size()) { |
451 | 0 | return false; |
452 | 0 | } |
453 | 0 | return lhs.buckets_ == rhs.buckets_; |
454 | 0 | } Unexecuted instantiation: spvtools::operator==(spvtools::EnumSet<spv::Capability> const&, spvtools::EnumSet<spv::Capability> const&) Unexecuted instantiation: spvtools::operator==(spvtools::EnumSet<spvtools::Extension> const&, spvtools::EnumSet<spvtools::Extension> const&) |
455 | | |
456 | | // Returns true if `lhs` and `rhs` hold at least 1 different value. |
457 | 0 | friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) { |
458 | 0 | return !(lhs == rhs); |
459 | 0 | } Unexecuted instantiation: spvtools::operator!=(spvtools::EnumSet<spv::Capability> const&, spvtools::EnumSet<spv::Capability> const&) Unexecuted instantiation: spvtools::operator!=(spvtools::EnumSet<spvtools::Extension> const&, spvtools::EnumSet<spvtools::Extension> const&) |
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_ |