Browse Source

sparse_set/storage: make storage safe to use from the base (to be tested)

Michele Caini 4 years ago
parent
commit
5b06f298f1
2 changed files with 97 additions and 58 deletions
  1. 29 27
      src/entt/entity/sparse_set.hpp
  2. 68 31
      src/entt/entity/storage.hpp

+ 29 - 27
src/entt/entity/sparse_set.hpp

@@ -242,19 +242,6 @@ class basic_sparse_set {
         }
     }
 
-    void append(const Entity entt) {
-        ENTT_ASSERT(count != reserved.second(), "Not enough space left");
-        ENTT_ASSERT(current(entt) == entity_traits::to_version(tombstone), "Slot not available");
-        assure_page(page(entt))[offset(entt)] = entity_traits::combine(static_cast<typename entity_traits::entity_type>(count), entity_traits::to_integral(entt));
-        packed_array[count++] = entt;
-    }
-
-    void recycle(const Entity entt) {
-        ENTT_ASSERT(current(entt) == entity_traits::to_version(tombstone), "Slot not available");
-        assure_page(page(entt))[offset(entt)] = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
-        free_list = std::exchange(packed_array[static_cast<size_type>(entity_traits::to_entity(free_list))], entt);
-    }
-
 protected:
     /*! @brief Exchanges the contents with those of a given sparse set. */
     virtual void swap_contents(basic_sparse_set &) {}
@@ -298,6 +285,27 @@ protected:
         ref = null;
     }
 
+    /**
+     * @brief Assigns an entity to a sparse set.
+     * @param entt A valid identifier.
+     */
+    virtual void try_emplace(const Entity entt, void *) {
+        ENTT_ASSERT(current(entt) == entity_traits::to_version(tombstone), "Slot not available");
+
+        if(free_list == null) {
+            if(const auto len = reserved.second(); count == len) {
+                const size_type sz = static_cast<size_type>(len * growth_factor_v);
+                resize_packed_array(sz + !(sz > len));
+            }
+
+            assure_page(page(entt))[offset(entt)] = entity_traits::combine(static_cast<typename entity_traits::entity_type>(count), entity_traits::to_integral(entt));
+            packed_array[count++] = entt;
+        } else {
+            assure_page(page(entt))[offset(entt)] = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
+            free_list = std::exchange(packed_array[static_cast<size_type>(entity_traits::to_entity(free_list))], entt);
+        }
+    }
+
 public:
     /*! @brief Allocator type. */
     using allocator_type = Allocator;
@@ -648,18 +656,11 @@ public:
      * results in undefined behavior.
      *
      * @param entt A valid identifier.
+     * @param ud Optional user data that are forwarded as-is to derived classes.
      */
