Browse Source

storage: ::insert always fills holes before appending

Michele Caini 4 years ago
parent
commit
60c175a51d
2 changed files with 32 additions and 33 deletions
  1. 27 26
      src/entt/entity/storage.hpp
  2. 5 7
      test/entt/entity/storage.cpp

+ 27 - 26
src/entt/entity/storage.hpp

@@ -270,6 +270,31 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
         alloc_traits::destroy(bucket.first(), std::addressof(elem));
     }
 
+    template<typename It, typename Generator>
+    void recycle_or_append(It first, It last, Generator generator) {
+        if constexpr(comp_traits::in_place_delete::value) {
+            for(const auto sz = base_type::size(); first != last && base_type::slot() != sz; ++first) {
+                emplace(*first, generator());
+            }
+        }
+
+        const auto req = base_type::size() + std::distance(first, last);
+        base_type::reserve(req);
+        reserve(req);
+
+        for(; first != last; ++first) {
+            const auto pos = base_type::size();
+            construct(packed[page(pos)] + offset(pos), generator());
+
+            ENTT_TRY {
+                base_type::emplace_back(*first);
+            } ENTT_CATCH {
+                destroy(packed[page(pos)][offset(pos)]);
+                ENTT_THROW;
+            }
+        }
+    }
+
 protected:
     /**
      * @brief Exchanges the contents with those of a given storage.
@@ -665,19 +690,7 @@ public:
      */
     template<typename It>
     void insert(It first, It last, const value_type &value = {}) {
-        reserve(base_type::size() + std::distance(first, last));
-
-        for(; first != last; ++first) {
-            const auto pos = base_type::size();
-            construct(packed[page(pos)] + offset(pos), value);
-
-            ENTT_TRY {
-                base_type::emplace_back(*first);
-            } ENTT_CATCH {
-                destroy(packed[page(pos)][offset(pos)]);
-                ENTT_THROW;
-            }
-        }
+        recycle_or_append(std::move(first), std::move(last), [&value]() -> decltype(auto) { return value; });
     }
 
     /**
@@ -694,19 +707,7 @@ public:
      */
     template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<std::decay_t<typename std::iterator_traits<CIt>::value_type>, value_type>>>
     void insert(EIt first, EIt last, CIt from) {
-        reserve(base_type::size() + std::distance(first, last));
-
-        for(; first != last; ++first, ++from) {
-            const auto pos = base_type::size();
-            construct(packed[page(pos)] + offset(pos), *from);
-
-            ENTT_TRY {
-                base_type::emplace_back(*first);
-            } ENTT_CATCH {
-                destroy(packed[page(pos)][offset(pos)]);
-                ENTT_THROW;
-            }
-        }
+        recycle_or_append(std::move(first), std::move(last), [&from]() -> decltype(auto) { return *(from++); });
     }
 
 private:

+ 5 - 7
test/entt/entity/storage.cpp

@@ -228,13 +228,11 @@ TEST(Storage, Insert) {
     const stable_type values[2u] = { stable_type{42}, stable_type{3} };
     pool.insert(std::rbegin(entities), std::rend(entities), std::begin(values));
 
-    ASSERT_EQ(pool.size(), 4u);
-    ASSERT_TRUE(pool.at(0u) == entt::tombstone);
-    ASSERT_TRUE(pool.at(1u) == entt::tombstone);
-    ASSERT_EQ(pool.at(2u), entities[1u]);
-    ASSERT_EQ(pool.at(3u), entities[0u]);
-    ASSERT_EQ(pool.index(entities[0u]), 3u);
-    ASSERT_EQ(pool.index(entities[1u]), 2u);
+    ASSERT_EQ(pool.size(), 2u);
+    ASSERT_EQ(pool.at(0u), entities[0u]);
+    ASSERT_EQ(pool.at(1u), entities[1u]);
+    ASSERT_EQ(pool.index(entities[0u]), 0u);
+    ASSERT_EQ(pool.index(entities[1u]), 1u);
     ASSERT_EQ(pool.get(entities[0u]).value, 3);
     ASSERT_EQ(pool.get(entities[1u]).value, 42);
 }