Coverage Report

Created: 2026-08-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_