diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 8aaf64642..3417f3629 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -240,12 +240,14 @@ protected: */ virtual void swap_and_pop(basic_iterator first, basic_iterator last) { for(; first != last; ++first) { - sparse_ref(packed.back()) = entity_traits::combine(static_cast(first.index()), entity_traits::to_integral(packed.back())); - const auto entt = std::exchange(packed[first.index()], packed.back()); + auto &self = sparse_ref(*first); + const auto entt = entity_traits::to_entity(self); + sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((packed.back() = tombstone, true), ""); // lazy self-assignment guard - sparse_ref(entt) = null; + self = null; packed.pop_back(); } } @@ -257,8 +259,8 @@ protected: */ virtual void in_place_pop(basic_iterator first, basic_iterator last) { for(; first != last; ++first) { - sparse_ref(*first) = null; - packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast(first.index()), entity_traits::reserved)); + const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*first), null)); + packed[static_cast(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); } } diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index 0baca647d..c6d5f36aa 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -428,6 +428,22 @@ TEST(SparseSetDeathTest, Erase) { ASSERT_DEATH(set.erase(entt::null), ""); } +TEST(SparseSet, CrossErase) { + using traits_type = entt::entt_traits; + + entt::sparse_set set; + entt::sparse_set other; + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + + set.insert(std::begin(entities), std::end(entities)); + other.emplace(entities[1u]); + set.erase(other.begin(), other.end()); + + ASSERT_TRUE(set.contains(entities[0u])); + ASSERT_FALSE(set.contains(entities[1u])); + ASSERT_EQ(set.data()[0u], entities[0u]); +} + TEST(SparseSet, StableErase) { using traits_type = entt::entt_traits; @@ -552,6 +568,22 @@ TEST(SparseSetDeathTest, StableErase) { ASSERT_DEATH(set.erase(entt::null), ""); } +TEST(SparseSet, CrossStableErase) { + using traits_type = entt::entt_traits; + + entt::sparse_set set{entt::deletion_policy::in_place}; + entt::sparse_set other{entt::deletion_policy::in_place}; + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + + set.insert(std::begin(entities), std::end(entities)); + other.emplace(entities[1u]); + set.erase(other.begin(), other.end()); + + ASSERT_TRUE(set.contains(entities[0u])); + ASSERT_FALSE(set.contains(entities[1u])); + ASSERT_EQ(set.data()[0u], entities[0u]); +} + TEST(SparseSet, Remove) { using traits_type = entt::entt_traits; @@ -609,6 +641,22 @@ TEST(SparseSet, Remove) { ASSERT_EQ(set.remove(entt::null), 0u); } +TEST(SparseSet, CrossRemove) { + using traits_type = entt::entt_traits; + + entt::sparse_set set; + entt::sparse_set other; + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + + set.insert(std::begin(entities), std::end(entities)); + other.emplace(entities[1u]); + set.remove(other.begin(), other.end()); + + ASSERT_TRUE(set.contains(entities[0u])); + ASSERT_FALSE(set.contains(entities[1u])); + ASSERT_EQ(set.data()[0u], entities[0u]); +} + TEST(SparseSet, StableRemove) { using traits_type = entt::entt_traits; @@ -739,6 +787,22 @@ TEST(SparseSet, StableRemove) { ASSERT_EQ(set.remove(entt::null), 0u); } +TEST(SparseSet, CrossStableRemove) { + using traits_type = entt::entt_traits; + + entt::sparse_set set{entt::deletion_policy::in_place}; + entt::sparse_set other{entt::deletion_policy::in_place}; + entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; + + set.insert(std::begin(entities), std::end(entities)); + other.emplace(entities[1u]); + set.remove(other.begin(), other.end()); + + ASSERT_TRUE(set.contains(entities[0u])); + ASSERT_FALSE(set.contains(entities[1u])); + ASSERT_EQ(set.data()[0u], entities[0u]); +} + TEST(SparseSet, Compact) { entt::sparse_set set{entt::deletion_policy::in_place};