550 lines
12 KiB
C++
550 lines
12 KiB
C++
#pragma once
|
|
|
|
#include "Base/Wrapper.hpp"
|
|
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4200)
|
|
#pragma warning(disable : 6011)
|
|
#pragma warning(disable : 6386)
|
|
|
|
namespace OpenVulkano
|
|
{
|
|
/**
|
|
* @class Stable Vector
|
|
* @brief Stable Vector is an alternative version for std::vector that provides chunk based memory allocation without re-aligning
|
|
*
|
|
* @throw Please know that this vector creates array gaps when you remove an element.
|
|
*/
|
|
template<typename T, size_t DEFAULT_CHUNK_SIZE = 32, int GROWTH_FACTOR = 2> class StableVector
|
|
{
|
|
struct VectorChunk
|
|
{
|
|
VectorChunk* m_prev = nullptr;
|
|
VectorChunk* m_next = nullptr;
|
|
size_t m_chunkSize;
|
|
int64_t m_lastUsedIndex;
|
|
bool* m_fill;
|
|
T m_data[0];
|
|
|
|
VectorChunk(size_t size) : m_chunkSize(size), m_lastUsedIndex(-1)
|
|
{
|
|
m_fill = reinterpret_cast<bool*>(m_data + size);
|
|
memset(m_fill, 0, size * sizeof(bool));
|
|
}
|
|
|
|
~VectorChunk()
|
|
{
|
|
for (size_t i = 0; i < m_chunkSize; i++)
|
|
{
|
|
if (m_fill[i])
|
|
{
|
|
m_data[i].~T();
|
|
}
|
|
}
|
|
m_prev = nullptr;
|
|
m_next = nullptr;
|
|
}
|
|
};
|
|
|
|
public:
|
|
class Iterator
|
|
{
|
|
public:
|
|
Iterator(VectorChunk* ptr, size_t index = 0) : m_ptr(ptr), m_index(index) {}
|
|
|
|
T& operator*() { return m_ptr->m_data[m_index]; }
|
|
T* operator->() { return &m_ptr->m_data[m_index]; }
|
|
|
|
Iterator operator++()
|
|
{
|
|
++m_index;
|
|
MovetoNextValidChunk();
|
|
return *this;
|
|
}
|
|
|
|
Iterator operator++(int)
|
|
{
|
|
Iterator temp = *this;
|
|
++(*this);
|
|
return temp;
|
|
}
|
|
|
|
Iterator operator--()
|
|
{
|
|
--m_index;
|
|
MovetoNextValidChunk();
|
|
return *this;
|
|
}
|
|
|
|
Iterator operator--(int)
|
|
{
|
|
Iterator temp = *this;
|
|
--(*this);
|
|
return temp;
|
|
}
|
|
|
|
template<typename Init> Iterator operator=(const Init& other)
|
|
{
|
|
m_ptr = other.m_ptr;
|
|
m_index = other.m_index;
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const Iterator& other) const { return m_ptr == other.m_ptr && m_index == other.m_index; }
|
|
bool operator!=(const Iterator& other) const { return !(*this == other); }
|
|
|
|
private:
|
|
void MovetoNextValidChunk()
|
|
{
|
|
while (m_ptr && (m_index > m_ptr->m_chunkSize || !m_ptr->m_fill[m_index]))
|
|
{
|
|
if (m_index >= m_ptr->m_chunkSize)
|
|
{
|
|
m_ptr = m_ptr->m_next;
|
|
m_index = 0;
|
|
}
|
|
else ++m_index;
|
|
}
|
|
|
|
if (m_ptr && m_index >= m_ptr->m_chunkSize)
|
|
{
|
|
m_ptr = m_ptr->m_next;
|
|
m_index = 0;
|
|
MovetoNextValidChunk();
|
|
}
|
|
}
|
|
|
|
private:
|
|
VectorChunk* m_ptr;
|
|
size_t m_index;
|
|
};
|
|
|
|
public:
|
|
StableVector() : m_firstChunk(nullptr), m_lastChunk(nullptr)
|
|
{
|
|
VectorChunk* chunk = SpawnChunk(DEFAULT_CHUNK_SIZE);
|
|
m_firstChunk = chunk;
|
|
m_lastChunk = chunk;
|
|
m_currentSize = 0;
|
|
m_totalCap = DEFAULT_CHUNK_SIZE;
|
|
}
|
|
|
|
StableVector(const StableVector<T>& copy)
|
|
{
|
|
m_firstChunk = nullptr;
|
|
m_lastChunk = nullptr;
|
|
m_currentSize = 0;
|
|
m_totalCap = 0;
|
|
|
|
VectorChunk* currentChunk = copy.m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
for (size_t i = 0; i < currentChunk->m_chunkSize; i++)
|
|
{
|
|
if (currentChunk->m_fill[i]) { PushBack(currentChunk->m_data[i]); }
|
|
}
|
|
|
|
currentChunk = currentChunk->m_next;
|
|
}
|
|
}
|
|
|
|
StableVector(StableVector<T>&& move) noexcept
|
|
{
|
|
m_firstChunk = move.m_firstChunk;
|
|
m_lastChunk = move.m_lastChunk;
|
|
m_currentSize = move.m_currentSize;
|
|
m_totalCap = move.m_totalCap;
|
|
|
|
move.m_firstChunk = nullptr;
|
|
move.m_lastChunk = nullptr;
|
|
move.m_currentSize = 0;
|
|
move.m_totalCap = 0;
|
|
}
|
|
|
|
~StableVector()
|
|
{
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
VectorChunk* temp = currentChunk;
|
|
currentChunk = currentChunk->m_next;
|
|
temp->~VectorChunk();
|
|
::operator delete(temp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the value to the first empty slot in the StableVector
|
|
*
|
|
* @param value - The value to be added
|
|
*/
|
|
void Add(const T& value)
|
|
{
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
for (size_t i = 0; i < currentChunk->m_chunkSize; i++)
|
|
{
|
|
if (!currentChunk->m_fill[i])
|
|
{
|
|
currentChunk->m_data[i] = value;
|
|
currentChunk->m_fill[i] = true;
|
|
m_currentSize++;
|
|
|
|
if (i > currentChunk->m_lastUsedIndex) currentChunk->m_lastUsedIndex = i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
currentChunk = currentChunk->m_next;
|
|
}
|
|
|
|
VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR));
|
|
|
|
new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(value);
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true;
|
|
m_currentSize++;
|
|
}
|
|
|
|
void PushBack(const T& value)
|
|
{
|
|
if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize)
|
|
{
|
|
VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR));
|
|
}
|
|
|
|
new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(value);
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true;
|
|
m_currentSize++;
|
|
}
|
|
|
|
void PushBack(T&& value) noexcept
|
|
{
|
|
if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize)
|
|
{
|
|
VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR));
|
|
}
|
|
|
|
new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::move(value));
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true;
|
|
m_currentSize++;
|
|
}
|
|
|
|
/**
|
|
* std version of PushBack(const T& value)
|
|
*/
|
|
void push_back(const T& value) { PushBack(value); }
|
|
|
|
/**
|
|
* std version of PushBack(T&& value)
|
|
*/
|
|
void push_back(T&& value) { PushBack(std::move(value)); }
|
|
|
|
template<typename... Args> void Emplace(Args&&... args)
|
|
{
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
|
|
while (currentChunk)
|
|
{
|
|
for (size_t i = 0; i < currentChunk->m_chunkSize; i++)
|
|
{
|
|
if (!currentChunk->m_fill[i])
|
|
{
|
|
currentChunk->m_data[i] = T(std::forward<Args>(args)...);
|
|
currentChunk->m_fill[i] = true;
|
|
m_currentSize++;
|
|
|
|
if (i > currentChunk->m_lastUsedIndex) currentChunk->m_lastUsedIndex = i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
currentChunk = currentChunk->m_next;
|
|
}
|
|
|
|
VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR));
|
|
|
|
new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::forward<Args>(args)...);
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true;
|
|
m_currentSize++;
|
|
}
|
|
|
|
template<typename... Args> void EmplaceBack(Args&&... args)
|
|
{
|
|
if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize)
|
|
VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR));
|
|
|
|
new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::forward<Args>(args)...);
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true;
|
|
m_currentSize++;
|
|
}
|
|
|
|
/**
|
|
* std version of EmplaceBack(Args&&... args)
|
|
*/
|
|
template<typename... Args> void emplace_back(Args&&... args) { EmplaceBack(std::forward<Args>(args)...); }
|
|
|
|
/**
|
|
* Pops the last element of the StableVector
|
|
*
|
|
* @throw Please know that this pop function also reduces the chunk's lastUsedIndex
|
|
*/
|
|
void PopBack()
|
|
{
|
|
if (m_currentSize == 0) return;
|
|
|
|
if (m_lastChunk->m_lastUsedIndex == -1)
|
|
{
|
|
VectorChunk* temp = m_lastChunk;
|
|
m_lastChunk = m_lastChunk->m_prev;
|
|
m_lastChunk->m_next = nullptr;
|
|
temp->~VectorChunk();
|
|
::operator delete(temp);
|
|
}
|
|
|
|
m_lastChunk->m_data[m_lastChunk->m_lastUsedIndex].~T();
|
|
m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = false;
|
|
m_lastChunk->m_lastUsedIndex--;
|
|
m_currentSize--;
|
|
}
|
|
|
|
/**
|
|
* std version of PopBack()
|
|
*/
|
|
void pop_back() { PopBack(); }
|
|
|
|
constexpr T& Back() const
|
|
{
|
|
if (m_currentSize == 0)
|
|
{
|
|
throw std::out_of_range("Vector is empty!");
|
|
}
|
|
return m_lastChunk->m_data[m_lastChunk->m_lastUsedIndex];
|
|
}
|
|
|
|
constexpr T& Front() const
|
|
{
|
|
if (m_currentSize == 0)
|
|
{
|
|
throw std::out_of_range("Vector is empty!");
|
|
}
|
|
return m_firstChunk->m_data[0];
|
|
}
|
|
|
|
/**
|
|
* std version of Back()
|
|
*/
|
|
constexpr T& back() const { return Back(); }
|
|
|
|
/**
|
|
* std version of Front()
|
|
*/
|
|
constexpr T& front() const { return Front(); }
|
|
|
|
void Remove(size_t index)
|
|
{
|
|
size_t localIndex = index;
|
|
VectorChunk* chunk = GetChunk(localIndex);
|
|
|
|
if (chunk)
|
|
{
|
|
chunk->m_data[localIndex].~T();
|
|
chunk->m_fill[localIndex] = false;
|
|
m_currentSize--;
|
|
}
|
|
else throw std::out_of_range("Index out of range!");
|
|
}
|
|
|
|
void Remove(const T& value)
|
|
{
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
for (size_t i = 0; i < currentChunk->m_chunkSize; i++)
|
|
{
|
|
if (currentChunk->m_fill[i] && currentChunk->m_data[i] == value)
|
|
{
|
|
currentChunk->m_data[i].~T();
|
|
currentChunk->m_fill[i] = false;
|
|
m_currentSize--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
currentChunk = currentChunk->m_next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* std version of Remove(size_t index)
|
|
*/
|
|
void erase(size_t index) { Remove(index); }
|
|
|
|
/**
|
|
* std version of Remove(const T& value)
|
|
*/
|
|
void erase(const T& value) { Remove(value); }
|
|
|
|
std::vector<T> ToVector() const
|
|
{
|
|
std::vector<T> vec;
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
for (size_t i = 0; i < currentChunk->m_chunkSize; i++)
|
|
{
|
|
if (currentChunk->m_fill[i])
|
|
{
|
|
vec.push_back(currentChunk->m_data[i]);
|
|
}
|
|
}
|
|
|
|
currentChunk = currentChunk->m_next;
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
T& At(size_t index) const
|
|
{
|
|
if (index >= Size()) [[unlikely]]
|
|
throw std::out_of_range("Index out of range!");
|
|
return (*this)[index];
|
|
}
|
|
|
|
bool Empty() const { return m_currentSize == 0; }
|
|
bool empty() const { return Empty(); }
|
|
|
|
T& operator[](size_t index) const
|
|
{
|
|
VectorChunk* chunk = m_firstChunk;
|
|
size_t localIndex = index;
|
|
while (chunk)
|
|
{
|
|
if (localIndex > chunk->m_chunkSize - 1)
|
|
{
|
|
localIndex -= (chunk->m_chunkSize);
|
|
chunk = chunk->m_next;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
return chunk->m_data[localIndex];
|
|
}
|
|
|
|
size_t Size() const { return m_currentSize; }
|
|
size_t size() const { return Size(); }
|
|
|
|
size_t Capacity() const { return m_totalCap; }
|
|
size_t capacity() const { return Capacity(); }
|
|
|
|
void Clear()
|
|
{
|
|
VectorChunk* currentChunk = m_firstChunk;
|
|
while (currentChunk)
|
|
{
|
|
VectorChunk* temp = currentChunk;
|
|
currentChunk = currentChunk->m_next;
|
|
delete temp;
|
|
}
|
|
|
|
m_firstChunk = nullptr;
|
|
m_lastChunk = nullptr;
|
|
m_currentSize = 0;
|
|
m_totalCap = DEFAULT_CHUNK_SIZE;
|
|
|
|
m_firstChunk = SpawnChunk(DEFAULT_CHUNK_SIZE);
|
|
m_lastChunk = m_firstChunk;
|
|
}
|
|
|
|
StableVector<T>& operator=(const StableVector<T>& copy)
|
|
{
|
|
if (this == ©) return *this;
|
|
|
|
Clear();
|
|
|
|
m_firstChunk = nullptr;
|
|
m_lastChunk = nullptr;
|
|
m_currentSize = 0;
|
|
m_totalCap = 0;
|
|
|
|
VectorChunk* currentChunk = copy.m_firstChunk;
|
|
|
|
for (auto it = copy.begin(); it != copy.end(); ++it) PushBack(*it);
|
|
}
|
|
|
|
StableVector<T>& operator=(StableVector<T>&& move) noexcept
|
|
{
|
|
if (this == &move) return *this;
|
|
|
|
Clear();
|
|
|
|
m_firstChunk = move.m_firstChunk;
|
|
m_lastChunk = move.m_lastChunk;
|
|
m_currentSize = move.m_currentSize;
|
|
m_totalCap = move.m_totalCap;
|
|
|
|
move.m_firstChunk = nullptr;
|
|
move.m_lastChunk = nullptr;
|
|
move.m_currentSize = 0;
|
|
move.m_totalCap = 0;
|
|
|
|
return *this;
|
|
}
|
|
|
|
Iterator begin() { return Iterator(m_firstChunk, 0); }
|
|
Iterator end() { return Iterator(m_lastChunk, m_lastChunk->m_lastUsedIndex + 1); }
|
|
|
|
const Iterator& cbegin() const { return Iterator(m_firstChunk, 0); }
|
|
const Iterator& cend() const { return Iterator(m_lastChunk, m_lastChunk->m_lastUsedIndex + 1); }
|
|
|
|
private:
|
|
VectorChunk* SpawnChunk(size_t requestedSize)
|
|
{
|
|
VectorChunk* chunk = static_cast<VectorChunk*>(
|
|
::operator new(sizeof(VectorChunk) + requestedSize * sizeof(T) + requestedSize * sizeof(bool)));
|
|
new (chunk) VectorChunk(requestedSize);
|
|
|
|
if (m_lastChunk)
|
|
{
|
|
chunk->m_prev = m_lastChunk;
|
|
m_lastChunk->m_next = chunk;
|
|
m_lastChunk = chunk;
|
|
m_totalCap += m_lastChunk->m_chunkSize;
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
VectorChunk* GetChunk(size_t& localIndex)
|
|
{
|
|
VectorChunk* chunk = m_firstChunk;
|
|
while (chunk)
|
|
{
|
|
if (localIndex > chunk->m_chunkSize - 1)
|
|
{
|
|
localIndex -= (chunk->m_chunkSize);
|
|
chunk = chunk->m_next;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
private:
|
|
VectorChunk* m_firstChunk;
|
|
VectorChunk* m_lastChunk;
|
|
size_t m_currentSize;
|
|
size_t m_totalCap;
|
|
};
|
|
}
|
|
|
|
#pragma warning(pop) |