diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 848cfc4c8..f70d788a0 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -191,37 +191,35 @@ class basic_sparse_set { return sparse[idx]; } - void maybe_release_pages() { - if(!count && bucket) { + void resize_packed(const std::size_t req) { + ENTT_ASSERT(req && (req != reserved) && !(req < count), "Invalid request"); + auto old = std::exchange(packed, alloc_traits::allocate(allocator, req)); + + for(size_type pos{}; pos < count; ++pos) { + alloc_traits::construct(allocator, std::addressof(packed[pos]), std::move(old[pos])); + alloc_traits::destroy(allocator, std::addressof(old[pos])); + } + + alloc_traits::deallocate(allocator, old, std::exchange(reserved, req)); + } + + void release_memory() { + if(reserved) { + std::destroy(packed, packed + std::exchange(count, 0u)); + alloc_traits::deallocate(allocator, std::exchange(packed, alloc_pointer{}), std::exchange(reserved, 0u)); + } + + if(bucket) { for(size_type pos{}; pos < bucket; ++pos) { if(sparse[pos]) { std::destroy(sparse[pos], sparse[pos] + page_size); alloc_traits::deallocate(allocator, sparse[pos], page_size); } - + bucket_alloc_traits::destroy(bucket_allocator, std::addressof(sparse[pos])); } - - bucket_alloc_traits::deallocate(bucket_allocator, sparse, std::exchange(bucket, 0u)); - } - } - - void maybe_resize_packed(const std::size_t req) { - if(const auto length = std::exchange(reserved, req); !reserved) { - if(length) { - std::destroy(packed, packed + std::exchange(count, 0u)); - alloc_traits::deallocate(allocator, packed, length); - } - } else if(reserved != length) { - ENTT_ASSERT(!(req < count), "Invalid request"); - auto old = std::exchange(packed, alloc_traits::allocate(allocator, reserved)); - - for(size_type pos{}; pos < count; ++pos) { - alloc_traits::construct(allocator, std::addressof(packed[pos]), std::move(old[pos])); - alloc_traits::destroy(allocator, std::addressof(old[pos])); - } - alloc_traits::deallocate(allocator, old, length); + bucket_alloc_traits::deallocate(bucket_allocator, sparse, std::exchange(bucket, 0u)); } } @@ -317,8 +315,7 @@ public: /*! @brief Default destructor. */ virtual ~basic_sparse_set() { - maybe_resize_packed(0u); - maybe_release_pages(); + release_memory(); } /** @@ -327,8 +324,7 @@ public: * @return This sparse set. */ basic_sparse_set & operator=(basic_sparse_set &&other) ENTT_NOEXCEPT { - maybe_resize_packed(0u); - maybe_release_pages(); + release_memory(); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); @@ -351,7 +347,7 @@ public: */ void reserve(const size_type cap) { if(cap > reserved) { - maybe_resize_packed(cap); + resize_packed(cap); } } @@ -366,8 +362,11 @@ public: /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { - maybe_resize_packed(count); - maybe_release_pages(); + if(!count && reserved) { + release_memory(); + } else if(count && count != reserved) { + resize_packed(count); + } } /** @@ -538,7 +537,7 @@ public: if(count == reserved) { const size_type sz(reserved * growth_factor); - maybe_resize_packed(sz + !(sz > reserved)); + resize_packed(sz + !(sz > reserved)); } push_back(entt); @@ -554,19 +553,15 @@ 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. - * @return The number of elements assigned to the sparse set. */ template - size_type insert(It first, It last) { - const auto length = std::distance(first, last); - reserve(count + length); + void insert(It first, It last) { + reserve(count + std::distance(first, last)); for(; first != last; ++first) { ENTT_ASSERT(!contains(*first), "Set already contains entity"); push_back(*first); } - - return length; } /** diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index a59ce63d1..e64dc7491 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -179,24 +179,27 @@ class basic_storage: public basic_sparse_set page_size ? page_size : count; std::destroy(packed[pos], packed[pos] + sz); count -= sz; } - + alloc_traits::deallocate(allocator, packed[pos], page_size); bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos])); } - if(length) { - bucket_alloc_traits::deallocate(bucket_allocator, packed, length); - } - } else if(bucket != length) { - ENTT_ASSERT(!(req < count), "Invalid request"); + bucket_alloc_traits::deallocate(bucket_allocator, std::exchange(packed, bucket_alloc_pointer{}), std::exchange(bucket, 0u)); + } + } + + void maybe_resize_packed(const std::size_t req) { + ENTT_ASSERT(req && !(req < count), "Invalid request"); + + if(const auto length = std::exchange(bucket, (req + page_size - 1u) / page_size); bucket != length) { const auto old = std::exchange(packed, bucket_alloc_traits::allocate(bucket_allocator, bucket)); if(bucket > length) { @@ -308,7 +311,7 @@ public: /*! @brief Default destructor. */ ~basic_storage() override { - maybe_resize_packed(0u); + release_memory(); } /** @@ -317,7 +320,7 @@ public: * @return This sparse set. */ basic_storage & operator=(basic_storage &&other) ENTT_NOEXCEPT { - maybe_resize_packed(0u); + release_memory(); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); @@ -356,7 +359,7 @@ public: /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { underlying_type::shrink_to_fit(); - maybe_resize_packed(count); + count ? maybe_resize_packed(count) : release_memory(); } /** @@ -542,12 +545,13 @@ public: */ template void insert(It first, It last, const value_type &value = {}) { - if(const auto length = underlying_type::insert(first, last); length) { - const auto sz = count + length; + if(const auto sz = count + std::distance(first, last); sz != count) { maybe_resize_packed(sz); + underlying_type::reserve(sz); - while(count != sz) { + for(; first != last; ++first) { push_back(value); + underlying_type::emplace(*first); } } } @@ -566,12 +570,13 @@ public: */ template::value_type>, value_type>>> void insert(EIt first, EIt last, CIt from) { - if(const auto length = underlying_type::insert(first, last); length) { - const auto sz = count + length; + if(const auto sz = count + std::distance(first, last); sz != count) { maybe_resize_packed(sz); + underlying_type::reserve(sz); - while(count != sz) { - push_back(*(from++)); + for(; first != last; ++first, ++from) { + push_back(*from); + underlying_type::emplace(*first); } } } diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index f9a0b2ada..1243a1872 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -134,10 +134,8 @@ TEST(SparseSet, Insert) { entities[1] = entt::entity{42}; set.emplace(entt::entity{12}); - - ASSERT_EQ(set.insert(std::end(entities), std::end(entities)), 0u); - ASSERT_EQ(set.insert(std::begin(entities), std::end(entities)), 2u); - + set.insert(std::end(entities), std::end(entities)); + set.insert(std::begin(entities), std::end(entities)); set.emplace(entt::entity{24}); ASSERT_TRUE(set.contains(entities[0]));