References. Removed testing, will rewrite the tests tomorrow
Bigfoot / Build & Test Debug with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 6m14s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 5m44s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 6m8s
Bigfoot / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 6m4s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 6m23s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 6m26s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 9m35s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 7m21s
Bigfoot / Build & Test Release with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 6m14s
Bigfoot / Build & Test Release with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 6m12s
Bigfoot / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 7m11s
Bigfoot / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 6m59s
Bigfoot / Clang Format Checks (push) Successful in 1m42s

This commit is contained in:
2026-07-31 18:27:05 +02:00
parent 33944d0b7d
commit b2220d981f
32 changed files with 1525 additions and 1350 deletions
@@ -8,21 +8,24 @@
#define BIGFOOT_UTILS_CONTAINERS_SLOTMAP_HPP
#include <Utils/UtilsAssertHandler.hpp>
#include <EASTL/array.h>
#include <EASTL/bitset.h>
#include <EASTL/iterator.h>
#include <EASTL/optional.h>
#include <EASTL/unique_ptr.h>
#include <EASTL/vector.h>
#include <cstddef>
#include <cstdint>
#include <limits>
namespace Bigfoot
{
template<class VERSION_TYPE = std::uint32_t,
class INDEX_TYPE = std::uint32_t,
std::enable_if_t<std::is_unsigned_v<VERSION_TYPE> && std::is_unsigned_v<INDEX_TYPE>, bool> = true>
class SlotMapKey
{
public:
using IndexType = INDEX_TYPE;
using VersionType = VERSION_TYPE;
using IndexType = std::uint32_t;
using VersionType = std::uint32_t;
private:
static constexpr std::uint32_t VERSION_BIT_COUNT = sizeof(VersionType) * std::numeric_limits<unsigned char>::digits;
@@ -78,234 +81,811 @@ class SlotMapKey
std::uint64_t m_key;
};
template<class TYPE, class VERSION_TYPE = std::uint32_t, class INDEX_TYPE = std::uint32_t>
class SlotMap
class SlotMapBase
{
public:
using InternalSlotKey = SlotMapKey<VERSION_TYPE, INDEX_TYPE>;
SlotMap():
m_freeSlotHead(std::numeric_limits<typename InternalSlotKey::IndexType>::max())
{
}
SlotMap(const SlotMap& p_map) = default;
SlotMap(SlotMap&& p_map) = default;
~SlotMap() = default;
template<class... ARGS>
[[nodiscard]]
InternalSlotKey Insert(ARGS&&... p_args)
{
ASSERT(UtilsAssertHandler, m_slots.size() < InternalSlotKey::MAX_INDEX, "All slots have been exhausted!");
const typename InternalSlotKey::IndexType dataIndex = static_cast<InternalSlotKey::IndexType>(m_data.size());
m_data.emplace_back(std::forward<ARGS>(p_args)...);
if (HasFreeSlots())
{
const typename InternalSlotKey::IndexType slotIndex = m_freeSlotHead;
m_freeSlotHead = m_slots[slotIndex].GetIndex();
m_slots[slotIndex] = InternalSlotKey {m_slots[slotIndex].GetVersion(), dataIndex};
m_dataToSlots.push_back(slotIndex);
return InternalSlotKey {m_slots[slotIndex].GetVersion(), slotIndex};
}
const typename InternalSlotKey::IndexType slotIndex = static_cast<InternalSlotKey::IndexType>(m_slots.size());
m_slots.emplace_back(1, dataIndex);
m_dataToSlots.push_back(slotIndex);
return InternalSlotKey {1, slotIndex};
}
void Remove(const InternalSlotKey p_slotKey)
{
if (!Has(p_slotKey))
{
return;
}
const typename InternalSlotKey::IndexType dataIndex = m_slots[p_slotKey.GetIndex()].GetIndex();
m_data.erase_unsorted(m_data.begin() + dataIndex);
m_dataToSlots.erase_unsorted(m_dataToSlots.begin() + dataIndex);
if (dataIndex < m_data.size())
{
m_slots[m_dataToSlots[dataIndex]] = InternalSlotKey {m_slots[m_dataToSlots[dataIndex]].GetVersion(),
dataIndex};
}
RecycleSlot(p_slotKey.GetVersion() + 1, p_slotKey.GetIndex());
}
~SlotMapBase() = default;
[[nodiscard]]
bool Has(const InternalSlotKey p_slotKey) const
bool Has(const SlotMapKey p_slotKey) const
{
return p_slotKey.Valid() && (p_slotKey.GetIndex() < m_slots.size() &&
p_slotKey.GetVersion() == m_slots[p_slotKey.GetIndex()].GetVersion());
}
[[nodiscard]]
TYPE* Get(const InternalSlotKey p_slotKey)
std::size_t GetSize() const
{
return Has(p_slotKey) ? &m_data[m_slots[p_slotKey.GetIndex()].GetIndex()] : nullptr;
}
[[nodiscard]]
const TYPE* Get(const InternalSlotKey p_slotKey) const
{
return Has(p_slotKey) ? &m_data[m_slots[p_slotKey.GetIndex()].GetIndex()] : nullptr;
}
void Reset()
{
m_data.clear();
m_slots.clear();
m_dataToSlots.clear();
m_freeSlotHead = std::numeric_limits<typename InternalSlotKey::IndexType>::max();
}
void Clear()
{
for (const typename eastl::vector<TYPE>::size_type slotIndex: m_dataToSlots)
{
RecycleSlot(m_slots[slotIndex].GetVersion() + 1, static_cast<InternalSlotKey::IndexType>(slotIndex));
}
m_data.clear();
m_dataToSlots.clear();
}
[[nodiscard]]
eastl::vector<TYPE>::size_type GetSize() const
{
return m_data.size();
}
[[nodiscard]]
eastl::vector<TYPE>::size_type GetCapacity() const
{
return m_data.capacity();
return m_size;
}
[[nodiscard]]
bool Empty() const
{
return m_data.empty();
return m_size == 0;
}
void Reserve(const eastl::vector<TYPE>::size_type p_size)
SlotMapBase& operator=(const SlotMapBase& p_base) = default;
SlotMapBase& operator=(SlotMapBase&& p_base) = default;
protected:
SlotMapBase() = default;
SlotMapBase(const SlotMapBase& p_base) = default;
SlotMapBase(SlotMapBase&& p_base) = default;
[[nodiscard]]
bool HasFreeSlots() const
{
return m_freeSlotHead != std::numeric_limits<SlotMapKey::IndexType>::max();
}
void RecycleSlot(const SlotMapKey::VersionType p_version, const SlotMapKey::IndexType p_slotIndex)
{
if (p_version == SlotMapKey::INVALID_VERSION)
{
m_slots[p_slotIndex] = SlotMapKey {SlotMapKey::INVALID_VERSION, 0};
return;
}
m_slots[p_slotIndex] = SlotMapKey {p_version, m_freeSlotHead};
m_freeSlotHead = p_slotIndex;
}
void ResetSlots()
{
m_size = 0;
m_slots.clear();
m_freeSlotHead = std::numeric_limits<SlotMapKey::IndexType>::max();
}
void ReserveSlots(const std::size_t p_size)
{
m_data.reserve(p_size);
m_dataToSlots.reserve(p_size);
m_slots.reserve(p_size);
}
std::size_t m_size = 0;
eastl::vector<SlotMapKey> m_slots;
SlotMapKey::IndexType m_freeSlotHead = std::numeric_limits<SlotMapKey::IndexType>::max();
};
template<class TYPE>
class DenseSlotMap: public SlotMapBase
{
public:
using iterator = typename eastl::vector<TYPE>::iterator;
using const_iterator = typename eastl::vector<TYPE>::const_iterator;
using reverse_iterator = typename eastl::vector<TYPE>::reverse_iterator;
using const_reverse_iterator = typename eastl::vector<TYPE>::const_reverse_iterator;
DenseSlotMap() = default;
DenseSlotMap(const DenseSlotMap& p_map) = default;
DenseSlotMap(DenseSlotMap&& p_map) = default;
~DenseSlotMap() = default;
template<class... ARGS>
[[nodiscard]]
eastl::vector<TYPE>::iterator begin()
SlotMapKey Insert(ARGS&&... p_args)
{
const SlotMapKey::IndexType dataIndex = static_cast<SlotMapKey::IndexType>(m_size);
m_data.emplace_back(std::forward<ARGS>(p_args)...);
++m_size;
return AcquireSlot(dataIndex);
}
void Remove(const SlotMapKey p_slotKey)
{
if (!Has(p_slotKey))
{
return;
}
const SlotMapKey::IndexType dataIndex = GetDataIndex(p_slotKey);
if (dataIndex != m_size - 1)
{
m_data[dataIndex] = eastl::move(m_data.back());
}
m_data.pop_back();
--m_size;
ReleaseSlot(p_slotKey, dataIndex);
}
[[nodiscard]]
TYPE* Get(const SlotMapKey p_slotKey)
{
return Has(p_slotKey) ? &m_data[GetDataIndex(p_slotKey)] : nullptr;
}
[[nodiscard]]
const TYPE* Get(const SlotMapKey p_slotKey) const
{
return Has(p_slotKey) ? &m_data[GetDataIndex(p_slotKey)] : nullptr;
}
void Reset()
{
m_data.clear();
m_dataToSlots.clear();
ResetSlots();
}
void Clear()
{
for (const std::size_t slotIndex: m_dataToSlots)
{
RecycleSlot(m_slots[slotIndex].GetVersion() + 1, static_cast<SlotMapKey::IndexType>(slotIndex));
}
m_size = 0;
m_data.clear();
m_dataToSlots.clear();
}
[[nodiscard]]
std::size_t GetCapacity() const
{
return m_data.capacity();
}
void Reserve(const std::size_t p_size)
{
m_data.reserve(p_size);
m_dataToSlots.reserve(p_size);
ReserveSlots(p_size);
}
[[nodiscard]]
iterator begin()
{
return m_data.begin();
}
[[nodiscard]]
eastl::vector<TYPE>::iterator end()
iterator end()
{
return m_data.end();
}
[[nodiscard]]
eastl::vector<TYPE>::const_iterator begin() const
const_iterator begin() const
{
return m_data.begin();
}
[[nodiscard]]
eastl::vector<TYPE>::const_iterator end() const
const_iterator end() const
{
return m_data.end();
}
[[nodiscard]]
eastl::vector<TYPE>::reverse_iterator rbegin()
reverse_iterator rbegin()
{
return m_data.rbegin();
}
[[nodiscard]]
eastl::vector<TYPE>::reverse_iterator rend()
reverse_iterator rend()
{
return m_data.rend();
}
[[nodiscard]]
eastl::vector<TYPE>::const_reverse_iterator rbegin() const
const_reverse_iterator rbegin() const
{
return m_data.rbegin();
}
[[nodiscard]]
eastl::vector<TYPE>::const_reverse_iterator rend() const
const_reverse_iterator rend() const
{
return m_data.rend();
}
[[nodiscard]]
eastl::vector<TYPE>::const_iterator cbegin() const
const_iterator cbegin() const
{
return m_data.cbegin();
}
[[nodiscard]]
eastl::vector<TYPE>::const_iterator cend() const
const_iterator cend() const
{
return m_data.cend();
}
[[nodiscard]]
eastl::vector<TYPE>::const_reverse_iterator crbegin() const
const_reverse_iterator crbegin() const
{
return m_data.crbegin();
}
[[nodiscard]]
eastl::vector<TYPE>::const_reverse_iterator crend() const
const_reverse_iterator crend() const
{
return m_data.crend();
}
SlotMap& operator=(const SlotMap& p_slotMap) = default;
SlotMap& operator=(SlotMap&& p_slotMap) = default;
DenseSlotMap& operator=(const DenseSlotMap& p_map) = default;
DenseSlotMap& operator=(DenseSlotMap&& p_map) = default;
private:
[[nodiscard]]
bool HasFreeSlots() const
SlotMapKey::IndexType GetDataIndex(const SlotMapKey p_slotKey) const
{
return m_freeSlotHead != std::numeric_limits<typename InternalSlotKey::IndexType>::max();
return m_slots[p_slotKey.GetIndex()].GetIndex();
}
void RecycleSlot(const InternalSlotKey::VersionType p_version, const InternalSlotKey::IndexType p_slotIndex)
[[nodiscard]]
SlotMapKey AcquireSlot(const SlotMapKey::IndexType p_dataIndex)
{
if (p_version == InternalSlotKey::INVALID_VERSION)
ASSERT(UtilsAssertHandler, m_slots.size() < SlotMapKey::MAX_INDEX, "All slots have been exhausted!");
if (HasFreeSlots())
{
m_slots[p_slotIndex] = InternalSlotKey {InternalSlotKey::INVALID_VERSION, 0};
return;
const SlotMapKey::IndexType slotIndex = m_freeSlotHead;
m_freeSlotHead = m_slots[slotIndex].GetIndex();
m_slots[slotIndex] = SlotMapKey {m_slots[slotIndex].GetVersion(), p_dataIndex};
m_dataToSlots.push_back(slotIndex);
return SlotMapKey {m_slots[slotIndex].GetVersion(), slotIndex};
}
m_slots[p_slotIndex] = InternalSlotKey {p_version, m_freeSlotHead};
m_freeSlotHead = p_slotIndex;
const SlotMapKey::IndexType slotIndex = static_cast<SlotMapKey::IndexType>(m_slots.size());
m_slots.emplace_back(1, p_dataIndex);
m_dataToSlots.push_back(slotIndex);
return SlotMapKey {1, slotIndex};
}
void ReleaseSlot(const SlotMapKey p_slotKey, const SlotMapKey::IndexType p_dataIndex)
{
m_dataToSlots.erase_unsorted(m_dataToSlots.begin() + p_dataIndex);
if (p_dataIndex < m_size)
{
m_slots[m_dataToSlots[p_dataIndex]] = SlotMapKey {m_slots[m_dataToSlots[p_dataIndex]].GetVersion(),
p_dataIndex};
}
RecycleSlot(p_slotKey.GetVersion() + 1, p_slotKey.GetIndex());
}
eastl::vector<TYPE> m_data;
eastl::vector<InternalSlotKey> m_slots;
eastl::vector<std::size_t> m_dataToSlots;
};
eastl::vector<typename eastl::vector<TYPE>::size_type> m_dataToSlots;
template<class TYPE>
class StableSlotMap: public SlotMapBase
{
private:
static constexpr std::size_t PAGE_SIZE = 64;
InternalSlotKey::IndexType m_freeSlotHead;
struct Page
{
eastl::array<eastl::optional<TYPE>, PAGE_SIZE> m_slots;
eastl::bitset<PAGE_SIZE> m_occupied;
};
public:
class iterator
{
public:
using iterator_category = eastl::bidirectional_iterator_tag;
using value_type = TYPE;
using difference_type = std::ptrdiff_t;
using pointer = TYPE*;
using reference = TYPE&;
iterator() = default;
[[nodiscard]]
reference operator*() const
{
return *m_current;
}
[[nodiscard]]
pointer operator->() const
{
return m_current;
}
iterator& operator++()
{
Advance();
return *this;
}
iterator operator++(int)
{
const iterator temp = *this;
++(*this);
return temp;
}
iterator& operator--()
{
Retreat();
return *this;
}
iterator operator--(int)
{
const iterator temp = *this;
--(*this);
return temp;
}
[[nodiscard]]
bool operator==(const iterator& p_other) const
{
return m_map == p_other.m_map && m_current == p_other.m_current;
}
private:
friend class StableSlotMap;
explicit iterator(StableSlotMap* p_map):
m_map(p_map)
{
}
void SeekFirstFrom(std::size_t p_pageIndex)
{
for (; p_pageIndex < m_map->m_pages.size(); ++p_pageIndex)
{
const std::size_t offset = m_map->m_pages[p_pageIndex]->m_occupied.find_first();
if (offset != PAGE_SIZE)
{
m_pageIndex = p_pageIndex;
m_offset = offset;
m_current = &(*m_map->m_pages[p_pageIndex]).m_slots[offset].value();
return;
}
}
m_pageIndex = m_map->m_pages.size();
m_offset = 0;
m_current = nullptr;
}
void SeekLastFrom(std::size_t p_pageIndex)
{
for (;;)
{
const std::size_t offset = m_map->m_pages[p_pageIndex]->m_occupied.find_last();
if (offset != PAGE_SIZE)
{
m_pageIndex = p_pageIndex;
m_offset = offset;
m_current = &(*m_map->m_pages[p_pageIndex]).m_slots[offset].value();
return;
}
if (p_pageIndex == 0)
{
return;
}
--p_pageIndex;
}
}
void Advance()
{
const std::size_t offset = m_map->m_pages[m_pageIndex]->m_occupied.find_next(m_offset);
if (offset != PAGE_SIZE)
{
m_offset = offset;
m_current = &(*m_map->m_pages[m_pageIndex]).m_slots[offset].value();
return;
}
SeekFirstFrom(m_pageIndex + 1);
}
void Retreat()
{
if (m_current == nullptr)
{
SeekLastFrom(m_map->m_pages.size() - 1);
return;
}
const std::size_t offset = m_map->m_pages[m_pageIndex]->m_occupied.find_prev(m_offset);
if (offset != PAGE_SIZE)
{
m_offset = offset;
m_current = &(*m_map->m_pages[m_pageIndex]).m_slots[offset].value();
return;
}
if (m_pageIndex == 0)
{
return;
}
SeekLastFrom(m_pageIndex - 1);
}
StableSlotMap* m_map = nullptr;
std::size_t m_pageIndex = 0;
std::size_t m_offset = 0;
TYPE* m_current = nullptr;
};
class const_iterator
{
public:
using iterator_category = eastl::bidirectional_iterator_tag;
using value_type = TYPE;
using difference_type = std::ptrdiff_t;
using pointer = const TYPE*;
using reference = const TYPE&;
const_iterator() = default;
[[nodiscard]]
reference operator*() const
{
return *m_current;
}
[[nodiscard]]
pointer operator->() const
{
return m_current;
}
const_iterator& operator++()
{
Advance();
return *this;
}
const_iterator operator++(int)
{
const const_iterator temp = *this;
++(*this);
return temp;
}
const_iterator& operator--()
{
Retreat();
return *this;
}
const_iterator operator--(int)
{
const const_iterator temp = *this;
--(*this);
return temp;
}
[[nodiscard]]
bool operator==(const const_iterator& p_other) const
{
return m_map == p_other.m_map && m_current == p_other.m_current;
}
private:
friend class StableSlotMap;
explicit const_iterator(const StableSlotMap* p_map):
m_map(p_map)
{
}
void SeekFirstFrom(std::size_t p_pageIndex)
{
for (; p_pageIndex < m_map->m_pages.size(); ++p_pageIndex)
{
const std::size_t offset = m_map->m_pages[p_pageIndex]->m_occupied.find_first();
if (offset != PAGE_SIZE)
{
m_pageIndex = p_pageIndex;
m_offset = offset;
m_current = &(*m_map->m_pages[p_pageIndex]).m_slots[offset].value();
return;
}
}
m_pageIndex = m_map->m_pages.size();
m_offset = 0;
m_current = nullptr;
}
void SeekLastFrom(std::size_t p_pageIndex)
{
for (;;)
{
const std::size_t offset = m_map->m_pages[p_pageIndex]->m_occupied.find_last();
if (offset != PAGE_SIZE)
{
m_pageIndex = p_pageIndex;
m_offset = offset;
m_current = &(*m_map->m_pages[p_pageIndex]).m_slots[offset].value();
return;
}
if (p_pageIndex == 0)
{
return;
}
--p_pageIndex;
}
}
void Advance()
{
const std::size_t offset = m_map->m_pages[m_pageIndex]->m_occupied.find_next(m_offset);
if (offset != PAGE_SIZE)
{
m_offset = offset;
m_current = &(*m_map->m_pages[m_pageIndex]).m_slots[offset].value();
return;
}
SeekFirstFrom(m_pageIndex + 1);
}
void Retreat()
{
if (m_current == nullptr)
{
SeekLastFrom(m_map->m_pages.size() - 1);
return;
}
const std::size_t offset = m_map->m_pages[m_pageIndex]->m_occupied.find_prev(m_offset);
if (offset != PAGE_SIZE)
{
m_offset = offset;
m_current = &(*m_map->m_pages[m_pageIndex]).m_slots[offset].value();
return;
}
if (m_pageIndex == 0)
{
return;
}
SeekLastFrom(m_pageIndex - 1);
}
const StableSlotMap* m_map = nullptr;
std::size_t m_pageIndex = 0;
std::size_t m_offset = 0;
const TYPE* m_current = nullptr;
};
using reverse_iterator = eastl::reverse_iterator<iterator>;
using const_reverse_iterator = eastl::reverse_iterator<const_iterator>;
StableSlotMap() = default;
StableSlotMap(const StableSlotMap& p_map) = delete;
StableSlotMap(StableSlotMap&& p_map) = default;
~StableSlotMap() = default;
template<class... ARGS>
[[nodiscard]]
SlotMapKey Insert(ARGS&&... p_args)
{
const SlotMapKey key = AcquireFreeSlot();
const SlotMapKey::IndexType index = key.GetIndex();
const std::size_t pageIndex = index / PAGE_SIZE;
if (pageIndex >= m_pages.size())
{
m_pages.push_back(eastl::make_unique<Page>());
}
Page& page = *m_pages[pageIndex];
const std::size_t offset = index % PAGE_SIZE;
page.m_slots[offset].emplace(std::forward<ARGS>(p_args)...);
page.m_occupied.set(offset);
++m_size;
return key;
}
void Remove(const SlotMapKey p_slotKey)
{
if (!Has(p_slotKey))
{
return;
}
const SlotMapKey::IndexType index = p_slotKey.GetIndex();
Page& page = *m_pages[index / PAGE_SIZE];
const std::size_t offset = index % PAGE_SIZE;
page.m_slots[offset].reset();
page.m_occupied.reset(offset);
--m_size;
ReleaseSlotVersion(index);
}
[[nodiscard]]
TYPE* Get(const SlotMapKey p_slotKey)
{
return Has(p_slotKey) ? &DataAt(p_slotKey.GetIndex()) : nullptr;
}
[[nodiscard]]
const TYPE* Get(const SlotMapKey p_slotKey) const
{
return Has(p_slotKey) ? &DataAt(p_slotKey.GetIndex()) : nullptr;
}
void Reset()
{
for (const eastl::unique_ptr<Page>& page: m_pages)
{
*page = Page {};
}
ResetSlots();
}
void Clear()
{
for (std::size_t pageIndex = 0; pageIndex < m_pages.size(); ++pageIndex)
{
Page& page = *m_pages[pageIndex];
for (std::size_t offset = page.m_occupied.find_first(); offset != PAGE_SIZE;
offset = page.m_occupied.find_next(offset))
{
page.m_slots[offset].reset();
ReleaseSlotVersion(static_cast<SlotMapKey::IndexType>(pageIndex * PAGE_SIZE + offset));
}
page.m_occupied.reset();
}
m_size = 0;
}
[[nodiscard]]
std::size_t GetCapacity() const
{
return m_pages.size() * PAGE_SIZE;
}
void Reserve(const std::size_t p_size)
{
const std::size_t pagesNeeded = (p_size + PAGE_SIZE - 1) / PAGE_SIZE;
m_pages.reserve(pagesNeeded);
for (std::size_t pageIndex = m_pages.size(); pageIndex < pagesNeeded; ++pageIndex)
{
m_pages.push_back(eastl::make_unique<Page>());
}
ReserveSlots(p_size);
}
[[nodiscard]]
iterator begin()
{
iterator it {this};
it.SeekFirstFrom(0);
return it;
}
[[nodiscard]]
iterator end()
{
return iterator {this};
}
[[nodiscard]]
const_iterator begin() const
{
const_iterator it {this};
it.SeekFirstFrom(0);
return it;
}
[[nodiscard]]
const_iterator end() const
{
return const_iterator {this};
}
[[nodiscard]]
reverse_iterator rbegin()
{
return reverse_iterator {end()};
}
[[nodiscard]]
reverse_iterator rend()
{
return reverse_iterator {begin()};
}
[[nodiscard]]
const_reverse_iterator rbegin() const
{
return const_reverse_iterator {end()};
}
[[nodiscard]]
const_reverse_iterator rend() const
{
return const_reverse_iterator {begin()};
}
[[nodiscard]]
const_iterator cbegin() const
{
return begin();
}
[[nodiscard]]
const_iterator cend() const
{
return end();
}
[[nodiscard]]
const_reverse_iterator crbegin() const
{
return rbegin();
}
[[nodiscard]]
const_reverse_iterator crend() const
{
return rend();
}
StableSlotMap& operator=(const StableSlotMap& p_map) = delete;
StableSlotMap& operator=(StableSlotMap&& p_map) = default;
private:
[[nodiscard]]
TYPE& DataAt(const std::size_t p_index)
{
return (*m_pages[p_index / PAGE_SIZE]).m_slots[p_index % PAGE_SIZE].value();
}
[[nodiscard]]
const TYPE& DataAt(const std::size_t p_index) const
{
return (*m_pages[p_index / PAGE_SIZE]).m_slots[p_index % PAGE_SIZE].value();
}
[[nodiscard]]
SlotMapKey AcquireFreeSlot()
{
ASSERT(UtilsAssertHandler, m_slots.size() < SlotMapKey::MAX_INDEX, "All slots have been exhausted!");
if (HasFreeSlots())
{
const SlotMapKey::IndexType slotIndex = m_freeSlotHead;
m_freeSlotHead = m_slots[slotIndex].GetIndex();
m_slots[slotIndex] = SlotMapKey {m_slots[slotIndex].GetVersion(), slotIndex};
return m_slots[slotIndex];
}
const SlotMapKey::IndexType slotIndex = static_cast<SlotMapKey::IndexType>(m_slots.size());
m_slots.emplace_back(1, slotIndex);
return m_slots[slotIndex];
}
void ReleaseSlotVersion(const SlotMapKey::IndexType p_index)
{
RecycleSlot(m_slots[p_index].GetVersion() + 1, p_index);
}
eastl::vector<eastl::unique_ptr<Page>> m_pages;
};
} // namespace Bigfoot
@@ -12,6 +12,13 @@ static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
FLATBUFFERS_VERSION_MINOR == 12 &&
FLATBUFFERS_VERSION_REVISION == 19,
"Non-compatible flatbuffers version included");
// Ensure the included flatbuffers.h is Bigfoot's fork - stock
// flatbuffers (or a differently versioned Bigfoot fork) is not compatible.
#ifndef FLATBUFFERS_BIGFOOT_VERSION
#error "This file requires Bigfoot's flatbuffers fork - stock flatbuffers is not compatible"
#endif
static_assert(FLATBUFFERS_BIGFOOT_VERSION == 1,
"Non-compatible Bigfoot flatbuffers fork version included");
#include "EASTL/unique_ptr.h"
#include "EASTL/string.h"