#include <algorithm>
#include <cassert>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <limits>
#include <type_traits>
#include <vector>
#ifndef SOURCE_ENUM_SET_H_
#define SOURCE_ENUM_SET_H_
#include "source/latest_version_spirv_header.h"
namespace spvtools {
template <typename T>
class EnumSet {
private:
using BucketType = uint64_t;
using ElementType = std::underlying_type_t<T>;
static_assert(std::is_enum_v<T>, "EnumSets only works with enums.");
static_assert(std::is_signed_v<ElementType> == false,
"EnumSet doesn't supports signed enums.");
struct Bucket {
BucketType data;
T start;
friend bool operator==(const Bucket& lhs, const Bucket& rhs) {
return lhs.start == rhs.start && lhs.data == rhs.data;
}
};
static constexpr size_t kBucketSize = sizeof(BucketType) * 8ULL;
public:
class Iterator {
public:
typedef Iterator self_type;
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef std::forward_iterator_tag iterator_category;
typedef size_t difference_type;
Iterator(const Iterator& other)
: set_(other.set_),
bucketIndex_(other.bucketIndex_),
bucketOffset_(other.bucketOffset_) {}
Iterator& operator++() {
do {
if (bucketIndex_ >= set_->buckets_.size()) {
bucketIndex_ = set_->buckets_.size();
bucketOffset_ = 0;
break;
}
if (bucketOffset_ + 1 == kBucketSize) {
bucketOffset_ = 0;
++bucketIndex_;
} else {
++bucketOffset_;
}
} while (bucketIndex_ < set_->buckets_.size() &&
!set_->HasEnumAt(bucketIndex_, bucketOffset_));
return *this;
}
Iterator operator++(int) {
Iterator old = *this;
operator++();
return old;
}
T operator*() const {
assert(set_->HasEnumAt(bucketIndex_, bucketOffset_) &&
"operator*() called on an invalid iterator.");
return GetValueFromBucket(set_->buckets_[bucketIndex_], bucketOffset_);
}
bool operator!=(const Iterator& other) const {
return set_ != other.set_ || bucketOffset_ != other.bucketOffset_ ||
bucketIndex_ != other.bucketIndex_;
}
bool operator==(const Iterator& other) const {
return !(operator!=(other));
}
Iterator& operator=(const Iterator& other) {
set_ = other.set_;
bucketIndex_ = other.bucketIndex_;
bucketOffset_ = other.bucketOffset_;
return *this;
}
private:
Iterator(const EnumSet* set, size_t bucketIndex, ElementType bucketOffset)
: set_(set), bucketIndex_(bucketIndex), bucketOffset_(bucketOffset) {}
private:
const EnumSet* set_ = nullptr;
size_t bucketIndex_ = 0;
ElementType bucketOffset_ = 0;
friend class EnumSet;
};
using value_type = T;
using const_iterator = Iterator;
using iterator = Iterator;
public:
iterator cbegin() const noexcept {
auto it = iterator(this, 0, 0);
if (buckets_.size() == 0) {
return it;
}
if (!HasEnumAt(it.bucketIndex_, it.bucketOffset_)) {
++it;
}
return it;
}
iterator begin() const noexcept { return cbegin(); }
iterator cend() const noexcept {
return iterator(this, buckets_.size(), 0);
}
iterator end() const noexcept { return cend(); }
EnumSet() : buckets_(0), size_(0) {}
EnumSet(T value) : EnumSet() { insert(value); }
EnumSet(std::initializer_list<T> values) : EnumSet() {
for (auto item : values) {
insert(item);
}
}
EnumSet(ElementType count, const T* array) : EnumSet() {
for (ElementType i = 0; i < count; i++) {
insert(array[i]);
}
}
template <class InputIt>
EnumSet(InputIt begin, InputIt end) : EnumSet() {
for (; begin != end; ++begin) {
insert(*begin);
}
}
EnumSet(const EnumSet& other)
: buckets_(other.buckets_), size_(other.size_) {}
EnumSet(EnumSet&& other)
: buckets_(std::move(other.buckets_)), size_(other.size_) {}
EnumSet& operator=(const EnumSet& other) {
buckets_ = other.buckets_;
size_ = other.size_;
return *this;
}
std::pair<iterator, bool> insert(const T& value) {
const size_t index = FindBucketForValue(value);
const ElementType offset = ComputeBucketOffset(value);
if (index >= buckets_.size() ||
buckets_[index].start != ComputeBucketStart(value)) {
size_ += 1;
InsertBucketFor(index, value);
return std::make_pair(Iterator(this, index, offset), true);
}
auto& bucket = buckets_[index];
const auto mask = ComputeMaskForValue(value);
if (bucket.data & mask) {
return std::make_pair(Iterator(this, index, offset), false);
}
size_ += 1;
bucket.data |= ComputeMaskForValue(value);
return std::make_pair(Iterator(this, index, offset), true);
}
iterator insert(const_iterator, const T& value) {
return insert(value).first;
}
iterator insert(const_iterator, T&& value) { return insert(value).first; }
template <class InputIt>
void insert(InputIt first, InputIt last) {
for (auto it = first; it != last; ++it) {
insert(*it);
}
}
size_t erase(const T& value) {
const size_t index = FindBucketForValue(value);
if (index >= buckets_.size() ||
buckets_[index].start != ComputeBucketStart(value)) {
return 0;
}
auto& bucket = buckets_[index];
const auto mask = ComputeMaskForValue(value);
if (!(bucket.data & mask)) {
return 0;
}
size_ -= 1;
bucket.data &= ~mask;
if (bucket.data == 0) {
buckets_.erase(buckets_.cbegin() + index);
}
return 1;
}
bool contains(T value) const {
const size_t index = FindBucketForValue(value);
if (index >= buckets_.size() ||
buckets_[index].start != ComputeBucketStart(value)) {
return false;
}
auto& bucket = buckets_[index];
return bucket.data & ComputeMaskForValue(value);
}
inline size_t count(T value) const { return contains(value) ? 1 : 0; }
inline bool empty() const { return size_ == 0; }
size_t size() const { return size_; }
bool HasAnyOf(const EnumSet<T>& in_set) const {
if (in_set.empty()) {
return true;
}
auto lhs = buckets_.cbegin();
auto rhs = in_set.buckets_.cbegin();
while (lhs != buckets_.cend() && rhs != in_set.buckets_.cend()) {
if (lhs->start == rhs->start) {
if (lhs->data & rhs->data) {
return true;
}
lhs++;
rhs++;
continue;
}
if (lhs->start < rhs->start) {
lhs++;
continue;
}
rhs++;
}
return false;
}
private:
static constexpr inline size_t ComputeLargestPossibleBucketIndexFor(T value) {
return static_cast<size_t>(value) / kBucketSize;
}
static constexpr inline T ComputeBucketStart(T value) {
return static_cast<T>(kBucketSize *
ComputeLargestPossibleBucketIndexFor(value));
}
static constexpr inline ElementType ComputeBucketOffset(T value) {
return static_cast<ElementType>(value) % kBucketSize;
}
static constexpr inline BucketType ComputeMaskForValue(T value) {
return 1ULL << ComputeBucketOffset(value);
}
static constexpr inline T GetValueFromBucket(const Bucket& bucket,
BucketType offset) {
return static_cast<T>(static_cast<ElementType>(bucket.start) + offset);
}
size_t FindBucketForValue(T value) const {
if (buckets_.size() == 0) {
return 0;
}
const T wanted_start = ComputeBucketStart(value);
assert(buckets_.size() > 0 &&
"Size must not be 0 here. Has the code above changed?");
size_t index = std::min(buckets_.size() - 1,
ComputeLargestPossibleBucketIndexFor(value));
for (; buckets_[index].start >= wanted_start; index--) {
if (index == 0) {
return 0;
}
}
return index + 1;
}
void InsertBucketFor(size_t index, T value) {
const T bucket_start = ComputeBucketStart(value);
Bucket bucket = {1ULL << ComputeBucketOffset(value), bucket_start};
auto it = buckets_.emplace(buckets_.begin() + index, std::move(bucket));
#if defined(NDEBUG)
(void)it;
#else
assert(std::next(it) == buckets_.end() ||
std::next(it)->start > bucket_start);
assert(it == buckets_.begin() || std::prev(it)->start < bucket_start);
#endif
}
bool HasEnumAt(size_t bucketIndex, BucketType bucketOffset) const {
assert(bucketIndex < buckets_.size());
assert(bucketOffset < kBucketSize);
return buckets_[bucketIndex].data & (1ULL << bucketOffset);
}
friend bool operator==(const EnumSet& lhs, const EnumSet& rhs) {
if (lhs.size_ != rhs.size_) {
return false;
}
if (lhs.buckets_.size() != rhs.buckets_.size()) {
return false;
}
return lhs.buckets_ == rhs.buckets_;
}
friend bool operator!=(const EnumSet& lhs, const EnumSet& rhs) {
return !(lhs == rhs);
}
std::vector<Bucket> buckets_;
size_t size_ = 0;
};
using CapabilitySet = EnumSet<spv::Capability>;
}
#endif