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
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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user