EnTT: src/entt/entity/sparse_set.hpp Source File

EnTT 3.16.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 <cstddef>
5#include <iterator>
6#include <memory>
7#include <type_traits>
8#include <utility>
9#include <vector>
10#include "../config/config.h"
11#include "../core/algorithm.hpp"
12#include "../core/any.hpp"
13#include "../core/bit.hpp"
14#include "../core/type_info.hpp"
15#include "entity.hpp"
16#include "fwd.hpp"
17
18namespace entt {
19
21namespace internal {
22
23template<typename Container>
24struct sparse_set_iterator final {
25 using value_type = typename Container::value_type;
26 using pointer = typename Container::const_pointer;
27 using reference = typename Container::const_reference;
28 using difference_type = typename Container::difference_type;
29 using iterator_category = std::random_access_iterator_tag;
30
31 constexpr sparse_set_iterator() noexcept
32 : packed{},
33 offset{} {}
34
35 constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
36 : packed{&ref},
37 offset{idx} {}
38
39 constexpr sparse_set_iterator &operator++() noexcept {
40 return --offset, *this;
41 }
42
43 constexpr sparse_set_iterator operator++(int) noexcept {
44 const sparse_set_iterator orig = *this;
45 return ++(*this), orig;
46 }
47
48 constexpr sparse_set_iterator &operator--() noexcept {
49 return ++offset, *this;
50 }
51
52 constexpr sparse_set_iterator operator--(int) noexcept {
53 const sparse_set_iterator orig = *this;
54 return operator--(), orig;
55 }
56
57 constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept {
58 offset -= value;
59 return *this;
60 }
61
62 constexpr sparse_set_iterator operator+(const difference_type value) const noexcept {
63 sparse_set_iterator copy = *this;
64 return (copy += value);
65 }
66
67 constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept {
68 return (*this += -value);
69 }
70
71 constexpr sparse_set_iterator operator-(const difference_type value) const noexcept {
72 return (*this + -value);
73 }
74
75 [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
76 return (*packed)[static_cast<typename Container::size_type>(index() - value)];
77 }
78
79 [[nodiscard]] constexpr pointer operator->() const noexcept {
80 return std::addressof(operator[](0));
81 }
82
83 [[nodiscard]] constexpr reference operator*() const noexcept {
84 return operator[](0);
85 }
86
87 [[nodiscard]] constexpr pointer data() const noexcept {
88 return packed ? packed->data() : nullptr;
89 }
90
91 [[nodiscard]] constexpr difference_type index() const noexcept {
92 return offset - 1;
93 }
94
95private:
96 const Container *packed;
97 difference_type offset;
98};
99
100template<typename Container>
101[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
102 return rhs.index() - lhs.index();
103}
104
105template<typename Container>
106[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
107 return lhs.index() == rhs.index();
108}
109
110template<typename Container>
111[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
112 return !(lhs == rhs);
113}
114
115template<typename Container>
116[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
117 return lhs.index() > rhs.index();
118}
119
120template<typename Container>
121[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
122 return rhs < lhs;
123}
124
125template<typename Container>
126[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
127 return !(lhs > rhs);
128}
129
130template<typename Container>
131[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
132 return !(lhs < rhs);
133}
134
135} // namespace internal
137
157template<typename Entity, typename Allocator>
158 class basic_sparse_set {
159 using alloc_traits = std::allocator_traits<Allocator>;
160 static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
161 using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
162 using packed_container_type = std::vector<Entity, Allocator>;
163 using traits_type = entt_traits<Entity>;
164
165 static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
166
167 // it could be auto but gcc complains and emits a warning due to a false positive
168 [[nodiscard]] std::size_t policy_to_head() const noexcept {
169 return static_cast<size_type>(max_size * static_cast<std::remove_const_t<decltype(max_size)>>(mode != deletion_policy::swap_only));
170 }
171
172 [[nodiscard]] auto entity_to_pos(const Entity entt) const noexcept {
173 return static_cast<size_type>(traits_type::to_entity(entt));
174 }
175
176 [[nodiscard]] auto pos_to_page(const std::size_t pos) const noexcept {
177 return static_cast<size_type>(pos / traits_type::page_size);
178 }
179
180 [[nodiscard]] auto sparse_ptr(const Entity entt) const {
181 const auto pos = entity_to_pos(entt);
182 const auto page = pos_to_page(pos);
183 return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
184 }
185
186 [[nodiscard]] auto &sparse_ref(const Entity entt) const {
187 ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
188 const auto pos = entity_to_pos(entt);
189 return sparse[pos_to_page(pos)][fast_mod(pos, traits_type::page_size)];
190 }
191
192 [[nodiscard]] auto to_iterator(const Entity entt) const {
193 return --(end() - static_cast<difference_type>(index(entt)));
194 }
195
196 [[nodiscard]] auto &assure_at_least(const Entity entt) {
197 const auto pos = entity_to_pos(entt);
198 const auto page = pos_to_page(pos);
199
200 if(!(page < sparse.size())) {
201 sparse.resize(page + 1u, nullptr);
202 }
203
204 if(!sparse[page]) {
205 constexpr entity_type init = null;
206 auto page_allocator{packed.get_allocator()};
207 sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
208 std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
209 }
210
211 return sparse[page][fast_mod(pos, traits_type::page_size)];
212 }
213
214 void release_sparse_pages() {
215 auto page_allocator{packed.get_allocator()};
216
217 for(auto &&page: sparse) {
218 if(page != nullptr) {
219 std::destroy(page, page + traits_type::page_size);
220 alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
221 page = nullptr;
222 }
223 }
224 }
225
226 void swap_at(const std::size_t lhs, const std::size_t rhs) {
227 auto &from = packed[lhs];
228 auto &to = packed[rhs];
229
230 sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
231 sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
232
233 std::swap(from, to);
234 }
235
236private:
237 [[nodiscard]] virtual const void *get_at(const std::size_t) const {
238 return nullptr;
239 }
240
241 virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
242 ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
243 }
244
245protected:
247 using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
248
253 void swap_only(const basic_iterator it) {
254 ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
255 const auto pos = index(*it);
256 bump(traits_type::next(*it));
257 swap_at(pos, head -= (pos < head));
258 }
259
264 void swap_and_pop(const basic_iterator it) {
265 ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
266 auto &self = sparse_ref(*it);
267 const auto entt = traits_type::to_entity(self);
268 sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
269 packed[static_cast<size_type>(entt)] = packed.back();
270 // unnecessary but it helps to detect nasty bugs
271 // NOLINTNEXTLINE(bugprone-assert-side-effect)
272 ENTT_ASSERT((packed.back() = null, true), "");
273 // lazy self-assignment guard
274 self = null;
275 packed.pop_back();
276 }
277
282 void in_place_pop(const basic_iterator it) {
283 ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
284 const auto pos = entity_to_pos(std::exchange(sparse_ref(*it), null));
285 packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
286 }
287
293 virtual void pop(basic_iterator first, basic_iterator last) {
294 switch(mode) {
295 case deletion_policy::swap_and_pop:
296 for(; first != last; ++first) {
297 swap_and_pop(first);
298 }
299 break;
300 case deletion_policy::in_place:
301 for(; first != last; ++first) {
302 in_place_pop(first);
303 }
304 break;
305 case deletion_policy::swap_only:
306 for(; first != last; ++first) {
307 swap_only(first);
308 }
309 break;
310 }
311 }
312
314 virtual void pop_all() {
315 switch(mode) {
316 case deletion_policy::in_place:
317 if(head != max_size) {
318 for(auto &&elem: packed) {
319 if(elem != tombstone) {
320 sparse_ref(elem) = null;
321 }
322 }
323 break;
324 }
325 [[fallthrough]];
326 case deletion_policy::swap_only:
327 case deletion_policy::swap_and_pop:
328 for(auto &&elem: packed) {
329 sparse_ref(elem) = null;
330 }
331 break;
332 }
333
334 head = policy_to_head();
335 packed.clear();
336 }
337
344 virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
345 ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
346 auto &elem = assure_at_least(entt);
347 auto pos = size();
348
349 switch(mode) {
350 case deletion_policy::in_place:
351 if(head != max_size && !force_back) {
352 pos = head;
353 ENTT_ASSERT(elem == null, "Slot not available");
354 elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
355 head = entity_to_pos(std::exchange(packed[pos], entt));
356 break;
357 }
358 [[fallthrough]];
359 case deletion_policy::swap_and_pop:
360 packed.push_back(entt);
361 ENTT_ASSERT(elem == null, "Slot not available");
362 elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
363 break;
364 case deletion_policy::swap_only:
365 if(elem == null) {
366 packed.push_back(entt);
367 elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
368 } else {
369 ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available");
370 bump(entt);
371 }
372
373 pos = head++;
374 swap_at(entity_to_pos(elem), pos);
375 break;
376 }
377
378 return iterator{packed, static_cast<difference_type>(++pos)};
379 }
380
382 // NOLINTNEXTLINE(performance-unnecessary-value-param)
383 virtual void bind_any(any) noexcept {}
384
385public:
387 using allocator_type = Allocator;
389 using entity_type = typename traits_type::value_type;
391 using version_type = typename traits_type::version_type;
393 using size_type = std::size_t;
395 using difference_type = std::ptrdiff_t;
397 using pointer = typename packed_container_type::const_pointer;
399 using iterator = basic_iterator;
401 using const_iterator = iterator;
403 using reverse_iterator = std::reverse_iterator<iterator>;
405 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
406
408 basic_sparse_set()
409 : basic_sparse_set{type_id<void>()} {}
410
415 explicit basic_sparse_set(const allocator_type &allocator)
416 : basic_sparse_set{deletion_policy::swap_and_pop, allocator} {}
417
423 explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
424 : basic_sparse_set{type_id<void>(), pol, allocator} {}
425
433 explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
434 : sparse{allocator},
435 packed{allocator},
436 descriptor{&elem},
437 mode{pol},
438 head{policy_to_head()} {
439 ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
440 }
441
443 basic_sparse_set(const basic_sparse_set &) = delete;
444
449 basic_sparse_set(basic_sparse_set &&other) noexcept
450 : sparse{std::move(other.sparse)},
451 packed{std::move(other.packed)},
452 descriptor{other.descriptor},
453 mode{other.mode},
454 head{std::exchange(other.head, policy_to_head())} {}
455
461 basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
462 : sparse{std::move(other.sparse), allocator},
463 packed{std::move(other.packed), allocator},
464 descriptor{other.descriptor},
465 mode{other.mode},
466 head{std::exchange(other.head, policy_to_head())} {
467 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
468 }
469
471 virtual ~basic_sparse_set() {
472 release_sparse_pages();
473 }
474
479 basic_sparse_set &operator=(const basic_sparse_set &) = delete;
480
486 basic_sparse_set &operator=(basic_sparse_set &&other) noexcept {
487 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
488 swap(other);
489 return *this;
490 }
491
496 void swap(basic_sparse_set &other) noexcept {
497 using std::swap;
498 swap(sparse, other.sparse);
499 swap(packed, other.packed);
500 swap(descriptor, other.descriptor);
501 swap(mode, other.mode);
502 swap(head, other.head);
503 }
504
509 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
510 return packed.get_allocator();
511 }
512
517 [[nodiscard]] deletion_policy policy() const noexcept {
518 return mode;
519 }
520
525 [[nodiscard]] size_type free_list() const noexcept {
526 return head;
527 }
528
533 void free_list(const size_type value) noexcept {
534 ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
535 head = value;
536 }
537
546 virtual void reserve(const size_type cap) {
547 packed.reserve(cap);
548 }
549
555 [[nodiscard]] virtual size_type capacity() const noexcept {
556 return packed.capacity();
557 }
558
560 virtual void shrink_to_fit() {
561 sparse_container_type other{sparse.get_allocator()};
562 const auto len = sparse.size();
563 size_type cnt{};
564
565 other.reserve(len);
566
567 for(auto &&elem: std::as_const(packed)) {
568 if(elem != tombstone) {
569 if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) {
570 if(const auto sz = page + 1u; sz > other.size()) {
571 other.resize(sz, nullptr);
572 }
573
574 other[page] = std::exchange(sparse[page], nullptr);
575
576 if(++cnt == len) {
577 // early exit due to lack of pages
578 break;
579 }
580 }
581 }
582 }
583
584 release_sparse_pages();
585 sparse.swap(other);
586
587 sparse.shrink_to_fit();
588 packed.shrink_to_fit();
589 }
590
600 [[nodiscard]] size_type extent() const noexcept {
601 return sparse.size() * traits_type::page_size;
602 }
603
614 [[nodiscard]] size_type size() const noexcept {
615 return packed.size();
616 }
617
622 [[nodiscard]] bool empty() const noexcept {
623 return packed.empty();
624 }
625
630 [[nodiscard]] bool contiguous() const noexcept {
631 return (mode != deletion_policy::in_place) || (head == max_size);
632 }
633
638 [[nodiscard]] pointer data() const noexcept {
639 return packed.data();
640 }
641
650 [[nodiscard]] iterator begin() const noexcept {
651 const auto pos = static_cast<difference_type>(packed.size());
652 return iterator{packed, pos};
653 }
654
656 [[nodiscard]] const_iterator cbegin() const noexcept {
657 return begin();
658 }
659
665 [[nodiscard]] iterator end() const noexcept {
666 return iterator{packed, {}};
667 }
668
670 [[nodiscard]] const_iterator cend() const noexcept {
671 return end();
672 }
673
683 [[nodiscard]] reverse_iterator rbegin() const noexcept {
684 return std::make_reverse_iterator(end());
685 }
686
688 [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
689 return rbegin();
690 }
691
697 [[nodiscard]] reverse_iterator rend() const noexcept {
698 return std::make_reverse_iterator(begin());
699 }
700
702 [[nodiscard]] const_reverse_iterator crend() const noexcept {
703 return rend();
704 }
705
712 [[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
713 return contains(entt) ? to_iterator(entt) : end();
714 }
715
721 [[nodiscard]] bool contains(const entity_type entt) const noexcept {
722 const auto *elem = sparse_ptr(entt);
723 constexpr auto cap = traits_type::entity_mask;
724 constexpr auto mask = traits_type::to_integral(null) & ~cap;
725 // testing versions permits to avoid accessing the packed array
726 return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
727 }
728
735 [[nodiscard]] version_type current(const entity_type entt) const noexcept {
736 const auto *elem = sparse_ptr(entt);
737 constexpr auto fallback = traits_type::to_version(tombstone);
738 return elem ? traits_type::to_version(*elem) : fallback;
739 }
740
751 [[nodiscard]] size_type index(const entity_type entt) const noexcept {
752 ENTT_ASSERT(contains(entt), "Set does not contain entity");
753 return entity_to_pos(sparse_ref(entt));
754 }
755
761 [[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
762 ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
763 return packed[pos];
764 }
765
776 [[nodiscard]] const void *value(const entity_type entt) const noexcept {
777 return get_at(index(entt));
778 }
779
781 [[nodiscard]] void *value(const entity_type entt) noexcept {
782 return const_cast<void *>(std::as_const(*this).value(entt));
783 }
784
797 iterator push(const entity_type entt, const void *elem = nullptr) {
798 return try_emplace(entt, false, elem);
799 }
800
814 template<typename It>
815 iterator push(It first, It last) {
816 auto curr = end();
817
818 for(; first != last; ++first) {
819 curr = try_emplace(*first, true);
820 }
821
822 return curr;
823 }
824
835 version_type bump(const entity_type entt) {
836 auto &elem = sparse_ref(entt);
837 ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
838 elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt));
839 packed[entity_to_pos(elem)] = entt;
840 return traits_type::to_version(entt);
841 }
842
852 void erase(const entity_type entt) {
853 const auto it = to_iterator(entt);
854 pop(it, it + 1u);
855 }
856
866 template<typename It>
867 void erase(It first, It last) {
868 if constexpr(std::is_same_v<It, basic_iterator>) {
869 pop(first, last);
870 } else {
871 for(; first != last; ++first) {
872 erase(*first);
873 }
874 }
875 }
876
882 bool remove(const entity_type entt) {
883 return contains(entt) && (erase(entt), true);
884 }
885
893 template<typename It>
894 size_type remove(It first, It last) {
895 size_type count{};
896
897 if constexpr(std::is_same_v<It, basic_iterator>) {
898 while(first != last) {
899 while(first != last && !contains(*first)) {
900 ++first;
901 }
902
903 const auto it = first;
904
905 while(first != last && contains(*first)) {
906 ++first;
907 }
908
909 count += static_cast<size_type>(std::distance(it, first));
910 erase(it, first);
911 }
912 } else {
913 for(; first != last; ++first) {
914 count += remove(*first);
915 }
916 }
917
918 return count;
919 }
920
922 void compact() {
923 if(mode == deletion_policy::in_place) {
924 size_type from = packed.size();
925 size_type pos = std::exchange(head, max_size);
926
927 for(; from && packed[from - 1u] == tombstone; --from) {}
928
929 while(pos != max_size) {
930 if(const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) {
931 --from;
932 swap_or_move(from, to);
933
934 packed[to] = packed[from];
935 const auto elem = static_cast<typename traits_type::entity_type>(to);
936 sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
937
938 for(; from && packed[from - 1u] == tombstone; --from) {}
939 }
940 }
941
942 packed.erase(packed.begin() + static_cast<difference_type>(from), packed.end());
943 }
944 }
945
959 void swap_elements(const entity_type lhs, const entity_type rhs) {
960 const auto from = index(lhs);
961 const auto to = index(rhs);
962
963 // basic no-leak guarantee if swapping throws
964 swap_or_move(from, to);
965 swap_at(from, to);
966 }
967
998 template<typename Compare, typename Sort = std_sort, typename... Args>
999 void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
1000 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
1001 ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
1002
1003 algo(packed.rend() - static_cast<difference_type>(length), packed.rend(), std::move(compare), std::forward<Args>(args)...);
1004
1005 for(size_type pos{}; pos < length; ++pos) {
1006 auto curr = pos;
1007 auto next = index(packed[curr]);
1008
1009 while(curr != next) {
1010 const auto idx = index(packed[next]);
1011 const auto entt = packed[curr];
1012
1013 swap_or_move(next, idx);
1014 const auto elem = static_cast<typename traits_type::entity_type>(curr);
1015 sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
1016 curr = std::exchange(next, idx);
1017 }
1018 }
1019 }
1020
1033 template<typename Compare, typename Sort = std_sort, typename... Args>
1034 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1035 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
1036 sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
1037 }
1038
1052 template<typename It>
1053 iterator sort_as(It first, It last) {
1054 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
1055 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
1056 auto it = end() - static_cast<difference_type>(len);
1057
1058 for(const auto other = end(); (it != other) && (first != last); ++first) {
1059 if(const auto curr = *first; contains(curr)) {
1060 if(const auto entt = *it; entt != curr) {
1061 // basic no-leak guarantee (with invalid state) if swapping throws
1062 swap_elements(entt, curr);
1063 }
1064
1065 ++it;
1066 }
1067 }
1068
1069 return it;
1070 }
1071
1073 void clear() {
1074 pop_all();
1075 // sanity check to avoid subtle issues due to storage classes
1076 ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
1077 head = policy_to_head();
1078 packed.clear();
1079 }
1080
1085 [[nodiscard]] const type_info &info() const noexcept {
1086 return *descriptor;
1087 }
1088
1090 [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
1091 return info();
1092 }
1093
1099 template<typename Type>
1100 void bind(Type &&value) noexcept {
1101 bind_any(forward_as_any(std::forward<Type>(value)));
1102 }
1103
1104private:
1105 sparse_container_type sparse;
1106 packed_container_type packed;
1107 const type_info *descriptor;
1108 deletion_policy mode;
1109 size_type head;
1110};
1111
1112} // namespace entt
1113
1114#endif
entt::basic_entt_traits::entity_type
typename Traits::entity_type entity_type
Underlying entity type.
Definition entity.hpp:71
entt::basic_entt_traits::version_mask
static constexpr entity_type version_mask
Version mask size.
Definition entity.hpp:78
entt::basic_entt_traits::next
static constexpr value_type next(const value_type value) noexcept
Returns the successor of a given identifier.
Definition entity.hpp:116
entt::basic_entt_traits::to_integral
static constexpr entity_type to_integral(const value_type value) noexcept
Converts an entity to its underlying type.
Definition entity.hpp:85
entt::basic_entt_traits::entity_mask
static constexpr entity_type entity_mask
Entity mask size.
Definition entity.hpp:76
entt::basic_entt_traits::to_entity
static constexpr entity_type to_entity(const value_type value) noexcept
Returns the entity part once converted to the underlying type.
Definition entity.hpp:94
entt::basic_entt_traits::value_type
typename Traits::value_type value_type
Value type.
Definition entity.hpp:69
entt::basic_entt_traits::version_type
typename Traits::version_type version_type
Underlying version type.
Definition entity.hpp:73
entt::basic_entt_traits::combine
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
Combines two identifiers in a single one.
Definition entity.hpp:149
entt::basic_entt_traits::to_version
static constexpr version_type to_version(const value_type value) noexcept
Returns the version part once converted to the underlying type.
Definition entity.hpp:103
entt::basic_sparse_set
Sparse set implementation.
Definition sparse_set.hpp:158
entt::basic_sparse_set::erase
void erase(const entity_type entt)
Erases an entity from a sparse set.
Definition sparse_set.hpp:852
entt::basic_sparse_set::begin
iterator begin() const noexcept
Returns an iterator to the beginning.
Definition sparse_set.hpp:650
entt::basic_sparse_set<>::version_type
typename traits_type::version_type version_type
Definition sparse_set.hpp:391
entt::basic_sparse_set::capacity
virtual size_type capacity() const noexcept
Returns the number of elements that a sparse set has currently allocated space for.
Definition sparse_set.hpp:555
entt::basic_sparse_set::cbegin
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
Definition sparse_set.hpp:656
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
Definition sparse_set.hpp:415
entt::basic_sparse_set::try_emplace
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void *=nullptr)
Assigns an entity to a sparse set.
Definition sparse_set.hpp:344
entt::basic_sparse_set::pop_all
virtual void pop_all()
Erases all entities of a sparse set.
Definition sparse_set.hpp:314
entt::basic_sparse_set::erase
void erase(It first, It last)
Erases entities from a set.
Definition sparse_set.hpp:867
entt::basic_sparse_set<>::const_reverse_iterator
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition sparse_set.hpp:405
entt::basic_sparse_set::swap_elements
void swap_elements(const entity_type lhs, const entity_type rhs)
Swaps two entities in a sparse set.
Definition sparse_set.hpp:959
entt::basic_sparse_set::swap_and_pop
void swap_and_pop(const basic_iterator it)
Erases an entity from a sparse set.
Definition sparse_set.hpp:264
entt::basic_sparse_set::size_type
std::size_t size_type
Unsigned integer type.
Definition sparse_set.hpp:393
entt::basic_sparse_set::end
iterator end() const noexcept
Returns an iterator to the end.
Definition sparse_set.hpp:665
entt::basic_sparse_set::value
void * value(const entity_type entt) noexcept
Returns the element assigned to an entity, if any.
Definition sparse_set.hpp:781
entt::basic_sparse_set<>::const_iterator
iterator const_iterator
Definition sparse_set.hpp:401
entt::basic_sparse_set::rend
reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
Definition sparse_set.hpp:697
entt::basic_sparse_set::push
iterator push(It first, It last)
Assigns one or more entities to a sparse set.
Definition sparse_set.hpp:815
entt::basic_sparse_set::sort_n
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.
Definition sparse_set.hpp:999
entt::basic_sparse_set::data
pointer data() const noexcept
Direct access to the internal packed array.
Definition sparse_set.hpp:638
entt::basic_sparse_set::difference_type
std::ptrdiff_t difference_type
Signed integer type.
Definition sparse_set.hpp:395
entt::basic_sparse_set::operator=
basic_sparse_set & operator=(basic_sparse_set &&other) noexcept
Move assignment operator.
Definition sparse_set.hpp:486
entt::basic_sparse_set::reserve
virtual void reserve(const size_type cap)
Increases the capacity of a sparse set.
Definition sparse_set.hpp:546
entt::basic_sparse_set::clear
void clear()
Clears a sparse set.
Definition sparse_set.hpp:1073
entt::basic_sparse_set::bind
void bind(Type &&value) noexcept
Forwards variables to derived classes, if any.
Definition sparse_set.hpp:1100
entt::basic_sparse_set::size
size_type size() const noexcept
Returns the number of elements in a sparse set.
Definition sparse_set.hpp:614
entt::basic_sparse_set<>::iterator
basic_iterator iterator
Definition sparse_set.hpp:399
entt::basic_sparse_set::operator=
basic_sparse_set & operator=(const basic_sparse_set &)=delete
Default copy assignment operator, deleted on purpose.
entt::basic_sparse_set::contiguous
bool contiguous() const noexcept
Checks whether a sparse set is fully packed.
Definition sparse_set.hpp:630
entt::basic_sparse_set::bump
version_type bump(const entity_type entt)
Bump the version number of an entity.
Definition sparse_set.hpp:835
entt::basic_sparse_set::shrink_to_fit
virtual void shrink_to_fit()
Requests the removal of unused capacity.
Definition sparse_set.hpp:560
entt::basic_sparse_set::extent
size_type extent() const noexcept
Returns the extent of a sparse set.
Definition sparse_set.hpp:600
entt::basic_sparse_set::swap
void swap(basic_sparse_set &other) noexcept
Exchanges the contents with those of a given sparse set.
Definition sparse_set.hpp:496
entt::basic_sparse_set<>::pointer
typename packed_container_type::const_pointer pointer
Definition sparse_set.hpp:397
entt::basic_sparse_set::find
const_iterator find(const entity_type entt) const noexcept
Finds an entity.
Definition sparse_set.hpp:712
entt::basic_sparse_set::push
iterator push(const entity_type entt, const void *elem=nullptr)
Assigns an entity to a sparse set.
Definition sparse_set.hpp:797
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set(deletion_policy pol, const allocator_type &allocator={})
Constructs an empty container with the given policy and allocator.
Definition sparse_set.hpp:423
entt::basic_sparse_set::pop
virtual void pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
Definition sparse_set.hpp:293
entt::basic_sparse_set::policy
deletion_policy policy() const noexcept
Returns the deletion policy of a sparse set.
Definition sparse_set.hpp:517
entt::basic_sparse_set::bind_any
virtual void bind_any(any) noexcept
Forwards variables to derived classes, if any.
Definition sparse_set.hpp:383
entt::basic_sparse_set::current
version_type current(const entity_type entt) const noexcept
Returns the contained version for an identifier.
Definition sparse_set.hpp:735
entt::basic_sparse_set::contains
bool contains(const entity_type entt) const noexcept
Checks if a sparse set contains an entity.
Definition sparse_set.hpp:721
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
Allocator-extended move constructor.
Definition sparse_set.hpp:461
entt::basic_sparse_set::sort_as
iterator sort_as(It first, It last)
Sort entities according to their order in a range.
Definition sparse_set.hpp:1053
entt::basic_sparse_set::cend
const_iterator cend() const noexcept
Returns an iterator to the end.
Definition sparse_set.hpp:670
entt::basic_sparse_set::in_place_pop
void in_place_pop(const basic_iterator it)
Erases an entity from a sparse set.
Definition sparse_set.hpp:282
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set(basic_sparse_set &&other) noexcept
Move constructor.
Definition sparse_set.hpp:449
entt::basic_sparse_set::crend
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the end.
Definition sparse_set.hpp:702
entt::basic_sparse_set::get_allocator
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
Definition sparse_set.hpp:509
entt::basic_sparse_set::remove
size_type remove(It first, It last)
Removes entities from a sparse set if they exist.
Definition sparse_set.hpp:894
entt::basic_sparse_set<>::value
const void * value(const entity_type entt) const noexcept
Definition sparse_set.hpp:776
entt::basic_sparse_set::rbegin
reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
Definition sparse_set.hpp:683
entt::basic_sparse_set::operator[]
entity_type operator[](const size_type pos) const noexcept
Returns the entity at specified location.
Definition sparse_set.hpp:761
entt::basic_sparse_set::free_list
size_type free_list() const noexcept
Returns data on the free list whose meaning depends on the mode.
Definition sparse_set.hpp:525
entt::basic_sparse_set::swap_only
void swap_only(const basic_iterator it)
Erases an entity from a sparse set.
Definition sparse_set.hpp:253
entt::basic_sparse_set<>::basic_iterator
internal::sparse_set_iterator< packed_container_type > basic_iterator
Definition sparse_set.hpp:247
entt::basic_sparse_set::~basic_sparse_set
virtual ~basic_sparse_set()
Default destructor.
Definition sparse_set.hpp:471
entt::basic_sparse_set::free_list
void free_list(const size_type value) noexcept
Sets data on the free list whose meaning depends on the mode.
Definition sparse_set.hpp:533
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set(const basic_sparse_set &)=delete
Default copy constructor, deleted on purpose.
entt::basic_sparse_set::crbegin
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
Definition sparse_set.hpp:688
entt::basic_sparse_set::empty
bool empty() const noexcept
Checks whether a sparse set is empty.
Definition sparse_set.hpp:622
entt::basic_sparse_set::index
size_type index(const entity_type entt) const noexcept
Returns the position of an entity in a sparse set.
Definition sparse_set.hpp:751
entt::basic_sparse_set::info
const type_info & info() const noexcept
Returns a type info object for the value type, if any.
Definition sparse_set.hpp:1085
entt::basic_sparse_set::type
const type_info & type() const noexcept
Returns a type info object for the value type, if any.
Definition sparse_set.hpp:1090
entt::basic_sparse_set::sort
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort all elements according to the given comparison function.
Definition sparse_set.hpp:1034
entt::basic_sparse_set::entity_type
typename traits_type::value_type entity_type
Underlying entity identifier.
Definition sparse_set.hpp:389
entt::basic_sparse_set::remove
bool remove(const entity_type entt)
Removes an entity from a sparse set if it exists.
Definition sparse_set.hpp:882
entt::basic_sparse_set::compact
void compact()
Removes all tombstones from a sparse set.
Definition sparse_set.hpp:922
entt::basic_sparse_set::basic_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.
Definition sparse_set.hpp:433
entt::basic_sparse_set<>::allocator_type
Allocator allocator_type
Definition sparse_set.hpp:387
entt::basic_sparse_set::basic_sparse_set
basic_sparse_set()
Default constructor.
Definition sparse_set.hpp:408
entt::basic_sparse_set<>::reverse_iterator
std::reverse_iterator< iterator > reverse_iterator
Definition sparse_set.hpp:403
entt
EnTT default namespace.
Definition dense_map.hpp:22
entt::fast_mod
constexpr std::enable_if_t< std::is_unsigned_v< Type >, Type > fast_mod(const Type value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition bit.hpp:63
entt::null
constexpr null_t null
Compile-time constant for null entities.
Definition entity.hpp:375
entt::tombstone
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:384
entt::any
basic_any<> any
Alias declaration for the most common use case.
Definition fwd.hpp:32
entt::operator<=
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:263
entt::operator<
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:250
entt::operator+
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
Definition type_traits.hpp:210
entt::deletion_policy
deletion_policy
Storage deletion policy.
Definition fwd.hpp:17
entt::deletion_policy::swap_only
@ swap_only
Swap-only deletion policy.
Definition fwd.hpp:23
entt::deletion_policy::swap_and_pop
@ swap_and_pop
Swap-and-pop deletion policy.
Definition fwd.hpp:19
entt::deletion_policy::in_place
@ in_place
In-place deletion policy.
Definition fwd.hpp:21
entt::operator!=
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:238
entt::operator>=
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:289
entt::type_id
const type_info & type_id() noexcept
Returns the type info object associated to a given type.
Definition type_info.hpp:259
entt::any_policy::ref
@ ref
Aliasing mode, non-const reference.
Definition fwd.hpp:19
entt::operator>
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:276
entt::forward_as_any
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:619
entt::operator==
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition hashed_string.hpp:226
entt::entt_traits
Entity traits.
Definition entity.hpp:163
entt::entt_traits< Entity >::page_size
static constexpr std::size_t page_size
Definition entity.hpp:167
entt::std_sort
Function object to wrap std::sort in a class type.
Definition algorithm.hpp:21
entt::type_info
Implementation specific information about a type.
Definition type_info.hpp:141