Pārlūkot izejas kodu

sparse_set: opaque insert always appends elements

Michele Caini 4 gadi atpakaļ
vecāks
revīzija
0a259da05a

+ 7 - 10
src/entt/entity/sparse_set.hpp

@@ -691,16 +691,13 @@ public:
      */
     template<typename It>
     iterator insert(It first, It last) {
-        auto it = first;
-
-        for(; it != last && free_list != null; ++it) {
-            emplace(*it);
-        }
-
-        reserve(packed.size() + std::distance(it, last));
-
-        for(; it != last; ++it) {
-            emplace(*it);
+        if constexpr(std::is_invocable_v<decltype(&basic_sparse_set::try_insert), basic_sparse_set &, It, It>) {
+            try_insert(first, last);
+        } else {
+            for(auto it = first; it != last; ++it) {
+                entity_type curr[1u]{*it};
+                try_insert(curr, curr + 1u);
+            }
         }
 
         return first == last ? end() : find(*first);

+ 28 - 10
src/entt/entity/storage.hpp

@@ -349,7 +349,6 @@ protected:
     void try_erase(const Entity entt) override {
         const auto pos = base_type::index(entt);
         auto &elem = element_at(comp_traits::in_place_delete ? pos : (base_type::size() - 1u));
-        // support chained destructors i.e. parent-to-child propagation
         [[maybe_unused]] auto unused = std::exchange(element_at(pos), std::move(elem));
         std::destroy_at(std::addressof(elem));
         base_type::try_erase(entt);
@@ -372,6 +371,19 @@ protected:
         }
     }
 
+    /**
+     * @brief Assigns one or more entities to a storage.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+    void try_insert([[maybe_unused]] const Entity *first, [[maybe_unused]] const Entity *last) override {
+        if constexpr(std::is_default_constructible_v<value_type>) {
+            consume_range(first, last, [this](auto *elem) mutable {
+                alloc_traits::construct(packed.second(), elem);
+            });
+        }
+    }
+
 public:
     /*! @brief Base type. */
     using base_type = underlying_type;
@@ -629,9 +641,6 @@ public:
 
     /**
      * @brief Returns the object assigned to an entity as a tuple.
-     *
-     * @sa get
-     *
      * @param entt A valid identifier.
      * @return The object assigned to the entity as a tuple.
      */
@@ -658,23 +667,23 @@ public:
      */
     template<typename... Args>
     value_type &emplace(const entity_type entt, Args &&...args) {
-        // support chained constructors i.e. parent-to-child propagation
         base_type::try_emplace(entt);
-        const auto pos = base_type::index(entt);
 
         ENTT_TRY {
+            auto elem = assure_at_least(base_type::index(entt));
+
             if constexpr(std::is_aggregate_v<value_type>) {
-                alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), Type{std::forward<Args>(args)...});
+                alloc_traits::construct(packed.second(), to_address(elem), Type{std::forward<Args>(args)...});
             } else {
-                alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), std::forward<Args>(args)...);
+                alloc_traits::construct(packed.second(), to_address(elem), std::forward<Args>(args)...);
             }
+
+            return *elem;
         }
         ENTT_CATCH {
             base_type::try_erase(entt);
             ENTT_THROW;
         }
-
-        return element_at(pos);
     }
 
     /**
@@ -934,6 +943,15 @@ class sigh_storage_mixin final: public Type {
         construction.publish(*owner, entt);
     }
 
+    void try_insert(const typename Type::entity_type *first, const typename Type::entity_type *last) final {
+        ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+        Type::try_insert(first, last);
+
+        for(auto it = construction.empty() ? last : first; it != last; ++it) {
+            construction.publish(*owner, *it);
+        }
+    }
+
 public:
     /*! @brief Underlying value type. */
     using value_type = typename Type::value_type;

+ 38 - 16
test/entt/entity/sparse_set.cpp

