| // |
| // 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 <algorithm> |
| #include <array> |
| #include <initializer_list> |
| |
| namespace angle |
| { |
| template <class T, size_t N, class Storage = std::array<T, N>> |
| 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 = T *; |
| using const_iterator = const T *; |
| |
| FastVector(); |
| FastVector(size_type count, const value_type &value); |
| FastVector(size_type count); |
| |
| FastVector(const FastVector<T, N, Storage> &other); |
| FastVector(FastVector<T, N, Storage> &&other); |
| FastVector(std::initializer_list<value_type> init); |
| |
| template <class InputIt, std::enable_if_t<!std::is_integral<InputIt>::value, bool> = true> |
| FastVector(InputIt first, InputIt last); |
| |
| FastVector<T, N, Storage> &operator=(const FastVector<T, N, Storage> &other); |
| FastVector<T, N, Storage> &operator=(FastVector<T, N, Storage> &&other); |
| FastVector<T, N, Storage> &operator=(std::initializer_list<value_type> 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 <typename... Args> |
| void emplace_back(Args &&...args); |
| |
| void pop_back(); |
| |
| reference front(); |
| const_reference front() const; |
| |
| reference back(); |
| const_reference back() const; |
| |
| void swap(FastVector<T, N, Storage> &other); |
| |
| void resize(size_type count); |
| void resize(size_type count, const value_type &value); |
| |
| // Specialty function that removes a known element and might shuffle the list. |
| void remove_and_permute(const value_type &element); |
| |
| private: |
| void assign_from_initializer_list(std::initializer_list<value_type> 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 <class T, size_t N, class StorageN, size_t M, class StorageM> |
| bool operator==(const FastVector<T, N, StorageN> &a, const FastVector<T, M, StorageM> &b) |
| { |
| return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); |
| } |
| |
| template <class T, size_t N, class StorageN, size_t M, class StorageM> |
| bool operator!=(const FastVector<T, N, StorageN> &a, const FastVector<T, M, StorageM> &b) |
| { |
| return !(a == b); |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE bool FastVector<T, N, Storage>::uses_fixed_storage() const |
| { |
| return mData == mFixedStorage.data(); |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector() |
| {} |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector(size_type count, const value_type &value) |
| { |
| ensure_capacity(count); |
| mSize = count; |
| std::fill(begin(), end(), value); |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector(size_type count) |
| { |
| ensure_capacity(count); |
| mSize = count; |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector(const FastVector<T, N, Storage> &other) |
| : FastVector(other.begin(), other.end()) |
| {} |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector(FastVector<T, N, Storage> &&other) : FastVector() |
| { |
| swap(other); |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::FastVector(std::initializer_list<value_type> init) |
| { |
| assign_from_initializer_list(init); |
| } |
| |
| template <class T, size_t N, class Storage> |
| template <class InputIt, std::enable_if_t<!std::is_integral<InputIt>::value, bool>> |
| FastVector<T, N, Storage>::FastVector(InputIt first, InputIt last) |
| { |
| size_t newSize = last - first; |
| ensure_capacity(newSize); |
| mSize = newSize; |
| std::copy(first, last, begin()); |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage> &FastVector<T, N, Storage>::operator=( |
| const FastVector<T, N, Storage> &other) |
| { |
| ensure_capacity(other.mSize); |
| mSize = other.mSize; |
| std::copy(other.begin(), other.end(), begin()); |
| return *this; |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage> &FastVector<T, N, Storage>::operator=(FastVector<T, N, Storage> &&other) |
| { |
| swap(other); |
| return *this; |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage> &FastVector<T, N, Storage>::operator=( |
| std::initializer_list<value_type> init) |
| { |
| assign_from_initializer_list(init); |
| return *this; |
| } |
| |
| template <class T, size_t N, class Storage> |
| FastVector<T, N, Storage>::~FastVector() |
| { |
| clear(); |
| if (!uses_fixed_storage()) |
| { |
| delete[] mData; |
| } |
| } |
| |
| template <class T, size_t N, class Storage> |
| typename FastVector<T, N, Storage>::reference FastVector<T, N, Storage>::at(size_type pos) |
| { |
| ASSERT(pos < mSize); |
| return mData[pos]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| typename FastVector<T, N, Storage>::const_reference FastVector<T, N, Storage>::at( |
| size_type pos) const |
| { |
| ASSERT(pos < mSize); |
| return mData[pos]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::reference FastVector<T, N, Storage>::operator[]( |
| size_type pos) |
| { |
| ASSERT(pos < mSize); |
| return mData[pos]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_reference |
| FastVector<T, N, Storage>::operator[](size_type pos) const |
| { |
| ASSERT(pos < mSize); |
| return mData[pos]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_pointer |
| angle::FastVector<T, N, Storage>::data() const |
| { |
| return mData; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::pointer angle::FastVector<T, N, Storage>::data() |
| { |
| return mData; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::iterator FastVector<T, N, Storage>::begin() |
| { |
| return mData; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_iterator FastVector<T, N, Storage>::begin() |
| const |
| { |
| return mData; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::iterator FastVector<T, N, Storage>::end() |
| { |
| return mData + mSize; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_iterator FastVector<T, N, Storage>::end() |
| const |
| { |
| return mData + mSize; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE bool FastVector<T, N, Storage>::empty() const |
| { |
| return mSize == 0; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::size_type FastVector<T, N, Storage>::size() const |
| { |
| return mSize; |
| } |
| |
| template <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::clear() |
| { |
| resize(0); |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE void FastVector<T, N, Storage>::push_back(const value_type &value) |
| { |
| if (mSize == mReservedSize) |
| ensure_capacity(mSize + 1); |
| mData[mSize++] = value; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE void FastVector<T, N, Storage>::push_back(value_type &&value) |
| { |
| emplace_back(std::move(value)); |
| } |
| |
| template <class T, size_t N, class Storage> |
| template <typename... Args> |
| ANGLE_INLINE void FastVector<T, N, Storage>::emplace_back(Args &&...args) |
| { |
| if (mSize == mReservedSize) |
| ensure_capacity(mSize + 1); |
| mData[mSize++] = std::move(T(std::forward<Args>(args)...)); |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE void FastVector<T, N, Storage>::pop_back() |
| { |
| ASSERT(mSize > 0); |
| mSize--; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::reference FastVector<T, N, Storage>::front() |
| { |
| ASSERT(mSize > 0); |
| return mData[0]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_reference FastVector<T, N, Storage>::front() |
| const |
| { |
| ASSERT(mSize > 0); |
| return mData[0]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::reference FastVector<T, N, Storage>::back() |
| { |
| ASSERT(mSize > 0); |
| return mData[mSize - 1]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE typename FastVector<T, N, Storage>::const_reference FastVector<T, N, Storage>::back() |
| const |
| { |
| ASSERT(mSize > 0); |
| return mData[mSize - 1]; |
| } |
| |
| template <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::swap(FastVector<T, N, Storage> &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 <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::resize(size_type count) |
| { |
| if (count > mSize) |
| { |
| ensure_capacity(count); |
| } |
| mSize = count; |
| } |
| |
| template <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::resize(size_type count, const value_type &value) |
| { |
| if (count > mSize) |
| { |
| ensure_capacity(count); |
| std::fill(mData + mSize, mData + count, value); |
| } |
| mSize = count; |
| } |
| |
| template <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::assign_from_initializer_list(std::initializer_list<value_type> init) |
| { |
| ensure_capacity(init.size()); |
| mSize = init.size(); |
| size_t index = 0; |
| for (auto &value : init) |
| { |
| mData[index++] = value; |
| } |
| } |
| |
| template <class T, size_t N, class Storage> |
| ANGLE_INLINE void FastVector<T, N, Storage>::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 <class T, size_t N, class Storage> |
| void FastVector<T, N, Storage>::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 Value, size_t N> |
| 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<Value, N> &other) const |
| { |
| return (size() == other.size()) && |
| (memcmp(data(), other.data(), size() * sizeof(Value)) == 0); |
| } |
| |
| private: |
| FastVector<Value, N> mData; |
| }; |
| |
| template <class Key, class Value, size_t N> |
| class FlatUnorderedMap final |
| { |
| public: |
| using Pair = std::pair<Key, Value>; |
| |
| FlatUnorderedMap() {} |
| ~FlatUnorderedMap() {} |
| |
| void insert(Key key, Value value) |
| { |
| ASSERT(!contains(key)); |
| mData.push_back(Pair(key, value)); |
| } |
| |
| bool contains(Key key) const |
| { |
| for (size_t index = 0; index < mData.size(); ++index) |
| { |
| if (mData[index].first == key) |
| return true; |
| } |
| return false; |
| } |
| |
| void clear() { mData.clear(); } |
| |
| bool get(Key key, Value *value) const |
| { |
| for (size_t index = 0; index < mData.size(); ++index) |
| { |
| const Pair &item = mData[index]; |
| if (item.first == key) |
| { |
| *value = item.second; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool empty() const { return mData.empty(); } |
| size_t size() const { return mData.size(); } |
| |
| private: |
| FastVector<Pair, N> mData; |
| }; |
| |
| template <class T, size_t N> |
| class FlatUnorderedSet final |
| { |
| public: |
| FlatUnorderedSet() {} |
| ~FlatUnorderedSet() {} |
| |
| bool empty() const { return mData.empty(); } |
| |
| void insert(T value) |
| { |
| ASSERT(!contains(value)); |
| mData.push_back(value); |
| } |
| |
| bool contains(T needle) const |
| { |
| for (T value : mData) |
| { |
| if (value == needle) |
| return true; |
| } |
| return false; |
| } |
| |
| void clear() { mData.clear(); } |
| |
| bool operator==(const FlatUnorderedSet<T, N> &other) const { return mData == other.mData; } |
| |
| private: |
| FastVector<T, N> 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<size_t>(rx::Log2(static_cast<unsigned int>(kWindowSize))); |
| using KeyBitSet = angle::BitSet64<kWindowSize>; |
| |
| 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<size_t>(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<size_t>(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<KeyBitSet> 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 <typename Value> |
| 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<size_t>(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<size_t>(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<Value> mValueData; |
| }; |
| } // namespace angle |
| |
| #endif // COMMON_FASTVECTOR_H_ |