#pragma once #include "Base/Wrapper.hpp" #include #include #include #include #include #include #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 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(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 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& 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&& 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 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)...); 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)...); m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; m_currentSize++; } template 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)...); m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; m_currentSize++; } /** * std version of EmplaceBack(Args&&... args) */ template void emplace_back(Args&&... args) { EmplaceBack(std::forward(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 ToVector() const { std::vector 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& operator=(const StableVector& 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& operator=(StableVector&& 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( ::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)