diff --git a/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp b/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp index ee78bbf..ae8bc14 100644 --- a/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp +++ b/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp @@ -40,7 +40,7 @@ static_assert(std::is_trivially_copyable_v, class SlotMapAdaptor { public: - using Key = SlotMap::SlotKey; + using Key = SlotMap::InternalSlotKey; Key Add(MyComplexStruct&& p_value) { diff --git a/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp b/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp index 3361112..ff9d60e 100644 --- a/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp +++ b/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp @@ -15,77 +15,77 @@ namespace Bigfoot { - -template && std::is_unsigned_v, bool> = true> +class SlotMapKey +{ + public: + using IndexType = INDEX_TYPE; + using VersionType = VERSION_TYPE; + + private: + static constexpr std::uint32_t VERSION_BIT_COUNT = sizeof(VersionType) * std::numeric_limits::digits; + static constexpr std::uint32_t INDEX_BIT_COUNT = sizeof(IndexType) * std::numeric_limits::digits; + static_assert(VERSION_BIT_COUNT + INDEX_BIT_COUNT <= 64, + "We cant construct a 64 bit key from the given Version and Index types!"); + + static constexpr std::uint64_t INDEX_MASK = (static_cast(1) << INDEX_BIT_COUNT) - 1; + + public: + static constexpr VersionType INVALID_VERSION = 0; + + static constexpr IndexType MAX_INDEX = std::numeric_limits::max(); + + constexpr SlotMapKey(const VersionType p_version, const IndexType p_index): + m_key((static_cast(p_version) << INDEX_BIT_COUNT) | + (static_cast(p_index) & INDEX_MASK)) + { + } + + constexpr SlotMapKey(): + SlotMapKey(INVALID_VERSION, 0) + { + } + + constexpr SlotMapKey(const SlotMapKey& p_slotKey) = default; + constexpr SlotMapKey(SlotMapKey&& p_slotKey) = default; + + constexpr ~SlotMapKey() = default; + + constexpr bool Valid() const + { + return GetVersion() != INVALID_VERSION; + } + + constexpr VersionType GetVersion() const + { + return static_cast(m_key >> INDEX_BIT_COUNT); + } + + constexpr IndexType GetIndex() const + { + return static_cast(m_key & INDEX_MASK); + } + + constexpr SlotMapKey& operator=(const SlotMapKey& p_slotKey) = default; + constexpr SlotMapKey& operator=(SlotMapKey&& p_slotKey) = default; + + [[nodiscard]] + constexpr bool operator==(const SlotMapKey& p_other) const = default; + + private: + std::uint64_t m_key; +}; + +template class SlotMap { public: - class SlotKey - { - public: - using IndexType = INDEX_TYPE; - using VersionType = VERSION_TYPE; - - private: - static constexpr std::uint32_t VERSION_BIT_COUNT = sizeof(VersionType) * - std::numeric_limits::digits; - static constexpr std::uint32_t INDEX_BIT_COUNT = sizeof(IndexType) * std::numeric_limits::digits; - static_assert(VERSION_BIT_COUNT + INDEX_BIT_COUNT <= 64, - "We cant construct a 64 bit key from the given Version and Index types!"); - - static constexpr std::uint64_t INDEX_MASK = (static_cast(1) << INDEX_BIT_COUNT) - 1; - - public: - static constexpr VersionType INVALID_VERSION = 0; - - static constexpr IndexType MAX_INDEX = std::numeric_limits::max(); - - constexpr SlotKey(const VersionType p_version, const IndexType p_index): - m_key((static_cast(p_version) << INDEX_BIT_COUNT) | - (static_cast(p_index) & INDEX_MASK)) - { - } - - constexpr SlotKey(): - SlotKey(INVALID_VERSION, 0) - { - } - - constexpr SlotKey(const SlotKey& p_slotKey) = default; - constexpr SlotKey(SlotKey&& p_slotKey) = default; - - constexpr ~SlotKey() = default; - - constexpr bool Valid() const - { - return GetVersion() != INVALID_VERSION; - } - - constexpr VersionType GetVersion() const - { - return static_cast(m_key >> INDEX_BIT_COUNT); - } - - constexpr IndexType GetIndex() const - { - return static_cast(m_key & INDEX_MASK); - } - - constexpr SlotKey& operator=(const SlotKey& p_slotKey) = default; - constexpr SlotKey& operator=(SlotKey&& p_slotKey) = default; - - [[nodiscard]] - constexpr bool operator==(const SlotKey& p_other) const = default; - - private: - std::uint64_t m_key; - }; + using InternalSlotKey = SlotMapKey; SlotMap(): - m_freeSlotHead(std::numeric_limits::max()) + m_freeSlotHead(std::numeric_limits::max()) { } @@ -96,67 +96,68 @@ class SlotMap template [[nodiscard]] - SlotKey Insert(ARGS&&... p_args) + InternalSlotKey Insert(ARGS&&... p_args) { - ASSERT(UtilsAssertHandler, m_slots.size() < SlotKey::MAX_INDEX, "All slots have been exhausted!"); + ASSERT(UtilsAssertHandler, m_slots.size() < InternalSlotKey::MAX_INDEX, "All slots have been exhausted!"); - const typename SlotKey::IndexType dataIndex = static_cast(m_data.size()); + const typename InternalSlotKey::IndexType dataIndex = static_cast(m_data.size()); m_data.emplace_back(std::forward(p_args)...); if (HasFreeSlots()) { - const typename SlotKey::IndexType slotIndex = m_freeSlotHead; + const typename InternalSlotKey::IndexType slotIndex = m_freeSlotHead; m_freeSlotHead = m_slots[slotIndex].GetIndex(); - m_slots[slotIndex] = SlotKey {m_slots[slotIndex].GetVersion(), dataIndex}; + m_slots[slotIndex] = InternalSlotKey {m_slots[slotIndex].GetVersion(), dataIndex}; m_dataToSlots.push_back(slotIndex); - return SlotKey {m_slots[slotIndex].GetVersion(), slotIndex}; + return InternalSlotKey {m_slots[slotIndex].GetVersion(), slotIndex}; } - const typename SlotKey::IndexType slotIndex = static_cast(m_slots.size()); + const typename InternalSlotKey::IndexType slotIndex = static_cast(m_slots.size()); m_slots.emplace_back(1, dataIndex); m_dataToSlots.push_back(slotIndex); - return SlotKey {1, slotIndex}; + return InternalSlotKey {1, slotIndex}; } - void Remove(const SlotKey p_slotKey) + void Remove(const InternalSlotKey p_slotKey) { if (!Has(p_slotKey)) { return; } - const typename SlotKey::IndexType dataIndex = m_slots[p_slotKey.GetIndex()].GetIndex(); + 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]] = SlotKey {m_slots[m_dataToSlots[dataIndex]].GetVersion(), dataIndex}; + m_slots[m_dataToSlots[dataIndex]] = InternalSlotKey {m_slots[m_dataToSlots[dataIndex]].GetVersion(), + dataIndex}; } RecycleSlot(p_slotKey.GetVersion() + 1, p_slotKey.GetIndex()); } [[nodiscard]] - bool Has(const SlotKey p_slotKey) const + bool Has(const InternalSlotKey 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 SlotKey p_slotKey) + TYPE* Get(const InternalSlotKey p_slotKey) { return Has(p_slotKey) ? &m_data[m_slots[p_slotKey.GetIndex()].GetIndex()] : nullptr; } [[nodiscard]] - const TYPE* Get(const SlotKey p_slotKey) const + const TYPE* Get(const InternalSlotKey p_slotKey) const { return Has(p_slotKey) ? &m_data[m_slots[p_slotKey.GetIndex()].GetIndex()] : nullptr; } @@ -166,14 +167,14 @@ class SlotMap m_data.clear(); m_slots.clear(); m_dataToSlots.clear(); - m_freeSlotHead = std::numeric_limits::max(); + m_freeSlotHead = std::numeric_limits::max(); } void Clear() { for (const typename eastl::vector::size_type slotIndex: m_dataToSlots) { - RecycleSlot(m_slots[slotIndex].GetVersion() + 1, static_cast(slotIndex)); + RecycleSlot(m_slots[slotIndex].GetVersion() + 1, static_cast(slotIndex)); } m_data.clear(); @@ -284,27 +285,27 @@ class SlotMap [[nodiscard]] bool HasFreeSlots() const { - return m_freeSlotHead != std::numeric_limits::max(); + return m_freeSlotHead != std::numeric_limits::max(); } - void RecycleSlot(const SlotKey::VersionType p_version, const SlotKey::IndexType p_slotIndex) + void RecycleSlot(const InternalSlotKey::VersionType p_version, const InternalSlotKey::IndexType p_slotIndex) { - if (p_version == SlotKey::INVALID_VERSION) + if (p_version == InternalSlotKey::INVALID_VERSION) { - m_slots[p_slotIndex] = SlotKey {SlotKey::INVALID_VERSION, 0}; + m_slots[p_slotIndex] = InternalSlotKey {InternalSlotKey::INVALID_VERSION, 0}; return; } - m_slots[p_slotIndex] = SlotKey {p_version, m_freeSlotHead}; + m_slots[p_slotIndex] = InternalSlotKey {p_version, m_freeSlotHead}; m_freeSlotHead = p_slotIndex; } eastl::vector m_data; - eastl::vector m_slots; + eastl::vector m_slots; eastl::vector::size_type> m_dataToSlots; - SlotKey::IndexType m_freeSlotHead; + InternalSlotKey::IndexType m_freeSlotHead; }; } // namespace Bigfoot diff --git a/Bigfoot/Tests/Utils/Containers/SlotMap.cpp b/Bigfoot/Tests/Utils/Containers/SlotMap.cpp index 4995cb5..2d4aa7e 100644 --- a/Bigfoot/Tests/Utils/Containers/SlotMap.cpp +++ b/Bigfoot/Tests/Utils/Containers/SlotMap.cpp @@ -43,10 +43,11 @@ template class SlotKeyFixture: public ::testing::Test { protected: - using SlotMapVersion = typename CONFIG::Version; - using SlotMapIndex = typename CONFIG::Index; - using SlotMapType = SlotMap; - using SlotKey = typename SlotMapType::SlotKey; + using SlotMapVersionType = typename CONFIG::Version; + using SlotMapIndexType = typename CONFIG::Index; + + using SlotMapType = SlotMap; + using SlotKeyType = typename SlotMapType::InternalSlotKey; }; TYPED_TEST_SUITE(SlotKeyFixture, SlotMapConfigs, SlotMapConfigNames); @@ -55,10 +56,10 @@ TYPED_TEST_SUITE(SlotKeyFixture, SlotMapConfigs, SlotMapConfigNames); TYPED_TEST(SlotKeyFixture, DefaultSlotKeyIsInvalid) { - constexpr typename TestFixture::SlotMapIndex index = 0; - constexpr typename TestFixture::SlotMapVersion version = 0; + constexpr typename TestFixture::SlotMapIndexType index = 0; + constexpr typename TestFixture::SlotMapVersionType version = 0; - constexpr typename TestFixture::SlotKey slotKey {}; + constexpr typename TestFixture::SlotKeyType slotKey {}; EXPECT_FALSE(slotKey.Valid()); EXPECT_EQ(slotKey.GetVersion(), version); EXPECT_EQ(slotKey.GetIndex(), index); @@ -68,10 +69,10 @@ TYPED_TEST(SlotKeyFixture, DefaultSlotKeyIsInvalid) TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnTrueIfTheSlotKeyIsValid) { - constexpr typename TestFixture::SlotMapIndex index = 0; - constexpr typename TestFixture::SlotMapVersion version = 1; + constexpr typename TestFixture::SlotMapIndexType index = 0; + constexpr typename TestFixture::SlotMapVersionType version = 1; - constexpr typename TestFixture::SlotKey slotKey {version, index}; + constexpr typename TestFixture::SlotKeyType slotKey {version, index}; EXPECT_TRUE(slotKey.Valid()); } @@ -79,10 +80,10 @@ TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnTrueIfTheSlotKeyIsValid) TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnFalseIfTheSlotKeyIsValid) { - constexpr typename TestFixture::SlotMapIndex index = 0; - constexpr typename TestFixture::SlotMapVersion version = 0; + constexpr typename TestFixture::SlotMapIndexType index = 0; + constexpr typename TestFixture::SlotMapVersionType version = 0; - constexpr typename TestFixture::SlotKey slotKey {version, index}; + constexpr typename TestFixture::SlotKeyType slotKey {version, index}; EXPECT_FALSE(slotKey.Valid()); } @@ -90,10 +91,10 @@ TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnFalseIfTheSlotKeyIsValid) TYPED_TEST(SlotKeyFixture, GetVersion_ShouldReturnTheVersion) { - constexpr typename TestFixture::SlotMapIndex index = 0; - constexpr typename TestFixture::SlotMapVersion version = 42; + constexpr typename TestFixture::SlotMapIndexType index = 0; + constexpr typename TestFixture::SlotMapVersionType version = 42; - constexpr typename TestFixture::SlotKey slotKey {version, index}; + constexpr typename TestFixture::SlotKeyType slotKey {version, index}; EXPECT_EQ(slotKey.GetVersion(), version); } @@ -101,10 +102,10 @@ TYPED_TEST(SlotKeyFixture, GetVersion_ShouldReturnTheVersion) TYPED_TEST(SlotKeyFixture, GetIndex_ShouldReturnTheIndex) { - constexpr typename TestFixture::SlotMapIndex index = 42; - constexpr typename TestFixture::SlotMapVersion version = 0; + constexpr typename TestFixture::SlotMapIndexType index = 42; + constexpr typename TestFixture::SlotMapVersionType version = 0; - constexpr typename TestFixture::SlotKey slotKey {version, index}; + constexpr typename TestFixture::SlotKeyType slotKey {version, index}; EXPECT_EQ(slotKey.GetIndex(), index); } @@ -114,10 +115,11 @@ template class SlotMapFixture: public ::testing::Test { protected: - using SlotMapVersion = typename CONFIG::Version; - using SlotMapIndex = typename CONFIG::Index; - using SlotMapType = SlotMap; - using SlotKey = typename SlotMapType::SlotKey; + using SlotMapVersionType = typename CONFIG::Version; + using SlotMapIndexType = typename CONFIG::Index; + + using SlotMapType = SlotMap; + using SlotKeyType = typename SlotMapType::InternalSlotKey; SlotMapType m_slotMap; }; @@ -159,8 +161,8 @@ TYPED_TEST(SlotMapFixture, Has_ShouldReturnFalseIfTheSlotMapDoesNotHaveTheKey) const auto slotKey = this->m_slotMap.Insert(42); this->m_slotMap.Remove(slotKey); EXPECT_FALSE(this->m_slotMap.Has(slotKey)); - EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKey {1, 22})); - EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKey {})); + EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKeyType {1, 22})); + EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKeyType {})); } /****************************************************************************************/ @@ -176,7 +178,7 @@ TYPED_TEST(SlotMapFixture, Remove_ShouldRemoveTheSlotKey) TYPED_TEST(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { GTEST_SKIP() << "Skipped for 32-bit version: exhausting all versions is too slow."; } @@ -184,8 +186,8 @@ TYPED_TEST(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) { auto key = this->m_slotMap.Insert(1); - for (typename TestFixture::SlotMapVersion i = 1; - i < std::numeric_limits::max(); + for (typename TestFixture::SlotMapVersionType i = 1; + i < std::numeric_limits::max(); ++i) { this->m_slotMap.Remove(key); @@ -195,7 +197,7 @@ TYPED_TEST(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) } // Slot is at MAX_VERSION — one more remove should overflow and permanently deactivate it - EXPECT_EQ(key.GetVersion(), std::numeric_limits::max()); + EXPECT_EQ(key.GetVersion(), std::numeric_limits::max()); this->m_slotMap.Remove(key); EXPECT_FALSE(this->m_slotMap.Has(key)); @@ -205,7 +207,7 @@ TYPED_TEST(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) EXPECT_NE(newKey.GetIndex(), key.GetIndex()); // Ensure an invalid key does not return an exhausted slot - EXPECT_EQ(this->m_slotMap.Get(typename TestFixture::SlotKey {}), nullptr); + EXPECT_EQ(this->m_slotMap.Get(typename TestFixture::SlotKeyType {}), nullptr); } } @@ -228,7 +230,7 @@ TYPED_TEST(SlotMapFixture, Remove_ShouldNotDoAnythingInCaseStaleKey) { const auto slotKey1 = this->m_slotMap.Insert(42); - this->m_slotMap.Remove(typename TestFixture::SlotKey {2, 0}); + this->m_slotMap.Remove(typename TestFixture::SlotKeyType {2, 0}); EXPECT_EQ(*this->m_slotMap.Get(slotKey1), 42); } @@ -243,8 +245,8 @@ TYPED_TEST(SlotMapFixture, Get_ShouldReturnNullptrForInvalidSlotKeys) const auto validate = [&](auto& p_slotMap) { EXPECT_EQ(p_slotMap.Get(slotKey), nullptr); - EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKey {1, 3}), nullptr); - EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKey {}), nullptr); + EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKeyType {1, 3}), nullptr); + EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKeyType {}), nullptr); }; validate(this->m_slotMap);