diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 4945bca4e..a7b5553e6 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -225,12 +225,28 @@ private: return nullptr; } - virtual void swap_at(const std::size_t, const std::size_t) {} + virtual void swap_or_move(const std::size_t, const std::size_t) {} protected: /*! @brief Random access iterator type. */ using basic_iterator = internal::sparse_set_iterator; + /** + * @brief Swaps two items at specific locations. + * @param lhs A position to move from. + * @param rhs The other position to move from. + */ + void swap_at(const std::size_t lhs, const std::size_t rhs) { + const auto entity = static_cast(lhs); + const auto other = static_cast(rhs); + + sparse_ref(packed[lhs]) = traits_type::combine(other, traits_type::to_integral(packed[lhs])); + sparse_ref(packed[rhs]) = traits_type::combine(entity, traits_type::to_integral(packed[rhs])); + + using std::swap; + swap(packed[lhs], packed[rhs]); + } + /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. @@ -784,13 +800,12 @@ public: for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[traits_type::to_entity(*it)])) { if(const size_type to = traits_type::to_entity(*it); to < from) { --from; - swap_at(from, to); - - using std::swap; - swap(packed[from], packed[to]); + swap_or_move(from, to); + packed[to] = std::exchange(packed[from], tombstone); const auto entity = static_cast(to); sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); + *it = traits_type::combine(static_cast(from), tombstone); for(; from && packed[from - 1u] == tombstone; --from) {} } @@ -814,22 +829,12 @@ public: * @param rhs A valid identifier. */ void swap_elements(const entity_type lhs, const entity_type rhs) { - ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); + const auto from = index(lhs); + const auto to = index(rhs); - auto &entt = sparse_ref(lhs); - auto &other = sparse_ref(rhs); - - const auto from = traits_type::to_entity(entt); - const auto to = traits_type::to_entity(other); - - // basic no-leak guarantee (with invalid state) if swapping throws - swap_at(static_cast(from), static_cast(to)); - - entt = traits_type::combine(to, traits_type::to_integral(packed[from])); - other = traits_type::combine(from, traits_type::to_integral(packed[to])); - - using std::swap; - swap(packed[from], packed[to]); + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); } /** @@ -877,7 +882,7 @@ public: const auto idx = index(packed[next]); const auto entt = packed[curr]; - swap_at(next, idx); + swap_or_move(next, idx); const auto entity = static_cast(curr); sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr])); curr = std::exchange(next, idx); diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 3b04e24e0..d89e3e413 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -305,7 +305,7 @@ private: return std::addressof(element_at(pos)); } - void swap_at([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");