Просмотр исходного кода

sparse_set: improved shrink_to_fit to also cleanup the sparse array

Michele Caini 1 год назад
Родитель
Сommit
476822e6c4
3 измененных файлов с 35 добавлено и 2 удалено
  1. 0 1
      TODO
  2. 26 0
      src/entt/entity/sparse_set.hpp
  3. 9 1
      test/entt/entity/sparse_set.cpp

+ 0 - 1
TODO

@@ -35,7 +35,6 @@ TODO:
 * don't pass reactive storage by default to callback
 * runtime types support for meta for types that aren't backed by C++ types
 * built-in no-pagination storage - no_pagination page size as limits::max
-* sparse_set shrink_to_fit argument for sparse array shrink policy (none, empty, deep, whatever)
 * any cdynamic to support const ownership construction
 * return meta context from meta objects
 * allow passing arguments to meta setter/getter (we can fallback on meta invoke for everything probably)

+ 26 - 0
src/entt/entity/sparse_set.hpp

@@ -556,6 +556,32 @@ public:
 
     /*! @brief Requests the removal of unused capacity. */
     virtual void shrink_to_fit() {
+        sparse_container_type other{sparse.get_allocator()};
+        const auto len = sparse.size();
+        size_type cnt{};
+
+        other.reserve(len);
+
+        for(auto &&elem: std::as_const(packed)) {
+            // also skip tombstones, if any
+            if(const auto page = pos_to_page(entity_to_pos(elem)); page < len && sparse[page] != nullptr) {
+                if(const auto sz = page + 1u; sz > other.size()) {
+                    other.resize(sz, nullptr);
+                }
+
+                other[page] = std::exchange(sparse[page], nullptr);
+
+                if(++cnt == len) {
+                    // early exit due to lack of pages
+                    break;
+                }
+            }
+        }
+
+        release_sparse_pages();
+        sparse.swap(other);
+
+        sparse.shrink_to_fit();
         packed.shrink_to_fit();
     }
 

+ 9 - 1
test/entt/entity/sparse_set.cpp

@@ -322,7 +322,15 @@ TYPED_TEST(SparseSet, Pagination) {
 
         set.shrink_to_fit();
 
-        ASSERT_EQ(set.extent(), 2 * traits_type::page_size);
+        switch(policy) {
+        case entt::deletion_policy::swap_and_pop:
+        case entt::deletion_policy::in_place: {
+            ASSERT_EQ(set.extent(), 0u);
+        } break;
+        case entt::deletion_policy::swap_only: {
+            ASSERT_EQ(set.extent(), 2 * traits_type::page_size);
+        } break;
+        }
     }
 }