Prechádzať zdrojové kódy

storage: swap-only entity storage

Michele Caini 3 rokov pred
rodič
commit
9f31803ba7
1 zmenil súbory, kde vykonal 244 pridanie a 0 odobranie
  1. 244 0
      src/entt/entity/storage.hpp

+ 244 - 0
src/entt/entity/storage.hpp

@@ -914,6 +914,250 @@ public:
     }
 };
 
+/**
+ * @brief Swap-only entity storage specialization.
+ * @tparam Entity A valid entity type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Allocator>
+class basic_storage<Entity, Entity, Allocator>
+    : public basic_sparse_set<Entity, Allocator> {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
+    using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
+    using underlying_iterator = typename underlying_type::basic_iterator;
+    using local_traits_type = entt_traits<Entity>;
+
+    auto next(const std::size_t pos) const noexcept {
+        ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element");
+        return local_traits_type::combine(static_cast<typename local_traits_type::entity_type>(pos), {});
+    }
+
+private:
+    void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override {
+        ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported");
+    }
+
+protected:
+    /**
+     * @brief Erases entities from 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 pop(underlying_iterator first, underlying_iterator last) override {
+        for(; first != last; ++first) {
+            if(const auto pos = base_type::index(*first); pos < length) {
+                const auto vers = local_traits_type::to_version(*first) + 1;
+                base_type::bump(local_traits_type::construct(local_traits_type::to_entity(*first), static_cast<typename local_traits_type::version_type>(vers + (vers == local_traits_type::to_version(tombstone)))));
+                (pos == --length) || (base_type::swap_at(pos, length), true);
+            }
+        }
+    }
+
+    /*! @brief Erases all entities of a sparse set. */
+    void pop_all() override {
+        for(; length; --length) {
+            const auto entt = base_type::operator[](length - 1u);
+            const auto vers = local_traits_type::to_version(entt) + 1;
+            base_type::bump(local_traits_type::construct(local_traits_type::to_entity(entt), static_cast<typename local_traits_type::version_type>(vers + (vers == local_traits_type::to_version(tombstone)))));
+        }
+    }
+
+    /**
+     * @brief Assigns an entity to a storage.
+     * @param hint A valid identifier.
+     * @return Iterator pointing to the emplaced element.
+     */
+    underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
+        return base_type::find(spawn(hint));
+    }
+
+public:
+    /*! @brief Base type. */
+    using base_type = basic_sparse_set<Entity, Allocator>;
+    /*! @brief Type of the objects assigned to entities. */
+    using value_type = void;
+    /*! @brief Component traits. */
+    using traits_type = component_traits<void>;
+    /*! @brief Underlying entity identifier. */
+    using entity_type = Entity;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Extended iterable storage proxy. */
+    using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
+    /*! @brief Constant extended iterable storage proxy. */
+    using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
+
+    /*! @brief Default constructor. */
+    basic_storage()
+        : basic_storage{allocator_type{}} {
+    }
+
+    /**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_storage(const allocator_type &allocator)
+        : base_type{type_id<void>(), deletion_policy::swap_and_pop, allocator},
+          length{} {}
+
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    basic_storage(basic_storage &&other) noexcept
+        : base_type{std::move(other)},
+          length{std::exchange(other.length, size_type{})} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
+        : base_type{std::move(other), allocator},
+          length{std::exchange(other.length, size_type{})} {}
+
+    /**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+    basic_storage &operator=(basic_storage &&other) noexcept {
+        base_type::operator=(std::move(other));
+        length = std::exchange(other.length, size_type{});
+        return *this;
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given storage.
+     * @param other Storage to exchange the content with.
+     */
+    void swap(basic_storage &other) {
+        using std::swap;
+        base_type::swap(other);
+        swap(length, other.length);
+    }
+
+    /**
+     * @brief Creates a new identifier or recycles a destroyed one.
+     * @return A valid identifier.
+     */
+    entity_type spawn() {
+        if(length == base_type::size()) {
+            return *base_type::try_emplace(next(length++), true);
+        }
+
+        return base_type::operator[](length++);
+    }
+
+    /**
+     * @brief Creates a new identifier or recycles a destroyed one.
+     *
+     * If the requested identifier isn't in use, the suggested one is used.
+     * Otherwise, a new identifier is returned.
+     *
+     * @param hint Required identifier.
+     * @return A valid identifier.
+     */
+    entity_type spawn(const entity_type hint) {
+        if(hint == null || hint == tombstone) {
+            return spawn();
+        } else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
+            const auto pos = static_cast<size_type>(local_traits_type::to_entity(hint));
+
+            while(!(pos < base_type::size())) {
+                base_type::try_emplace(next(base_type::size()), true);
+            }
+
+            base_type::swap_at(pos, length++);
+        } else if(const auto idx = base_type::index(curr); idx < length) {
+            return spawn();
+        } else {
+            base_type::swap_at(idx, length++);
+        }
+
+        base_type::bump(hint);
+
+        return hint;
+    }
+
+    /**
+     * @brief Assigns each element in a range an identifier.
+     * @tparam It Type of mutable forward iterator.
+     * @param first An iterator to the first element of the range to generate.
+     * @param last An iterator past the last element of the range to generate.
+     */
+    template<typename It>
+    void spawn(It first, It last) {
+        for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) {
+            *first = base_type::operator[](length);
+        }
+
+        for(; first != last; ++first) {
+            *first = *base_type::try_emplace(next(length++), true);
+        }
+    }
+
+    /**
+     * @brief Makes all elements in a range contiguous.
+     * @tparam It Type of forward iterator.
+     * @param first An iterator to the first element of the range to pack.
+     * @param last An iterator past the last element of the range to pack.
+     * @return The number of elements within the newly created range.
+     */
+    template<typename It>
+    typename size_type pack(It first, It last) {
+        size_type next = length;
+
+        for(; first != last; ++first, --next) {
+            const auto pos = base_type::index(*first);
+            ENTT_ASSERT(pos < length, "Invalid element");
+            base_type::swap_at(pos, static_cast<size_type>(next - 1u));
+        }
+
+        return (length - next);
+    }
+
+    /**
+     * @brief Returns an iterable object to use to _visit_ a storage.
+     *
+     * The iterable object returns a tuple that contains the current entity.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+    [[nodiscard]] iterable each() noexcept {
+        return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}};
+    }
+
+    /*! @copydoc each */
+    [[nodiscard]] const_iterable each() const noexcept {
+        return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}};
+    }
+
+    /**
+     * @brief Returns the number of elements considered still in use.
+     * @return The number of elements considered still in use.
+     */
+    [[nodiscard]] size_type in_use() const noexcept {
+        return length;
+    }
+
+    /**
+     * @brief Sets the number of elements considered still in use.
+     * @param len The number of elements considered still in use.
+     */
+    void in_use(const size_type len) noexcept {
+        ENTT_ASSERT(!(len > base_type::size()), "Invalid length");
+        length = len;
+    }
+
+private:
+    size_type length;
+};
+
 } // namespace entt
 
 #endif