@@ -337,11 +337,11 @@ TEST(SparseSet, Insert) {
 
     ASSERT_NE(set.insert(std::rbegin(entities), std::rend(entities)), set.end());
 
-    ASSERT_EQ(set.size(), 4u);
-    ASSERT_EQ(set.at(1u), entities[0u]);
-    ASSERT_EQ(set.at(2u), entities[1u]);
-    ASSERT_EQ(set.index(entities[0u]), 1u);
-    ASSERT_EQ(set.index(entities[1u]), 2u);
+    ASSERT_EQ(set.size(), 6u);
+    ASSERT_EQ(set.at(4u), entities[1u]);
+    ASSERT_EQ(set.at(5u), entities[0u]);
+    ASSERT_EQ(set.index(entities[0u]), 5u);
+    ASSERT_EQ(set.index(entities[1u]), 4u);
 }
 
 TEST(SparseSet, Erase) {
@@ -409,7 +409,10 @@ TEST(SparseSet, StableErase) {
     ASSERT_TRUE(set.empty());
     ASSERT_EQ(set.size(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
+
     set.erase(set.begin(), set.end());
 
     ASSERT_FALSE(set.empty());
@@ -422,7 +425,10 @@ TEST(SparseSet, StableErase) {
     ASSERT_TRUE(set.at(2u) == entt::tombstone);
     ASSERT_EQ(set.slot(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
+
     set.erase(entities, entities + 2u);
 
     ASSERT_FALSE(set.empty());
@@ -443,7 +449,10 @@ TEST(SparseSet, StableErase) {
     ASSERT_EQ(set.current(entities[2u]), traits_type::to_version(entt::tombstone));
     ASSERT_EQ(set.slot(), 2u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
+
     std::swap(entities[1u], entities[2u]);
     set.erase(entities, entities + 2u);
 
@@ -478,7 +487,10 @@ TEST(SparseSet, StableErase) {
 
     ASSERT_EQ(set.size(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
+
     set.erase(entities[2u]);
 
     ASSERT_DEATH(set.erase(entities[2u]), "");
@@ -593,7 +605,9 @@ TEST(SparseSet, StableRemove) {
     ASSERT_TRUE(set.empty());
     ASSERT_EQ(set.size(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
 
     ASSERT_EQ(set.remove(set.begin(), set.end()), 3u);
     ASSERT_EQ(set.remove(set.begin(), set.end()), 0u);
@@ -608,7 +622,9 @@ TEST(SparseSet, StableRemove) {
     ASSERT_TRUE(set.at(2u) == entt::tombstone);
     ASSERT_EQ(set.slot(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
 
     ASSERT_EQ(set.remove(entities, entities + 2u), 2u);
     ASSERT_EQ(set.remove(entities, entities + 2u), 0u);
@@ -631,7 +647,10 @@ TEST(SparseSet, StableRemove) {
     ASSERT_EQ(set.current(entities[2u]), traits_type::to_version(entt::tombstone));
     ASSERT_EQ(set.slot(), 2u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
+
     std::swap(entities[1u], entities[2u]);
 
     ASSERT_EQ(set.remove(entities, entities + 2u), 2u);
@@ -668,7 +687,9 @@ TEST(SparseSet, StableRemove) {
 
     ASSERT_EQ(set.size(), 0u);
 
-    set.insert(std::begin(entities), std::end(entities));
+    set.emplace(entities[0u]);
+    set.emplace(entities[1u]);
+    set.emplace(entities[2u]);
 
     ASSERT_EQ(set.remove(entities[2u]), 1u);
     ASSERT_EQ(set.remove(entities[2u]), 0u);
@@ -1000,6 +1021,7 @@ TEST(SparseSet, SortRange) {
     ASSERT_EQ(set[3u], entities[2u]);
 
     set.clear();
+    set.compact();
     set.insert(std::begin(entities), std::end(entities));
     set.sort_n(0u, std::less{});
 
@@ -1263,10 +1285,10 @@ TEST(SparseSet, ThrowingAllocator) {
     ASSERT_THROW(set.insert(std::begin(entities), std::end(entities)), test::throwing_allocator<entt::entity>::exception_type);
     ASSERT_EQ(set.extent(), 2 * ENTT_SPARSE_PAGE);
     ASSERT_TRUE(set.contains(entt::entity{0}));
-    ASSERT_TRUE(set.contains(entt::entity{1}));
+    ASSERT_FALSE(set.contains(entt::entity{1}));
     ASSERT_FALSE(set.contains(entt::entity{ENTT_SPARSE_PAGE}));
-    ASSERT_EQ(set.capacity(), 3u);
-    ASSERT_EQ(set.size(), 2u);
+    ASSERT_EQ(set.capacity(), 1u);
+    ASSERT_EQ(set.size(), 1u);
 
     set.emplace(entities[1u]);
 

+ 20 - 3
test/entt/entity/storage.cpp

@@ -648,23 +648,40 @@ TEST(Storage, EmptyTypeFromBase) {
 
     ASSERT_NE(base.emplace(entities[0u], &instance), base.end());
 
+    ASSERT_EQ(pool.size(), 1u);
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
     ASSERT_EQ(base.get(entities[0u]), nullptr);
+    ASSERT_EQ(base.index(entities[0u]), 0u);
 
     base.erase(entities[0u]);
 
     ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
 
+    ASSERT_EQ(pool.size(), 3u);
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_TRUE(pool.contains(entities[1u]));
+    ASSERT_EQ(base.index(entities[0u]), 1u);
+    ASSERT_EQ(base.index(entities[1u]), 2u);
+
+    base.erase(std::begin(entities), std::end(entities));
+
+    ASSERT_NE(base.insert(std::rbegin(entities), std::rend(entities)), base.end());
+
+    ASSERT_EQ(pool.size(), 5u);
+    ASSERT_TRUE(pool.contains(entities[0u]));
+    ASSERT_TRUE(pool.contains(entities[1u]));
+    ASSERT_EQ(base.index(entities[0u]), 4u);
+    ASSERT_EQ(base.index(entities[1u]), 3u);
 
     base.erase(std::begin(entities), std::end(entities));
 
     ASSERT_FALSE(pool.empty());
-    ASSERT_EQ(pool.size(), 2u);
-    ASSERT_TRUE(*base.begin() == entt::tombstone);
-    ASSERT_TRUE(*(++base.begin()) == entt::tombstone);
+    ASSERT_EQ(pool.size(), 5u);
+
+    for(std::size_t pos{}, last = base.size(); pos != last; ++pos) {
+        ASSERT_TRUE(base[pos] == entt::tombstone);
+    }
 }
 
 TEST(Storage, NonDefaultConstructibleTypeFromBase) {