-    void emplace(const entity_type entt) {
-        if(free_list == null) {
-            if(const auto len = reserved.second(); count == len) {
-                const size_type sz = static_cast<size_type>(len * growth_factor_v);
-                resize_packed_array(sz + !(sz > len));
-            }
-
-            append(entt);
-        } else {
-            recycle(entt);
-        }
+    void emplace(const entity_type entt, void *ud = nullptr) {
+        try_emplace(entt, ud);
+        ENTT_ASSERT(contains(entt), "Emplace did not take place");
     }
 
     /**
@@ -672,17 +673,18 @@ public:
      * @tparam It Type of input iterator.
      * @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.
+     * @param ud Optional user data that are forwarded as-is to derived classes.
      */
     template<typename It>
-    void insert(It first, It last) {
+    void insert(It first, It last, void *ud = nullptr) {
         for(; first != last && free_list != null; ++first) {
-            recycle(*first);
+            emplace(*first, ud);
         }
 
         reserve(count + std::distance(first, last));
 
         for(; first != last; ++first) {
-            append(*first);
+            emplace(*first, ud);
         }
     }
 

+ 68 - 31
src/entt/entity/storage.hpp

@@ -352,6 +352,26 @@ protected:
         destroy(packed[page(pos)][offset(pos)]);
     }
 
+    /**
+     * @brief Assigns an entity to a storage.
+     * @param entt A valid identifier.
+     * @param ud Optional user data that are forwarded as-is to derived classes.
+     */
+    void try_emplace(const Entity entt, void *ud) override {
+        if constexpr(std::is_default_constructible_v<value_type>) {
+            const auto pos = base_type::slot();
+            construct(assure_at_least(pos) + offset(pos));
+
+            ENTT_TRY {
+                base_type::try_emplace(entt, nullptr);
+                ENTT_ASSERT(pos == base_type::index(entt), "Misplaced component");
+            } ENTT_CATCH {
+                destroy(packed[page(pos)][offset(pos)]);
+                ENTT_THROW;
+            }
+        }
+    }
+
 public:
     /*! @brief Base type. */
     using base_type = underlying_type;
@@ -642,7 +662,7 @@ public:
         construct(elem, std::forward<Args>(args)...);
 
         ENTT_TRY {
-            base_type::emplace(entt);
+            base_type::try_emplace(entt, nullptr);
             ENTT_ASSERT(pos == base_type::index(entt), "Misplaced component");
         } ENTT_CATCH {
             destroy(packed[page(pos)][offset(pos)]);
@@ -803,8 +823,8 @@ public:
      */
     template<typename... Args>
     void emplace(const entity_type entt, Args &&... args) {
-        [[maybe_unused]] value_type elem{std::forward<Args>(args)...};
-        base_type::emplace(entt);
+        [[maybe_unused]] const value_type elem{std::forward<Args>(args)...};
+        base_type::try_emplace(entt, nullptr);
     }
 
     /**
@@ -828,7 +848,17 @@ public:
      */
     template<typename It, typename... Args>
     void insert(It first, It last, Args &&...) {
-        base_type::insert(std::move(first), std::move(last));
+        if constexpr(comp_traits::in_place_delete::value) {
+            for(const auto sz = base_type::size(); first != last && base_type::slot() != sz; ++first) {
+                emplace(*first);
+            }
+        }
+
+        base_type::reserve(base_type::size() + std::distance(first, last));
+
+        for(; first != last; ++first) {
+            emplace(*first);
+        }
     }
 };
 
@@ -861,6 +891,18 @@ struct storage_adapter_mixin: Type {
         return Type::emplace(entt, std::forward<Args>(args)...);
     }
 
+    /**
+     * @brief Patches the given instance for an entity.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched instance.
+     */
+    template<typename... Func>
+    decltype(auto) patch(basic_registry<entity_type> &, const entity_type entt, Func &&... func) {
+        return Type::patch(entt, std::forward<Func>(func)...);
+    }
+
     /**
      * @brief Assigns entities to a storage.
      * @tparam It Type of input iterator.
@@ -875,18 +917,6 @@ struct storage_adapter_mixin: Type {
     void insert(basic_registry<entity_type> &, It first, It last, Args &&... args) {
         Type::insert(std::move(first), std::move(last), std::forward<Args>(args)...);
     }
-
-    /**
-     * @brief Patches the given instance for an entity.
-     * @tparam Func Types of the function objects to invoke.
-     * @param entt A valid identifier.
-     * @param func Valid function objects.
-     * @return A reference to the patched instance.
-     */
-    template<typename... Func>
-    decltype(auto) patch(basic_registry<entity_type> &, const entity_type entt, Func &&... func) {
-        return Type::patch(entt, std::forward<Func>(func)...);
-    }
 };
 
 
@@ -910,6 +940,13 @@ class sigh_storage_mixin final: public Type {
         Type::in_place_pop(entt, ud);
     }
 
+    /*! @copydoc basic_sparse_set::try_emplace */
+    void try_emplace(const typename Type::entity_type entt, void *ud) final {
+        ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry");
+        Type::try_emplace(entt, ud);
+        construction.publish(*static_cast<basic_registry<typename Type::entity_type> *>(ud), entt);
+    }
+
 public:
     /*! @brief Underlying value type. */
     using value_type = typename Type::value_type;
@@ -999,6 +1036,21 @@ public:
         return this->get(entt);
     }
 
+    /**
+     * @brief Patches the given instance for an entity.
+     * @tparam Func Types of the function objects to invoke.
+     * @param owner The registry that issued the request.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched instance.
+     */
+    template<typename... Func>
+    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entt, Func &&... func) {
+        Type::patch(entt, std::forward<Func>(func)...);
+        update.publish(owner, entt);
+        return this->get(entt);
+    }
+
     /**
      * @brief Assigns entities to a storage.
      * @tparam It Type of input iterator.
@@ -1021,21 +1073,6 @@ public:
         }
     }
 
-    /**
-     * @brief Patches the given instance for an entity.
-     * @tparam Func Types of the function objects to invoke.
-     * @param owner The registry that issued the request.
-     * @param entt A valid identifier.
-     * @param func Valid function objects.
-     * @return A reference to the patched instance.
-     */
-    template<typename... Func>
-    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entt, Func &&... func) {
-        Type::patch(entt, std::forward<Func>(func)...);
-        update.publish(owner, entt);
-        return this->get(entt);
-    }
-
 private:
     sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
     sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};