EnTT 3.13.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/memory.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{std::addressof(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 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 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->data()[index() - value];
77 }
78
79 [[nodiscard]] constexpr pointer operator->() const noexcept {
80 return packed->data() + index();
81 }
82
83 [[nodiscard]] constexpr reference operator*() const noexcept {
84 return *operator->();
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
157template<typename Entity, typename Allocator>
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 underlying_type = typename entt_traits<Entity>::entity_type;
164
165 [[nodiscard]] auto sparse_ptr(const Entity entt) const {
166 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
167 const auto page = pos / traits_type::page_size;
168 return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
169 }
170
171 [[nodiscard]] auto &sparse_ref(const Entity entt) const {
172 ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
173 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
174 return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
175 }
176
177 [[nodiscard]] auto to_iterator(const Entity entt) const {
178 return --(end() - index(entt));
179 }
180
181 [[nodiscard]] auto &assure_at_least(const Entity entt) {
182 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
183 const auto page = pos / traits_type::page_size;
184
185 if(!(page < sparse.size())) {
186 sparse.resize(page + 1u, nullptr);
187 }
188
189 if(!sparse[page]) {
190 constexpr entity_type init = null;
191 auto page_allocator{packed.get_allocator()};
192 sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
193 std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
194 }
195
196 return sparse[page][fast_mod(pos, traits_type::page_size)];
197 }
198
199 void release_sparse_pages() {
200 auto page_allocator{packed.get_allocator()};
201
202 for(auto &&page: sparse) {
203 if(page != nullptr) {
204 std::destroy(page, page + traits_type::page_size);
205 alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
206 page = nullptr;
207 }
208 }
209 }
210
211 void swap_at(const std::size_t from, const std::size_t to) {
212 auto &lhs = packed[from];
213 auto &rhs = packed[to];
214
215 sparse_ref(lhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(to), traits_type::to_integral(lhs));
216 sparse_ref(rhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(from), traits_type::to_integral(rhs));
217
218 std::swap(lhs, rhs);
219 }
220
221 underlying_type policy_to_head() {
223 }
224
225private:
226 virtual const void *get_at(const std::size_t) const {
227 return nullptr;
228 }
229
230 virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
231 ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
232 }
233
234protected:
236 using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
237
242 void swap_only(const basic_iterator it) {
243 ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
244 const auto pos = static_cast<underlying_type>(index(*it));
246 swap_at(pos, static_cast<size_type>(head -= (pos < head)));
247 }
248
254 ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
255 auto &self = sparse_ref(*it);
256 const auto entt = traits_type::to_entity(self);
257 sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
258 packed[static_cast<size_type>(entt)] = packed.back();
259 // unnecessary but it helps to detect nasty bugs
260 ENTT_ASSERT((packed.back() = null, true), "");
261 // lazy self-assignment guard
262 self = null;
263 packed.pop_back();
264 }
265
271 ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
272 const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
273 packed[static_cast<size_type>(entt)] = traits_type::combine(std::exchange(head, entt), tombstone);
274 }
275
276protected:
282 virtual void pop(basic_iterator first, basic_iterator last) {
283 switch(mode) {
285 for(; first != last; ++first) {
286 swap_and_pop(first);
287 }
288 break;
290 for(; first != last; ++first) {
291 in_place_pop(first);
292 }
293 break;
295 for(; first != last; ++first) {
296 swap_only(first);
297 }
298 break;
299 }
300 }
301
303 virtual void pop_all() {
304 switch(mode) {
306 if(head != traits_type::to_entity(null)) {
307 for(auto first = begin(); !(first.index() < 0); ++first) {
308 if(*first != tombstone) {
309 sparse_ref(*first) = null;
310 }
311 }
312 break;
313 }
314 [[fallthrough]];
317 for(auto first = begin(); !(first.index() < 0); ++first) {
318 sparse_ref(*first) = null;
319 }
320 break;
321 }
322
323 head = policy_to_head();
324 packed.clear();
325 }
326
333 virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
334 auto &elem = assure_at_least(entt);
335 auto pos = size();
336
337 switch(mode) {
339 if(head != traits_type::to_entity(null) && !force_back) {
340 pos = static_cast<size_type>(head);
341 ENTT_ASSERT(elem == null, "Slot not available");
343 head = traits_type::to_entity(std::exchange(packed[pos], entt));
344 break;
345 }
346 [[fallthrough]];
348 packed.push_back(entt);
349 ENTT_ASSERT(elem == null, "Slot not available");
350 elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
351 break;
353 if(elem == null) {
354 packed.push_back(entt);
355 elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
356 } else {
357 ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available");
358 bump(entt);
359 }
360
361 if(force_back) {
362 pos = static_cast<size_type>(head++);
363 swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
364 }
365
366 break;
367 }
368
369 return --(end() - pos);
370 }
371
372public:
380 using size_type = std::size_t;
382 using allocator_type = Allocator;
384 using pointer = typename packed_container_type::const_pointer;
390 using reverse_iterator = std::reverse_iterator<iterator>;
392 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
393
396 : basic_sparse_set{type_id<void>()} {}
397
402 explicit basic_sparse_set(const allocator_type &allocator)
403 : basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
404
410 explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
411 : basic_sparse_set{type_id<void>(), pol, allocator} {}
412
421 : sparse{allocator},
422 packed{allocator},
423 info{&elem},
424 mode{pol},
425 head{policy_to_head()} {}
426
432 : sparse{std::move(other.sparse)},
433 packed{std::move(other.packed)},
434 info{other.info},
435 mode{other.mode},
436 head{std::exchange(other.head, policy_to_head())} {}
437
443 basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
444 : sparse{std::move(other.sparse), allocator},
445 packed{std::move(other.packed), allocator},
446 info{other.info},
447 mode{other.mode},
448 head{std::exchange(other.head, policy_to_head())} {
449 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
450 }
451
454 release_sparse_pages();
455 }
456
463 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
464
465 release_sparse_pages();
466 sparse = std::move(other.sparse);
467 packed = std::move(other.packed);
468 info = other.info;
469 mode = other.mode;
470 head = std::exchange(other.head, policy_to_head());
471 return *this;
472 }
473
478 void swap(basic_sparse_set &other) {
479 using std::swap;
480 swap(sparse, other.sparse);
481 swap(packed, other.packed);
482 swap(info, other.info);
483 swap(mode, other.mode);
484 swap(head, other.head);
485 }
486
491 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
492 return packed.get_allocator();
493 }
494
499 [[nodiscard]] deletion_policy policy() const noexcept {
500 return mode;
501 }
502
507 [[nodiscard]] size_type free_list() const noexcept {
508 return static_cast<size_type>(head);
509 }
510
515 void free_list(const size_type len) noexcept {
516 ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
517 head = static_cast<underlying_type>(len);
518 }
519
528 virtual void reserve(const size_type cap) {
529 packed.reserve(cap);
530 }
531
537 [[nodiscard]] virtual size_type capacity() const noexcept {
538 return packed.capacity();
539 }
540
542 virtual void shrink_to_fit() {
543 packed.shrink_to_fit();
544 }
545
556 [[nodiscard]] size_type extent() const noexcept {
557 return sparse.size() * traits_type::page_size;
558 }
559
570 [[nodiscard]] size_type size() const noexcept {
571 return packed.size();
572 }
573
578 [[nodiscard]] bool empty() const noexcept {
579 return packed.empty();
580 }
581
586 [[nodiscard]] bool contiguous() const noexcept {
587 return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null));
588 }
589
594 [[nodiscard]] pointer data() const noexcept {
595 return packed.data();
596 }
597
606 [[nodiscard]] iterator begin() const noexcept {
607 const auto pos = static_cast<typename iterator::difference_type>(packed.size());
608 return iterator{packed, pos};
609 }
610
612 [[nodiscard]] const_iterator cbegin() const noexcept {
613 return begin();
614 }
615
621 [[nodiscard]] iterator end() const noexcept {
622 return iterator{packed, {}};
623 }
624
626 [[nodiscard]] const_iterator cend() const noexcept {
627 return end();
628 }
629
639 [[nodiscard]] reverse_iterator rbegin() const noexcept {
640 return std::make_reverse_iterator(end());
641 }
642
644 [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
645 return rbegin();
646 }
647
653 [[nodiscard]] reverse_iterator rend() const noexcept {
654 return std::make_reverse_iterator(begin());
655 }
656
658 [[nodiscard]] const_reverse_iterator crend() const noexcept {
659 return rend();
660 }
661
663 [[nodiscard]] iterator begin(int) const noexcept {
664 return (mode == deletion_policy::swap_only) ? (end() - static_cast<typename iterator::difference_type>(head)) : begin();
665 }
666
668 [[nodiscard]] const_iterator cbegin(int) const noexcept {
669 return begin(0);
670 }
671
673 [[nodiscard]] iterator end(int) const noexcept {
674 return end();
675 }
676
678 [[nodiscard]] const_iterator cend(int) const noexcept {
679 return end(0);
680 }
681
683 [[nodiscard]] reverse_iterator rbegin(int) const noexcept {
684 return std::make_reverse_iterator(end(0));
685 }
686
688 [[nodiscard]] const_reverse_iterator crbegin(int) const noexcept {
689 return rbegin(0);
690 }
691
693 [[nodiscard]] reverse_iterator rend(int) const noexcept {
694 return std::make_reverse_iterator(begin(0));
695 }
696
698 [[nodiscard]] const_reverse_iterator crend(int) const noexcept {
699 return rend(0);
700 }
701
708 [[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
709 return contains(entt) ? to_iterator(entt) : end();
710 }
711
717 [[nodiscard]] bool contains(const entity_type entt) const noexcept {
718 const auto elem = sparse_ptr(entt);
719 constexpr auto cap = traits_type::entity_mask;
720 constexpr auto mask = traits_type::to_integral(null) & ~cap;
721 // testing versions permits to avoid accessing the packed array
722 return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
723 }
724
731 [[nodiscard]] version_type current(const entity_type entt) const noexcept {
732 const auto elem = sparse_ptr(entt);
733 constexpr auto fallback = traits_type::to_version(tombstone);
734 return elem ? traits_type::to_version(*elem) : fallback;
735 }
736
747 [[nodiscard]] size_type index(const entity_type entt) const noexcept {
748 ENTT_ASSERT(contains(entt), "Set does not contain entity");
749 return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
750 }
751
757 [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
758 return pos < packed.size() ? packed[pos] : null;
759 }
760
766 [[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
767 ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
768 return packed[pos];
769 }
770
781 [[nodiscard]] const void *value(const entity_type entt) const noexcept {
782 return get_at(index(entt));
783 }
784
786 [[nodiscard]] void *value(const entity_type entt) noexcept {
787 return const_cast<void *>(std::as_const(*this).value(entt));
788 }
789
802 iterator push(const entity_type entt, const void *elem = nullptr) {
803 return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
804 }
805
819 template<typename It>
820 iterator push(It first, It last) {
821 for(auto it = first; it != last; ++it) {
822 try_emplace(*it, true);
823 }
824
825 return first == last ? end() : find(*first);
826 }
827
839 auto &entity = sparse_ref(entt);
840 ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
842 packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
844 }
845
855 void erase(const entity_type entt) {
856 const auto it = to_iterator(entt);
857 pop(it, it + 1u);
858 }
859
869 template<typename It>
870 void erase(It first, It last) {
871 if constexpr(std::is_same_v<It, basic_iterator>) {
872 pop(first, last);
873 } else {
874 for(; first != last; ++first) {
875 erase(*first);
876 }
877 }
878 }
879
885 bool remove(const entity_type entt) {
886 return contains(entt) && (erase(entt), true);
887 }
888
896 template<typename It>
897 size_type remove(It first, It last) {
898 size_type count{};
899
900 if constexpr(std::is_same_v<It, basic_iterator>) {
901 while(first != last) {
902 while(first != last && !contains(*first)) {
903 ++first;
904 }
905
906 const auto it = first;
907
908 while(first != last && contains(*first)) {
909 ++first;
910 }
911
912 count += std::distance(it, first);
913 erase(it, first);
914 }
915 } else {
916 for(; first != last; ++first) {
917 count += remove(*first);
918 }
919 }
920
921 return count;
922 }
923
925 void compact() {
926 if(mode == deletion_policy::in_place) {
927 size_type from = packed.size();
928 for(; from && packed[from - 1u] == tombstone; --from) {}
929 underlying_type pos = std::exchange(head, traits_type::entity_mask);
930
931 while(pos != traits_type::to_entity(null)) {
932 if(const auto to = static_cast<size_type>(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) {
933 --from;
934 swap_or_move(from, to);
935
936 packed[to] = packed[from];
937 const auto entity = static_cast<typename traits_type::entity_type>(to);
938 sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
939
940 for(; from && packed[from - 1u] == tombstone; --from) {}
941 }
942 }
943
944 packed.erase(packed.begin() + from, packed.end());
945 }
946 }
947
961 void swap_elements(const entity_type lhs, const entity_type rhs) {
962 const auto from = index(lhs);
963 const auto to = index(rhs);
964
965 // basic no-leak guarantee if swapping throws
966 swap_or_move(from, to);
967 swap_at(from, to);
968 }
969
1000 template<typename Compare, typename Sort = std_sort, typename... Args>
1001 void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
1002 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
1003 ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
1004
1005 algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
1006
1007 for(size_type pos{}; pos < length; ++pos) {
1008 auto curr = pos;
1009 auto next = index(packed[curr]);
1010
1011 while(curr != next) {
1012 const auto idx = index(packed[next]);
1013 const auto entt = packed[curr];
1014
1015 swap_or_move(next, idx);
1016 const auto entity = static_cast<typename traits_type::entity_type>(curr);
1017 sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
1018 curr = std::exchange(next, idx);
1019 }
1020 }
1021 }
1022
1035 template<typename Compare, typename Sort = std_sort, typename... Args>
1036 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1037 sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
1038 }
1039
1052 template<typename It>
1053 void sort_as(It first, It last) {
1054 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
1055
1056 for(auto it = begin(0); it.index() && first != last; ++first) {
1057 if(const auto curr = *first; contains(curr)) {
1058 if(const auto entt = *it; entt != curr) {
1059 // basic no-leak guarantee (with invalid state) if swapping throws
1060 swap_elements(entt, curr);
1061 }
1062
1063 ++it;
1064 }
1065 }
1066 }
1067
1072 [[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) {
1073 sort_as(other.begin(), other.end());
1074 }
1075
1077 void clear() {
1078 pop_all();
1079 // sanity check to avoid subtle issues due to storage classes
1080 ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
1081 head = policy_to_head();
1082 packed.clear();
1083 }
1084
1089 const type_info &type() const noexcept {
1090 return *info;
1091 }
1092
1094 virtual void bind(any) noexcept {}
1095
1096private:
1097 sparse_container_type sparse;
1098 packed_container_type packed;
1099 const type_info *info;
1100 deletion_policy mode;
1101 underlying_type head;
1102};
1103
1104} // namespace entt
1105
1106#endif
typename Traits::entity_type entity_type
Underlying entity type.
Definition entity.hpp:76
static constexpr value_type next(const value_type value) noexcept
Returns the successor of a given identifier.
Definition entity.hpp:117
static constexpr entity_type to_integral(const value_type value) noexcept
Converts an entity to its underlying type.
Definition entity.hpp:90
static constexpr entity_type entity_mask
Entity mask size.
Definition entity.hpp:81
static constexpr entity_type to_entity(const value_type value) noexcept
Returns the entity part once converted to the underlying type.
Definition entity.hpp:99
typename Traits::value_type value_type
Value type.
Definition entity.hpp:74
typename Traits::version_type version_type
Underlying version type.
Definition entity.hpp:78
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
Combines two identifiers in a single one.
Definition entity.hpp:146
static constexpr version_type to_version(const value_type value) noexcept
Returns the version part once converted to the underlying type.
Definition entity.hpp:108
Basic sparse set implementation.
void erase(const entity_type entt)
Erases an entity from a sparse set.
void swap(basic_sparse_set &other)
Exchanges the contents with those of a given sparse set.
iterator begin() const noexcept
Returns an iterator to the beginning.
typename traits_type::version_type version_type
Underlying version type.
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.
std::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type.
void swap_elements(const entity_type lhs, const entity_type rhs)
Swaps two entities in a sparse set.
void swap_and_pop(const basic_iterator it)
Erases an entity from a sparse set.
iterator end(int) const noexcept
Returns an iterator to the end. Useful only in case of swap-only policy.
std::size_t size_type
Unsigned integer type.
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.
const_reverse_iterator crend(int) const noexcept
Returns a reverse iterator to the beginning. Useful only in case of swap-only policy.
const_iterator cbegin(int) const noexcept
Returns an iterator to the beginning. Useful only in case of swap-only policy.
iterator const_iterator
Constant random access iterator type.
const_iterator cend(int) const noexcept
Returns an iterator to the end. Useful only in case of swap-only policy.
reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
iterator push(It first, It last)
Assigns one or more entities to a sparse set.
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.
size_type size() const noexcept
Returns the number of elements in a sparse set.
basic_iterator iterator
Random access iterator type.
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.
void sort_as(const basic_sparse_set &other)
Sort entities according to their order in a range.
virtual void shrink_to_fit()
Requests the removal of unused capacity.
size_type extent() const noexcept
Returns the extent of a sparse set.
typename packed_container_type::const_pointer pointer
Pointer type to contained entities.
const_iterator find(const entity_type entt) const noexcept
Finds an entity.
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
Allocator-extended move constructor.
iterator push(const entity_type entt, const void *elem=nullptr)
Assigns an entity to 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.
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.
virtual void bind(any) noexcept
Forwards variables to derived classes, if any.
const_iterator cend() const noexcept
Returns an iterator to the end.
void in_place_pop(const basic_iterator it)
Erases an entity from a sparse set.
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
Returns the element assigned to an entity, if any.
const_reverse_iterator crbegin(int) const noexcept
Returns a reverse iterator to the beginning. Useful only in case of swap-only policy.
void free_list(const size_type len) noexcept
Sets the head of the free list, if possible.
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, without bounds checking.
size_type free_list() const noexcept
Returns the head of the free list, if any.
void swap_only(const basic_iterator it)
Erases an entity from a sparse set.
internal::sparse_set_iterator< packed_container_type > basic_iterator
Random access iterator type.
virtual ~basic_sparse_set()
Default destructor.
iterator begin(int) const noexcept
Returns an iterator to the beginning. Useful only in case of swap-only policy.
entity_type at(const size_type pos) const noexcept
Returns the entity at specified location, with bounds checking.
reverse_iterator rend(int) const noexcept
Returns a reverse iterator to the beginning. Useful only in case of swap-only policy.
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.
void sort_as(It first, It last)
Sort entities according to their order in a range.
const type_info & type() const noexcept
Returned value type, if any.
reverse_iterator rbegin(int) const noexcept
Returns a reverse iterator to the beginning. Useful only in case of swap-only policy.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort all elements according to the given comparison function.
typename traits_type::value_type entity_type
Underlying entity identifier.
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.
Allocator allocator_type
Allocator type.
basic_sparse_set()
Default constructor.
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
EnTT default namespace.
Definition dense_map.hpp:21
entity
Default entity identifier.
Definition fwd.hpp:13
constexpr null_t null
Compile-time constant for null entities.
Definition entity.hpp:363
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:372
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition memory.hpp:47
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
deletion_policy
Storage deletion policy.
Definition fwd.hpp:16
@ swap_only
Swap-only deletion policy.
@ swap_and_pop
Swap-and-pop deletion policy.
@ in_place
In-place deletion policy.
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
const type_info & type_id() noexcept
Returns the type info object associated to a given type.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
static constexpr std::size_t page_size
Page size, default is ENTT_SPARSE_PAGE.
Definition entity.hpp:160
Function object to wrap std::sort in a class type.
Definition algorithm.hpp:21
Implementation specific information about a type.