blob: b3c205d87ddb91e646b848ce3b31f43b3207d8ad [file] [log] [blame]
//
// 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 <cstring>
#include <initializer_list>
#include <iterator>
namespace angle
{
template <class Iter>
class WrapIter
{
public:
typedef Iter iterator_type;
typedef typename std::iterator_traits<iterator_type>::value_type value_type;
typedef typename std::iterator_traits<iterator_type>::difference_type difference_type;
typedef typename std::iterator_traits<iterator_type>::pointer pointer;
typedef typename std::iterator_traits<iterator_type>::reference reference;
typedef typename std::iterator_traits<iterator_type>::iterator_category iterator_category;
WrapIter() : mIter() {}
WrapIter(const WrapIter &x) = default;
WrapIter &operator=(const WrapIter &x) = default;
WrapIter(const Iter &iter) : mIter(iter) {}
~WrapIter() = default;
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 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 = WrapIter<T *>;
using const_iterator = WrapIter<const T *>;
// This class does not call destructors when resizing down (for performance reasons).
static_assert(std::is_trivially_destructible_v<value_type>);
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 resetWithRawData(size_type count, const uint8_t *data);
void resize(size_type count);
void resize(size_type count, const value_type &value);
// Only for use with non trivially constructible types.
// When increasing size, new elements may have previous values. Use with caution in cases when
// initialization of new elements is not required (will be explicitly initialized later), or
// is never resizing down (not possible to reuse previous values).
void resize_maybe_value_reuse(size_type count);
// Only for use with non trivially constructible types.
// No new elements added, so this function is safe to use. Generates ASSERT() if try resize up.
void resize_down(size_type count);
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<value_type> init);
void ensure_capacity(size_t capacity);
bool uses_fixed_storage() const;
void resize_impl(size_type count);
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_impl(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>::resetWithRawData(size_type count, const uint8_t *data)
{
static_assert(std::is_trivially_copyable<value_type>(),
"This is a special method for trivially copyable types.");
ASSERT(count > 0 && data != nullptr);
resize_impl(count);
std::memcpy(mData, data, count * sizeof(value_type));
}
template <class T, size_t N, class Storage>
ANGLE_INLINE void FastVector<T, N, Storage>::resize(size_type count)
{
// Trivially constructible types will have undefined values when created therefore reusing
// previous values after resize should not be a problem..
static_assert(std::is_trivially_constructible_v<value_type>,
"For non trivially constructible types please use: resize(count, value), "
"resize_maybe_value_reuse(count), or resize_down(count) methods.");
resize_impl(count);
}
template <class T, size_t N, class Storage>
ANGLE_INLINE void FastVector<T, N, Storage>::resize_maybe_value_reuse(size_type count)
{
static_assert(!std::is_trivially_constructible_v<value_type>,
"This is a special method for non trivially constructible types. "
"Please use regular resize(count) method.");
resize_impl(count);
}
template <class T, size_t N, class Storage>
ANGLE_INLINE void FastVector<T, N, Storage>::resize_down(size_type count)
{
static_assert(!std::is_trivially_constructible_v<value_type>,
"This is a special method for non trivially constructible types. "
"Please use regular resize(count) method.");
ASSERT(count <= mSize);
resize_impl(count);
}
template <class T, size_t N, class Storage>
void FastVector<T, N, Storage>::resize_impl(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>::reserve(size_type count)
{
ensure_capacity(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>
ANGLE_INLINE void FastVector<T, N, Storage>::remove_and_permute(iterator pos)
{
ASSERT(pos >= begin());
ASSERT(pos < end());
size_t len = mSize - 1;
*pos = std::move(mData[len]);
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 Storage = FastVector<Value, N>>
class FastMap 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 = typename Storage::iterator;
using const_iterator = typename Storage::const_iterator;
FastMap() {}
~FastMap() {}
Value &operator[](uint32_t key)
{
if (mData.size() <= key)
{
mData.resize(key + 1, {});
}
return at(key);
}
const Value &operator[](uint32_t key) const { return at(key); }
Value &at(uint32_t key)
{
ASSERT(key < mData.size());
return mData[key];
}
const Value &at(uint32_t key) const
{
ASSERT(key < mData.size());
return mData[key];
}
void clear() { mData.clear(); }
void resetWithRawData(size_type count, const uint8_t *data)
{
mData.resetWithRawData(count, data);
}
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);
}
iterator begin() { return mData.begin(); }
const_iterator begin() const { return mData.begin(); }
iterator end() { return mData.end(); }
const_iterator end() const { return mData.end(); }
private:
FastVector<Value, N> mData;
};
template <class Key, class Value, size_t N>
class FlatUnorderedMap final
{
public:
using Pair = std::pair<Key, Value>;
using Storage = FastVector<Pair, N>;
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<Pair, N> mData;
};
template <class T, size_t N>
class FlatUnorderedSet final
{
public:
using Storage = FastVector<T, N>;
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<T, N> &other) const { return mData == other.mData; }
private:
Storage mData;
};
} // namespace angle
#endif // COMMON_FASTVECTOR_H_