// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // FastVector.h: // A vector class with a initial fixed size and variable growth. // Based on FixedVector. // #ifndef COMMON_FASTVECTOR_H_ #define COMMON_FASTVECTOR_H_ #include "bitset_utils.h" #include "common/debug.h" #include #include #include #include namespace angle { template class WrapIter { public: typedef Iter iterator_type; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef typename std::iterator_traits::pointer pointer; typedef typename std::iterator_traits::reference reference; typedef typename std::iterator_traits::iterator_category iterator_category; WrapIter() : mIter() {} WrapIter(const Iter &iter) : mIter(iter) {} ~WrapIter() = default; WrapIter &operator=(const WrapIter &x) { mIter = x.mIter; return *this; } bool operator==(const WrapIter &x) const { return mIter == x.mIter; } bool operator!=(const WrapIter &x) const { return mIter != x.mIter; } bool operator<(const WrapIter &x) const { return mIter < x.mIter; } bool operator<=(const WrapIter &x) const { return mIter <= x.mIter; } bool operator>(const WrapIter &x) const { return mIter > x.mIter; } bool operator>=(const WrapIter &x) const { return mIter >= x.mIter; } WrapIter &operator++() { mIter++; return *this; } WrapIter operator++(int) { WrapIter tmp(mIter); mIter++; return tmp; } WrapIter operator+(difference_type n) { WrapIter tmp(mIter); tmp.mIter += n; return tmp; } WrapIter operator-(difference_type n) { WrapIter tmp(mIter); tmp.mIter -= n; return tmp; } difference_type operator-(const WrapIter &x) const { return mIter - x.mIter; } iterator_type operator->() const { return mIter; } reference operator*() const { return *mIter; } private: iterator_type mIter; }; template > class FastVector final { public: using value_type = typename Storage::value_type; using size_type = typename Storage::size_type; using reference = typename Storage::reference; using const_reference = typename Storage::const_reference; using pointer = typename Storage::pointer; using const_pointer = typename Storage::const_pointer; using iterator = WrapIter; using const_iterator = WrapIter; FastVector(); FastVector(size_type count, const value_type &value); FastVector(size_type count); FastVector(const FastVector &other); FastVector(FastVector &&other); FastVector(std::initializer_list init); template ::value, bool> = true> FastVector(InputIt first, InputIt last); FastVector &operator=(const FastVector &other); FastVector &operator=(FastVector &&other); FastVector &operator=(std::initializer_list init); ~FastVector(); reference at(size_type pos); const_reference at(size_type pos) const; reference operator[](size_type pos); const_reference operator[](size_type pos) const; pointer data(); const_pointer data() const; iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; bool empty() const; size_type size() const; void clear(); void push_back(const value_type &value); void push_back(value_type &&value); template void emplace_back(Args &&...args); void pop_back(); reference front(); const_reference front() const; reference back(); const_reference back() const; void swap(FastVector &other); void resize(size_type count); void resize(size_type count, const value_type &value); void reserve(size_type count); // Specialty function that removes a known element and might shuffle the list. void remove_and_permute(const value_type &element); void remove_and_permute(iterator pos); private: void assign_from_initializer_list(std::initializer_list init); void ensure_capacity(size_t capacity); bool uses_fixed_storage() const; Storage mFixedStorage; pointer mData = mFixedStorage.data(); size_type mSize = 0; size_type mReservedSize = N; }; template bool operator==(const FastVector &a, const FastVector &b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } template bool operator!=(const FastVector &a, const FastVector &b) { return !(a == b); } template ANGLE_INLINE bool FastVector::uses_fixed_storage() const { return mData == mFixedStorage.data(); } template FastVector::FastVector() {} template FastVector::FastVector(size_type count, const value_type &value) { ensure_capacity(count); mSize = count; std::fill(begin(), end(), value); } template FastVector::FastVector(size_type count) { ensure_capacity(count); mSize = count; } template FastVector::FastVector(const FastVector &other) : FastVector(other.begin(), other.end()) {} template FastVector::FastVector(FastVector &&other) : FastVector() { swap(other); } template FastVector::FastVector(std::initializer_list init) { assign_from_initializer_list(init); } template template ::value, bool>> FastVector::FastVector(InputIt first, InputIt last) { size_t newSize = last - first; ensure_capacity(newSize); mSize = newSize; std::copy(first, last, begin()); } template FastVector &FastVector::operator=( const FastVector &other) { ensure_capacity(other.mSize); mSize = other.mSize; std::copy(other.begin(), other.end(), begin()); return *this; } template FastVector &FastVector::operator=(FastVector &&other) { swap(other); return *this; } template FastVector &FastVector::operator=( std::initializer_list init) { assign_from_initializer_list(init); return *this; } template FastVector::~FastVector() { clear(); if (!uses_fixed_storage()) { delete[] mData; } } template typename FastVector::reference FastVector::at(size_type pos) { ASSERT(pos < mSize); return mData[pos]; } template typename FastVector::const_reference FastVector::at( size_type pos) const { ASSERT(pos < mSize); return mData[pos]; } template ANGLE_INLINE typename FastVector::reference FastVector::operator[]( size_type pos) { ASSERT(pos < mSize); return mData[pos]; } template ANGLE_INLINE typename FastVector::const_reference FastVector::operator[](size_type pos) const { ASSERT(pos < mSize); return mData[pos]; } template ANGLE_INLINE typename FastVector::const_pointer angle::FastVector::data() const { return mData; } template ANGLE_INLINE typename FastVector::pointer angle::FastVector::data() { return mData; } template ANGLE_INLINE typename FastVector::iterator FastVector::begin() { return mData; } template ANGLE_INLINE typename FastVector::const_iterator FastVector::begin() const { return mData; } template ANGLE_INLINE typename FastVector::iterator FastVector::end() { return mData + mSize; } template ANGLE_INLINE typename FastVector::const_iterator FastVector::end() const { return mData + mSize; } template ANGLE_INLINE bool FastVector::empty() const { return mSize == 0; } template ANGLE_INLINE typename FastVector::size_type FastVector::size() const { return mSize; } template void FastVector::clear() { resize(0); } template ANGLE_INLINE void FastVector::push_back(const value_type &value) { if (mSize == mReservedSize) ensure_capacity(mSize + 1); mData[mSize++] = value; } template ANGLE_INLINE void FastVector::push_back(value_type &&value) { emplace_back(std::move(value)); } template template ANGLE_INLINE void FastVector::emplace_back(Args &&...args) { if (mSize == mReservedSize) ensure_capacity(mSize + 1); mData[mSize++] = std::move(T(std::forward(args)...)); } template ANGLE_INLINE void FastVector::pop_back() { ASSERT(mSize > 0); mSize--; } template ANGLE_INLINE typename FastVector::reference FastVector::front() { ASSERT(mSize > 0); return mData[0]; } template ANGLE_INLINE typename FastVector::const_reference FastVector::front() const { ASSERT(mSize > 0); return mData[0]; } template ANGLE_INLINE typename FastVector::reference FastVector::back() { ASSERT(mSize > 0); return mData[mSize - 1]; } template ANGLE_INLINE typename FastVector::const_reference FastVector::back() const { ASSERT(mSize > 0); return mData[mSize - 1]; } template void FastVector::swap(FastVector &other) { std::swap(mSize, other.mSize); pointer tempData = other.mData; if (uses_fixed_storage()) other.mData = other.mFixedStorage.data(); else other.mData = mData; if (tempData == other.mFixedStorage.data()) mData = mFixedStorage.data(); else mData = tempData; std::swap(mReservedSize, other.mReservedSize); if (uses_fixed_storage() || other.uses_fixed_storage()) std::swap(mFixedStorage, other.mFixedStorage); } template void FastVector::resize(size_type count) { if (count > mSize) { ensure_capacity(count); } mSize = count; } template void FastVector::resize(size_type count, const value_type &value) { if (count > mSize) { ensure_capacity(count); std::fill(mData + mSize, mData + count, value); } mSize = count; } template void FastVector::reserve(size_type count) { ensure_capacity(count); } template void FastVector::assign_from_initializer_list(std::initializer_list init) { ensure_capacity(init.size()); mSize = init.size(); size_t index = 0; for (auto &value : init) { mData[index++] = value; } } template ANGLE_INLINE void FastVector::remove_and_permute(const value_type &element) { size_t len = mSize - 1; for (size_t index = 0; index < len; ++index) { if (mData[index] == element) { mData[index] = std::move(mData[len]); break; } } pop_back(); } template ANGLE_INLINE void FastVector::remove_and_permute(iterator pos) { ASSERT(pos >= begin()); ASSERT(pos < end()); size_t len = mSize - 1; *pos = std::move(mData[len]); pop_back(); } template void FastVector::ensure_capacity(size_t capacity) { // We have a minimum capacity of N. if (mReservedSize < capacity) { ASSERT(capacity > N); size_type newSize = std::max(mReservedSize, N); while (newSize < capacity) { newSize *= 2; } pointer newData = new value_type[newSize]; if (mSize > 0) { std::move(begin(), end(), newData); } if (!uses_fixed_storage()) { delete[] mData; } mData = newData; mReservedSize = newSize; } } template class FastMap final { public: FastMap() {} ~FastMap() {} Value &operator[](uint32_t key) { if (mData.size() <= key) { mData.resize(key + 1, {}); } return mData[key]; } const Value &operator[](uint32_t key) const { ASSERT(key < mData.size()); return mData[key]; } void clear() { mData.clear(); } bool empty() const { return mData.empty(); } size_t size() const { return mData.size(); } const Value *data() const { return mData.data(); } bool operator==(const FastMap &other) const { return (size() == other.size()) && (memcmp(data(), other.data(), size() * sizeof(Value)) == 0); } private: FastVector mData; }; template class FlatUnorderedMap final { public: using Pair = std::pair; using Storage = FastVector; using iterator = typename Storage::iterator; using const_iterator = typename Storage::const_iterator; FlatUnorderedMap() = default; ~FlatUnorderedMap() = default; iterator begin() { return mData.begin(); } const_iterator begin() const { return mData.begin(); } iterator end() { return mData.end(); } const_iterator end() const { return mData.end(); } iterator find(const Key &key) { for (auto it = mData.begin(); it != mData.end(); ++it) { if (it->first == key) { return it; } } return mData.end(); } const_iterator find(const Key &key) const { for (auto it = mData.begin(); it != mData.end(); ++it) { if (it->first == key) { return it; } } return mData.end(); } Value &operator[](const Key &key) { iterator it = find(key); if (it != end()) { return it->second; } mData.push_back(Pair(key, {})); return mData.back().second; } void insert(Pair pair) { ASSERT(!contains(pair.first)); mData.push_back(std::move(pair)); } void insert(const Key &key, Value value) { insert(Pair(key, value)); } void erase(iterator pos) { mData.remove_and_permute(pos); } bool contains(const Key &key) const { return find(key) != end(); } void clear() { mData.clear(); } bool get(const Key &key, Value *value) const { auto it = find(key); if (it != end()) { *value = it->second; return true; } return false; } bool empty() const { return mData.empty(); } size_t size() const { return mData.size(); } private: FastVector mData; }; template class FlatUnorderedSet final { public: using Storage = FastVector; using iterator = typename Storage::iterator; using const_iterator = typename Storage::const_iterator; FlatUnorderedSet() = default; ~FlatUnorderedSet() = default; iterator begin() { return mData.begin(); } const_iterator begin() const { return mData.begin(); } iterator end() { return mData.end(); } const_iterator end() const { return mData.end(); } iterator find(const T &value) { for (auto it = mData.begin(); it != mData.end(); ++it) { if (*it == value) { return it; } } return mData.end(); } const_iterator find(const T &value) const { for (auto it = mData.begin(); it != mData.end(); ++it) { if (*it == value) { return it; } } return mData.end(); } bool empty() const { return mData.empty(); } void insert(const T &value) { ASSERT(!contains(value)); mData.push_back(value); } void erase(const T &value) { ASSERT(contains(value)); mData.remove_and_permute(value); } void remove(const T &value) { erase(value); } bool contains(const T &value) const { return find(value) != end(); } void clear() { mData.clear(); } bool operator==(const FlatUnorderedSet &other) const { return mData == other.mData; } private: Storage mData; }; class FastIntegerSet final { public: static constexpr size_t kWindowSize = 64; static constexpr size_t kOneLessThanKWindowSize = kWindowSize - 1; static constexpr size_t kShiftForDivision = static_cast(rx::Log2(static_cast(kWindowSize))); using KeyBitSet = angle::BitSet64; ANGLE_INLINE FastIntegerSet(); ANGLE_INLINE ~FastIntegerSet(); ANGLE_INLINE void ensureCapacity(size_t size) { if (capacity() <= size) { reserve(size * 2); } } ANGLE_INLINE void insert(uint64_t key) { size_t sizedKey = static_cast(key); ASSERT(!contains(sizedKey)); ensureCapacity(sizedKey); ASSERT(capacity() > sizedKey); size_t index = sizedKey >> kShiftForDivision; size_t offset = sizedKey & kOneLessThanKWindowSize; mKeyData[index].set(offset, true); } ANGLE_INLINE bool contains(uint64_t key) const { size_t sizedKey = static_cast(key); size_t index = sizedKey >> kShiftForDivision; size_t offset = sizedKey & kOneLessThanKWindowSize; return (sizedKey < capacity()) && (mKeyData[index].test(offset)); } ANGLE_INLINE void clear() { for (KeyBitSet &it : mKeyData) { it.reset(); } } ANGLE_INLINE bool empty() const { for (const KeyBitSet &it : mKeyData) { if (it.any()) { return false; } } return true; } ANGLE_INLINE size_t size() const { size_t valid_entries = 0; for (const KeyBitSet &it : mKeyData) { valid_entries += it.count(); } return valid_entries; } private: ANGLE_INLINE size_t capacity() const { return kWindowSize * mKeyData.size(); } ANGLE_INLINE void reserve(size_t newSize) { size_t alignedSize = rx::roundUpPow2(newSize, kWindowSize); size_t count = alignedSize >> kShiftForDivision; mKeyData.resize(count, KeyBitSet::Zero()); } std::vector mKeyData; }; // This is needed to accommodate the chromium style guide error - // [chromium-style] Complex constructor has an inlined body. ANGLE_INLINE FastIntegerSet::FastIntegerSet() {} ANGLE_INLINE FastIntegerSet::~FastIntegerSet() {} template class FastIntegerMap final { public: FastIntegerMap() {} ~FastIntegerMap() {} ANGLE_INLINE void ensureCapacity(size_t size) { // Ensure key set has capacity mKeySet.ensureCapacity(size); // Ensure value vector has capacity ensureCapacityImpl(size); } ANGLE_INLINE void insert(uint64_t key, Value value) { // Insert key ASSERT(!mKeySet.contains(key)); mKeySet.insert(key); // Insert value size_t sizedKey = static_cast(key); ensureCapacityImpl(sizedKey); ASSERT(capacity() > sizedKey); mValueData[sizedKey] = value; } ANGLE_INLINE bool contains(uint64_t key) const { return mKeySet.contains(key); } ANGLE_INLINE bool get(uint64_t key, Value *out) const { if (!mKeySet.contains(key)) { return false; } size_t sizedKey = static_cast(key); *out = mValueData[sizedKey]; return true; } ANGLE_INLINE void clear() { mKeySet.clear(); } ANGLE_INLINE bool empty() const { return mKeySet.empty(); } ANGLE_INLINE size_t size() const { return mKeySet.size(); } private: ANGLE_INLINE size_t capacity() const { return mValueData.size(); } ANGLE_INLINE void ensureCapacityImpl(size_t size) { if (capacity() <= size) { reserve(size * 2); } } ANGLE_INLINE void reserve(size_t newSize) { size_t alignedSize = rx::roundUpPow2(newSize, FastIntegerSet::kWindowSize); mValueData.resize(alignedSize); } FastIntegerSet mKeySet; std::vector mValueData; }; } // namespace angle #endif // COMMON_FASTVECTOR_H_