EnTT 4.0.0
Loading...
Searching...
No Matches
sparse_set.hpp
1#ifndef ENTT_ENTITY_SPARSE_SET_HPP
2#define ENTT_ENTITY_SPARSE_SET_HPP
3
4#include <compare>
5#include "../config/config.h"
6#include "../core/algorithm.hpp"
7#include "../core/any.hpp"
8#include "../core/bit.hpp"
9#include "../core/type_info.hpp"
10#include "../stl/concepts.hpp"
11#include "../stl/cstddef.hpp"
12#include "../stl/iterator.hpp"
13#include "../stl/memory.hpp"
14#include "../stl/type_traits.hpp"
15#include "../stl/utility.hpp"
16#include "../stl/vector.hpp"
17#include "entity.hpp"
18#include "fwd.hpp"
19
20namespace entt {
21
23namespace internal {
24
25template<typename Container>
26struct sparse_set_iterator final {
27 using value_type = Container::value_type;
28 using pointer = Container::const_pointer;
29 using reference = Container::const_reference;
30 using difference_type = Container::difference_type;
31 using iterator_category = stl::random_access_iterator_tag;
32
33 constexpr sparse_set_iterator() noexcept
34 : packed{},
35 offset{} {}
36
37 constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
38 : packed{&ref},
39 offset{idx} {}
40
41 constexpr sparse_set_iterator &operator++() noexcept {
42 return --offset, *this;
43 }
44
45 constexpr sparse_set_iterator operator++(int) noexcept {
46 const sparse_set_iterator orig = *this;
47 return ++(*this), orig;
48 }
49
50 constexpr sparse_set_iterator &operator--() noexcept {
51 return ++offset, *this;
52 }
53
54 constexpr sparse_set_iterator operator--(int) noexcept {
55 const sparse_set_iterator orig = *this;
56 return operator--(), orig;
57 }
58
59 constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept {
60 offset -= value;
61 return *this;
62 }
63
64 constexpr sparse_set_iterator operator+(const difference_type value) const noexcept {
65 sparse_set_iterator copy = *this;
66 return (copy += value);
67 }
68
69 constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept {
70 return (*this += -value);
71 }
72
73 constexpr sparse_set_iterator operator-(const difference_type value) const noexcept {
74 return (*this + -value);
75 }
76
77 [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
78 return (*packed)[static_cast<Container::size_type>(index() - value)];
79 }
80
81 [[nodiscard]] constexpr pointer operator->() const noexcept {
82 return stl::addressof(operator[](0));
83 }
84
85 [[nodiscard]] constexpr reference operator*() const noexcept {
86 return operator[](0);
87 }
88
89 [[nodiscard]] constexpr stl::ptrdiff_t operator-(const sparse_set_iterator &other) const noexcept {
90 // intentionally reversed due to backward iteration
91 return other.offset - offset;
92 }
93
94 [[nodiscard]] constexpr bool operator==(const sparse_set_iterator &other) const noexcept {
95 return offset == other.offset;
96 }
97
98 [[nodiscard]] constexpr auto operator<=>(const sparse_set_iterator &other) const noexcept {
99 // intentionally reversed due to backward iteration
100 return other.offset <=> offset;
101 }
102
103 [[nodiscard]] constexpr pointer data() const noexcept {
104 return packed ? packed->data() : nullptr;
105 }
106
107 [[nodiscard]] constexpr difference_type index() const noexcept {
108 return offset - 1;
109 }
110
111private:
112 const Container *packed;
113 difference_type offset;
114};
115
116} // namespace internal
118
138template<typename Entity, typename Allocator>
140 using alloc_traits = stl::allocator_traits<Allocator>;
141 static_assert(stl::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
142 using sparse_container_type = stl::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
143 using packed_container_type = stl::vector<Entity, Allocator>;
144 using traits_type = entt_traits<Entity>;
145
146 static constexpr auto max_size = static_cast<stl::size_t>(traits_type::to_entity(null));
147
148 // it could be auto but gcc complains and emits a warning due to a false positive
149 [[nodiscard]] stl::size_t policy_to_head() const noexcept {
150 return static_cast<size_type>(max_size * static_cast<stl::remove_const_t<decltype(max_size)>>(mode != deletion_policy::swap_only));
151 }
152
153 [[nodiscard]] auto entity_to_pos(const Entity entt) const noexcept {
154 return static_cast<size_type>(traits_type::to_entity(entt));
155 }
156
157 [[nodiscard]] auto pos_to_page(const stl::size_t pos) const noexcept {
158 return static_cast<size_type>(pos / traits_type::page_size);
159 }
160
161 [[nodiscard]] auto sparse_ptr(const Entity entt) const {
162 const auto pos = entity_to_pos(entt);
163 const auto page = pos_to_page(pos);
164 return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
165 }
166
167 [[nodiscard]] auto &sparse_ref(const Entity entt) const {
168 ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
169 const auto pos = entity_to_pos(entt);
170 return sparse[pos_to_page(pos)][fast_mod(pos, traits_type::page_size)];
171 }
172
173 [[nodiscard]] auto to_iterator(const Entity entt) const {
174 return --(end() - static_cast<difference_type>(index(entt)));
175 }
176
177 [[nodiscard]] auto &assure_at_least(const Entity entt) {
178 const auto pos = entity_to_pos(entt);
179 const auto page = pos_to_page(pos);
180
181 if(!(page < sparse.size())) {
182 sparse.resize(page + 1u, nullptr);
183 }
184
185 if(!sparse[page]) {
186 constexpr entity_type init = null;
187 auto page_allocator{packed.get_allocator()};
188 sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
189 stl::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
190 }
191
192 return sparse[page][fast_mod(pos, traits_type::page_size)];
193 }
194
195 void release_sparse_pages() {
196 for(auto page_allocator{packed.get_allocator()}; auto &&page: sparse) {
197 if(page != nullptr) {
198 stl::destroy(page, page + traits_type::page_size);
199 alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
200 page = nullptr;
201 }
202 }
203 }
204
205 void swap_at(const stl::size_t lhs, const stl::size_t rhs) {
206 auto &from = packed[lhs];
207 auto &to = packed[rhs];
208
209 sparse_ref(from) = traits_type::combine(static_cast<traits_type::entity_type>(rhs), traits_type::to_integral(from));
210 sparse_ref(to) = traits_type::combine(static_cast<traits_type::entity_type>(lhs), traits_type::to_integral(to));
211
212 stl::swap(from, to);
213 }
214
215private:
216 [[nodiscard]] virtual const void *get_at(const stl::size_t) const {
217 return nullptr;
218 }
219
220 virtual void swap_or_move([[maybe_unused]] const stl::size_t lhs, [[maybe_unused]] const stl::size_t rhs) {
221 ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
222 }
223
224protected:
226 using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
227
232 void swap_only(const Entity entt) {
233 ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
234 const auto pos = index(entt);
236 swap_at(pos, head -= (pos < head));
237 }
238
243 void swap_and_pop(const Entity entt) {
244 ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
245 auto &self = sparse_ref(entt);
246 const auto pos = traits_type::to_entity(self);
247 sparse_ref(packed.back()) = traits_type::combine(pos, traits_type::to_integral(packed.back()));
248 packed[static_cast<size_type>(pos)] = packed.back();
249 // unnecessary but it helps to detect nasty bugs
250 // NOLINTNEXTLINE(bugprone-assert-side-effect)
251 ENTT_ASSERT((packed.back() = null, true), "");
252 // lazy self-assignment guard
253 self = null;
254 packed.pop_back();
255 }
256
261 void in_place_pop(const Entity entt) {
262 ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
263 const auto pos = entity_to_pos(stl::exchange(sparse_ref(entt), null));
264 packed[pos] = traits_type::combine(static_cast<traits_type::entity_type>(stl::exchange(head, pos)), tombstone);
265 }
266
272 virtual void pop(basic_iterator first, basic_iterator last) {
273 switch(mode) {
275 for(; first != last; ++first) {
276 swap_and_pop(*first);
277 }
278 break;
280 for(; first != last; ++first) {
281 in_place_pop(*first);
282 }
283 break;
285 for(; first != last; ++first) {
286 swap_only(*first);
287 }
288 break;
289 }
290 }
291
293 virtual void pop_all() {
294 if(!packed.empty()) {
295 // suboptimal with few entities, but exploits cache way more with many
296 for(auto &&elem: sparse) {
297 if(elem) {
298 for(size_type pos{}; pos < traits_type::page_size; ++pos) {
299 elem[pos] = null;
300 }
301 }
302 }
303 }
304
305 head = policy_to_head();
306 packed.clear();
307 }
308
315 virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
316 ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
317 auto &elem = assure_at_least(entt);
318 auto pos = size();
319
320 switch(mode) {
322 if(head != max_size && !force_back) {
323 pos = head;
324 ENTT_ASSERT(elem == null, "Slot not available");
326 head = entity_to_pos(stl::exchange(packed[pos], entt));
327 break;
328 }
329 [[fallthrough]];
331 packed.push_back(entt);
332 ENTT_ASSERT(elem == null, "Slot not available");
333 elem = traits_type::combine(static_cast<traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
334 break;
336 if(elem == null) {
337 packed.push_back(entt);
338 elem = traits_type::combine(static_cast<traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
339 } else {
340 ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available");
341 bump(entt);
342 }
343
344 pos = head++;
345 swap_at(entity_to_pos(elem), pos);
346 break;
347 }
348
349 return iterator{packed, static_cast<difference_type>(++pos)};
350 }
351
353 // NOLINTNEXTLINE(performance-unnecessary-value-param)
354 virtual void bind_any(any) noexcept {}
355
356public:
358 using allocator_type = Allocator;
364 using size_type = stl::size_t;
366 using difference_type = stl::ptrdiff_t;
368 using pointer = packed_container_type::const_pointer;
374 using reverse_iterator = stl::reverse_iterator<iterator>;
376 using const_reverse_iterator = stl::reverse_iterator<const_iterator>;
377
381
386 explicit basic_sparse_set(const allocator_type &allocator)
388
394 explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
395 : basic_sparse_set{type_id<void>(), pol, allocator} {}
396
405 : sparse{allocator},
406 packed{allocator},
407 descriptor{&elem},
408 mode{pol},
409 head{policy_to_head()} {
410 ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
411 }
412
415
421 : sparse{stl::move(other.sparse)},
422 packed{stl::move(other.packed)},
423 descriptor{other.descriptor},
424 mode{other.mode},
425 head{stl::exchange(other.head, policy_to_head())} {}
426
433 : sparse{stl::move(other.sparse), allocator},
434 packed{stl::move(other.packed), allocator},
435 descriptor{other.descriptor},
436 mode{other.mode},
437 head{stl::exchange(other.head, policy_to_head())} {
438 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
439 }
440
443 release_sparse_pages();
444 }
445
451
458 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
459 swap(other);
460 return *this;
461 }
462
467 void swap(basic_sparse_set &other) noexcept {
468 using stl::swap;
469 swap(sparse, other.sparse);
470 swap(packed, other.packed);
471 swap(descriptor, other.descriptor);
472 swap(mode, other.mode);
473 swap(head, other.head);
474 }
475
480 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
481 return packed.get_allocator();
482 }
483
488 [[nodiscard]] deletion_policy policy() const noexcept {
489 return mode;
490 }
491
496 [[nodiscard]] size_type free_list() const noexcept {
497 return head;
498 }
499
504 void free_list(const size_type value) noexcept {
505 ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
506 head = value;
507 }
508
517 virtual void reserve(const size_type cap) {
518 packed.reserve(cap);
519 }
520
526 [[nodiscard]] virtual size_type capacity() const noexcept {
527 return packed.capacity();
528 }
529
531 virtual void shrink_to_fit() {
532 sparse_container_type other{sparse.get_allocator()};
533 const auto len = sparse.size();
534 other.reserve(len);
535
536 for(size_type cnt{}; auto &&elem: stl::as_const(packed)) {
537 if(elem != tombstone) {
538 if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) {
539 if(const auto sz = page + 1u; sz > other.size()) {
540 other.resize(sz, nullptr);
541 }
542
543 other[page] = stl::exchange(sparse[page], nullptr);
544
545 if(++cnt == len) {
546 // early exit due to lack of pages
547 break;
548 }
549 }
550 }
551 }
552
553 release_sparse_pages();
554 sparse.swap(other);
555
556 sparse.shrink_to_fit();
557 packed.shrink_to_fit();
558 }
559
569 [[nodiscard]] size_type extent() const noexcept {
570 return sparse.size() * traits_type::page_size;
571 }
572
583 [[nodiscard]] size_type size() const noexcept {
584 return packed.size();
585 }
586
591 [[nodiscard]] bool empty() const noexcept {
592 return packed.empty();
593 }
594
599 [[nodiscard]] bool contiguous() const noexcept {
600 return (mode != deletion_policy::in_place) || (head == max_size);
601 }
602
607 [[nodiscard]] pointer data() const noexcept {
608 return packed.data();
609 }
610
619 [[nodiscard]] iterator begin() const noexcept {
620 const auto pos = static_cast<difference_type>(packed.size());
621 return iterator{packed, pos};
622 }
623
625 [[nodiscard]] const_iterator cbegin() const noexcept {
626 return begin();
627 }
628
634 [[nodiscard]] iterator end() const noexcept {
635 return iterator{packed, {}};
636 }
637
639 [[nodiscard]] const_iterator cend() const noexcept {
640 return end();
641 }
642
652 [[nodiscard]] reverse_iterator rbegin() const noexcept {
653 return stl::make_reverse_iterator(end());
654 }
655
657 [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
658 return rbegin();
659 }
660
666 [[nodiscard]] reverse_iterator rend() const noexcept {
667 return stl::make_reverse_iterator(begin());
668 }
669
671 [[nodiscard]] const_reverse_iterator crend() const noexcept {
672 return rend();
673 }
674
681 [[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
682 return contains(entt) ? to_iterator(entt) : end();
683 }
684
690 [[nodiscard]] bool contains(const entity_type entt) const noexcept {
691 const auto *elem = sparse_ptr(entt);
692 constexpr auto cap = traits_type::entity_mask;
693 constexpr auto mask = traits_type::to_integral(null) & ~cap;
694 // testing versions permits to avoid accessing the packed array
695 return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
696 }
697
704 [[nodiscard]] version_type current(const entity_type entt) const noexcept {
705 const auto *elem = sparse_ptr(entt);
706 constexpr auto fallback = traits_type::to_version(tombstone);
707 return elem ? traits_type::to_version(*elem) : fallback;
708 }
709
720 [[nodiscard]] size_type index(const entity_type entt) const noexcept {
721 ENTT_ASSERT(contains(entt), "Set does not contain entity");
722 return entity_to_pos(sparse_ref(entt));
723 }
724
730 [[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
731 ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
732 return packed[pos];
733 }
734
745 [[nodiscard]] const void *value(const entity_type entt) const noexcept {
746 return get_at(index(entt));
747 }
748
750 [[nodiscard]] void *value(const entity_type entt) noexcept {
751 return const_cast<void *>(stl::as_const(*this).value(entt));
752 }
753
766 iterator push(const entity_type entt, const void *elem = nullptr) {
767 return try_emplace(entt, false, elem);
768 }
769
782 iterator push(stl::input_iterator auto first, stl::input_iterator auto last) {
783 auto curr = end();
784
785 for(; first != last; ++first) {
786 curr = try_emplace(*first, true);
787 }
788
789 return curr;
790 }
791
803 auto &elem = sparse_ref(entt);
804 ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
806 packed[entity_to_pos(elem)] = entt;
808 }
809
819 void erase(const entity_type entt) {
820 const auto it = to_iterator(entt);
821 pop(it, it + 1u);
822 }
823
833 template<stl::input_iterator It>
834 void erase(It first, It last) {
835 if constexpr(stl::is_same_v<It, basic_iterator>) {
836 pop(first, last);
837 } else {
838 for(; first != last; ++first) {
839 erase(*first);
840 }
841 }
842 }
843
849 bool remove(const entity_type entt) {
850 return contains(entt) && (erase(entt), true);
851 }
852
860 template<stl::input_iterator It>
861 size_type remove(It first, It last) {
862 size_type count{};
863
864 if constexpr(stl::is_same_v<It, basic_iterator>) {
865 while(first != last) {
866 while(first != last && !contains(*first)) {
867 ++first;
868 }
869
870 const auto it = first;
871
872 while(first != last && contains(*first)) {
873 ++first;
874 }
875
876 count += static_cast<size_type>(stl::distance(it, first));
877 erase(it, first);
878 }
879 } else {
880 for(; first != last; ++first) {
881 count += remove(*first);
882 }
883 }
884
885 return count;
886 }
887
889 void compact() {
890 if(mode == deletion_policy::in_place) {
891 size_type from = packed.size();
892 size_type pos = stl::exchange(head, max_size);
893
894 for(; from && packed[from - 1u] == tombstone; --from) {}
895
896 while(pos != max_size) {
897 if(const auto to = stl::exchange(pos, entity_to_pos(packed[pos])); to < from) {
898 --from;
899 swap_or_move(from, to);
900
901 packed[to] = packed[from];
902 const auto elem = static_cast<traits_type::entity_type>(to);
903 sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
904
905 for(; from && packed[from - 1u] == tombstone; --from) {}
906 }
907 }
908
909 packed.erase(packed.begin() + static_cast<difference_type>(from), packed.end());
910 }
911 }
912
926 void swap_elements(const entity_type lhs, const entity_type rhs) {
927 const auto from = index(lhs);
928 const auto to = index(rhs);
929
930 // basic no-leak guarantee if swapping throws
931 swap_or_move(from, to);
932 swap_at(from, to);
933 }
934
965 template<typename Compare, typename Sort = std_sort, typename... Args>
966 void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
967 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
968 ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
969
970 algo(packed.rend() - static_cast<difference_type>(length), packed.rend(), stl::move(compare), stl::forward<Args>(args)...);
971
972 for(size_type pos{}; pos < length; ++pos) {
973 auto curr = pos;
974 auto next = index(packed[curr]);
975
976 while(curr != next) {
977 const auto idx = index(packed[next]);
978 const auto entt = packed[curr];
979
980 swap_or_move(next, idx);
981 const auto elem = static_cast<traits_type::entity_type>(curr);
982 sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
983 curr = stl::exchange(next, idx);
984 }
985 }
986 }
987
1000 template<typename Compare, typename Sort = std_sort, typename... Args>
1001 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1002 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
1003 sort_n(len, stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
1004 }
1005
1019 template<stl::input_iterator It>
1020 iterator sort_as(It first, It last) {
1021 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
1022 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
1023 auto it = end() - static_cast<difference_type>(len);
1024
1025 for(const auto other = end(); (it != other) && (first != last); ++first) {
1026 if(const auto curr = *first; contains(curr)) {
1027 if(const auto entt = *it; entt != curr) {
1028 // basic no-leak guarantee (with invalid state) if swapping throws
1029 swap_elements(entt, curr);
1030 }
1031
1032 ++it;
1033 }
1034 }
1035
1036 return it;
1037 }
1038
1040 void clear() {
1041 pop_all();
1042 // sanity check to avoid subtle issues due to storage classes
1043 ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
1044 head = policy_to_head();
1045 packed.clear();
1046 }
1047
1052 [[nodiscard]] const type_info &info() const noexcept {
1053 return *descriptor;
1054 }
1055
1061 template<typename Type>
1062 void bind(Type &&value) noexcept {
1063 bind_any(forward_as_any(stl::forward<Type>(value)));
1064 }
1065
1066private:
1067 sparse_container_type sparse;
1068 packed_container_type packed;
1069 const type_info *descriptor;
1070 deletion_policy mode;
1071 size_type head;
1072};
1073
1074} // namespace entt
1075
1076#endif
static constexpr value_type next(const value_type value) noexcept
Definition entity.hpp:130
static constexpr entity_type to_integral(const value_type value) noexcept
Definition entity.hpp:97
internal::entt_traits< Type >::entity_type entity_type
Definition entity.hpp:83
static constexpr entity_type to_entity(const value_type value) noexcept
Definition entity.hpp:106
internal::entt_traits< Type >::value_type value_type
Definition entity.hpp:81
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
Definition entity.hpp:163
internal::entt_traits< Type >::version_type version_type
Definition entity.hpp:85
static constexpr version_type to_version(const value_type value) noexcept
Definition entity.hpp:116
Sparse set implementation.
void erase(const entity_type entt)
Erases an entity from a sparse set.
iterator begin() const noexcept
Returns an iterator to the beginning.
virtual size_type capacity() const noexcept
Returns the number of elements that a sparse set has currently allocated space for.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
basic_sparse_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void *=nullptr)
Assigns an entity to a sparse set.
virtual void pop_all()
Erases all entities of a sparse set.
void erase(It first, It last)
Erases entities from a set.
traits_type::value_type entity_type
Underlying entity identifier.
packed_container_type::const_pointer pointer
void swap_elements(const entity_type lhs, const entity_type rhs)
Swaps two entities in a sparse set.
iterator end() const noexcept
Returns an iterator to the end.
void * value(const entity_type entt) noexcept
Returns the element assigned to an entity, if any.
reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
void sort_n(const size_type length, Compare compare, Sort algo=Sort{}, Args &&...args)
Sort the first count elements according to the given comparison function.
pointer data() const noexcept
Direct access to the internal packed array.
basic_sparse_set & operator=(basic_sparse_set &&other) noexcept
Move assignment operator.
virtual void reserve(const size_type cap)
Increases the capacity of a sparse set.
void clear()
Clears a sparse set.
void bind(Type &&value) noexcept
Forwards variables to derived classes, if any.
size_type size() const noexcept
Returns the number of elements in a sparse set.
stl::reverse_iterator< const_iterator > const_reverse_iterator
basic_sparse_set & operator=(const basic_sparse_set &)=delete
Default copy assignment operator, deleted on purpose.
void swap_only(const Entity entt)
Erases an entity from a sparse set.
bool contiguous() const noexcept
Checks whether a sparse set is fully packed.
version_type bump(const entity_type entt)
Bump the version number of an entity.
virtual void shrink_to_fit()
Requests the removal of unused capacity.
size_type extent() const noexcept
Returns the extent of a sparse set.
void swap(basic_sparse_set &other) noexcept
Exchanges the contents with those of a given sparse set.
const_iterator find(const entity_type entt) const noexcept
Finds an entity.
iterator push(const entity_type entt, const void *elem=nullptr)
Assigns an entity to a sparse set.
void in_place_pop(const Entity entt)
Erases an entity from a sparse set.
basic_sparse_set(deletion_policy pol, const allocator_type &allocator={})
Constructs an empty container with the given policy and allocator.
virtual void pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
deletion_policy policy() const noexcept
Returns the deletion policy of a sparse set.
iterator push(stl::input_iterator auto first, stl::input_iterator auto last)
Assigns one or more entities to a sparse set.
virtual void bind_any(any) noexcept
Forwards variables to derived classes, if any.
version_type current(const entity_type entt) const noexcept
Returns the contained version for an identifier.
bool contains(const entity_type entt) const noexcept
Checks if a sparse set contains an entity.
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
Allocator-extended move constructor.
stl::ptrdiff_t difference_type
Signed integer type.
iterator sort_as(It first, It last)
Sort entities according to their order in a range.
const_iterator cend() const noexcept
Returns an iterator to the end.
stl::reverse_iterator< iterator > reverse_iterator
basic_sparse_set(basic_sparse_set &&other) noexcept
Move constructor.
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the end.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
size_type remove(It first, It last)
Removes entities from a sparse set if they exist.
const void * value(const entity_type entt) const noexcept
reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
entity_type operator[](const size_type pos) const noexcept
Returns the entity at specified location.
size_type free_list() const noexcept
Returns data on the free list whose meaning depends on the mode.
void swap_and_pop(const Entity entt)
Erases an entity from a sparse set.
stl::size_t size_type
Unsigned integer type.
internal::sparse_set_iterator< packed_container_type > basic_iterator
virtual ~basic_sparse_set()
Default destructor.
void free_list(const size_type value) noexcept
Sets data on the free list whose meaning depends on the mode.
basic_sparse_set(const basic_sparse_set &)=delete
Default copy constructor, deleted on purpose.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
bool empty() const noexcept
Checks whether a sparse set is empty.
size_type index(const entity_type entt) const noexcept
Returns the position of an entity in a sparse set.
const type_info & info() const noexcept
Returns a type info object for the value type, if any.
traits_type::version_type version_type
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort all elements according to the given comparison function.
bool remove(const entity_type entt)
Removes an entity from a sparse set if it exists.
void compact()
Removes all tombstones from a sparse set.
basic_sparse_set(const type_info &elem, deletion_policy pol=deletion_policy::swap_and_pop, const allocator_type &allocator={})
Constructs an empty container with the given value type, policy and allocator.
basic_sparse_set()
Default constructor.
Custom EnTT namespace for the standard template library.
Definition entt.hpp:5
EnTT default namespace.
Definition dense_map.hpp:25
deletion_policy
Storage deletion policy.
Definition fwd.hpp:18
@ swap_only
Swap-only deletion policy.
Definition fwd.hpp:24
@ swap_and_pop
Swap-and-pop deletion policy.
Definition fwd.hpp:20
@ in_place
In-place deletion policy.
Definition fwd.hpp:22
constexpr null_t null
Compile-time constant for null entities.
Definition entity.hpp:299
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:308
basic_any<> any
Alias declaration for the most common use case.
Definition fwd.hpp:32
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
@ ref
Aliasing mode, non-const reference.
Definition fwd.hpp:19
const type_info & type_id() noexcept
Returns the type info object associated to a given type.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:617
constexpr Type fast_mod(const Type value, const stl::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition bit.hpp:19
Entity traits.
Definition entity.hpp:177
static constexpr stl::size_t page_size
Definition entity.hpp:181
Function object to wrap stl::sort in a class type.
Definition algorithm.hpp:22
Implementation specific information about a type.