EnTT 3.14.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 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)[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
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>;
164
165 static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
166
167 [[nodiscard]] auto policy_to_head() const noexcept {
168 return static_cast<size_type>(max_size * static_cast<decltype(max_size)>(mode != deletion_policy::swap_only));
169 }
170
171 [[nodiscard]] auto sparse_ptr(const Entity entt) const {
172 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
173 const auto page = pos / traits_type::page_size;
174 return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
175 }
176
177 [[nodiscard]] auto &sparse_ref(const Entity entt) const {
178 ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
179 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
181 }
182
183 [[nodiscard]] auto to_iterator(const Entity entt) const {
184 return --(end() - index(entt));
185 }
186
187 [[nodiscard]] auto &assure_at_least(const Entity entt) {
188 const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
189 const auto page = pos / traits_type::page_size;
190
191 if(!(page < sparse.size())) {
192 sparse.resize(page + 1u, nullptr);
193 }
194
195 if(!sparse[page]) {
196 constexpr entity_type init = null;
197 auto page_allocator{packed.get_allocator()};
198 sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
199 std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
200 }
201
202 return sparse[page][fast_mod(pos, traits_type::page_size)];
203 }
204
205 void release_sparse_pages() {
206 auto page_allocator{packed.get_allocator()};
207
208 for(auto &&page: sparse) {
209 if(page != nullptr) {
210 std::destroy(page, page + traits_type::page_size);
211 alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
212 page = nullptr;
213 }
214 }
215 }
216
217 void swap_at(const std::size_t lhs, const std::size_t rhs) {
218 auto &from = packed[lhs];
219 auto &to = packed[rhs];
220
223
224 std::swap(from, to);
225 }
226
227private:
228 [[nodiscard]] virtual const void *get_at(const std::size_t) const {
229 return nullptr;
230 }
231
232 virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
233 ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported");
234 }
235
236protected:
238 using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
239
245 ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
246 const auto pos = index(*it);
248 swap_at(pos, head -= (pos < head));
249 }
250
256 ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
257 auto &self = sparse_ref(*it);
258 const auto entt = traits_type::to_entity(self);
259 sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
260 packed[static_cast<size_type>(entt)] = packed.back();
261 // unnecessary but it helps to detect nasty bugs
262 ENTT_ASSERT((packed.back() = null, true), "");
263 // lazy self-assignment guard
264 self = null;
265 packed.pop_back();
266 }
267
273 ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
274 const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null)));
275 packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
276 }
277
283 virtual void pop(basic_iterator first, basic_iterator last) {
284 switch(mode) {
286 for(; first != last; ++first) {
287 swap_and_pop(first);
288 }
289 break;
291 for(; first != last; ++first) {
292 in_place_pop(first);
293 }
294 break;
296 for(; first != last; ++first) {
297 swap_only(first);
298 }
299 break;
300 }
301 }
302
304 virtual void pop_all() {
305 switch(mode) {
307 if(head != max_size) {
308 for(auto first = begin(); !(first.index() < 0); ++first) {
309 if(*first != tombstone) {
310 sparse_ref(*first) = null;
311 }
312 }
313 break;
314 }
315 [[fallthrough]];
318 for(auto first = begin(); !(first.index() < 0); ++first) {
319 sparse_ref(*first) = null;
320 }
321 break;
322 }
323
324 head = policy_to_head();
325 packed.clear();
326 }
327
334 virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
335 ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
336 auto &elem = assure_at_least(entt);
337 auto pos = size();
338
339 switch(mode) {
341 if(head != max_size && !force_back) {
342 pos = head;
343 ENTT_ASSERT(elem == null, "Slot not available");
345 head = static_cast<size_type>(traits_type::to_entity(std::exchange(packed[pos], entt)));
346 break;
347 }
348 [[fallthrough]];
350 packed.push_back(entt);
351 ENTT_ASSERT(elem == null, "Slot not available");
353 break;
355 if(elem == null) {
356 packed.push_back(entt);
358 } else {
359 ENTT_ASSERT(!(static_cast<size_type>(traits_type::to_entity(elem)) < head), "Slot not available");
360 bump(entt);
361 }
362
363 pos = head++;
364 swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
365 break;
366 }
367
368 return --(end() - static_cast<typename iterator::difference_type>(pos));
369 }
370
372 // NOLINTNEXTLINE(performance-unnecessary-value-param)
373 virtual void bind_any(any) noexcept {}
374
375public:
377 using allocator_type = Allocator;
383 using size_type = std::size_t;
385 using pointer = typename packed_container_type::const_pointer;
391 using reverse_iterator = std::reverse_iterator<iterator>;
393 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
394
398
405
413
422 : sparse{allocator},
423 packed{allocator},
424 info{&elem},
425 mode{pol},
426 head{policy_to_head()} {
427 ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
428 }
429
432
438 : sparse{std::move(other.sparse)},
439 packed{std::move(other.packed)},
440 info{other.info},
441 mode{other.mode},
442 head{std::exchange(other.head, policy_to_head())} {}
443
450 : sparse{std::move(other.sparse), allocator},
451 packed{std::move(other.packed), allocator},
452 info{other.info},
453 mode{other.mode},
454 head{std::exchange(other.head, policy_to_head())} {
455 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
456 }
457
460 release_sparse_pages();
461 }
462
468
475 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
476 swap(other);
477 return *this;
478 }
479
484 void swap(basic_sparse_set &other) noexcept {
485 using std::swap;
486 swap(sparse, other.sparse);
487 swap(packed, other.packed);
488 swap(info, other.info);
489 swap(mode, other.mode);
490 swap(head, other.head);
491 }
492
498 return packed.get_allocator();
499 }
500
506 return mode;
507 }
508
514 return head;
515 }
516
521 void free_list(const size_type value) noexcept {
522 ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
523 head = value;
524 }
525
534 virtual void reserve(const size_type cap) {
535 packed.reserve(cap);
536 }
537
544 return packed.capacity();
545 }
546
548 virtual void shrink_to_fit() {
549 packed.shrink_to_fit();
550 }
551
563 return sparse.size() * traits_type::page_size;
564 }
565
577 return packed.size();
578 }
579
585 return packed.empty();
586 }
587
593 return (mode != deletion_policy::in_place) || (head == max_size);
594 }
595
601 return packed.data();
602 }
603
613 const auto pos = static_cast<typename iterator::difference_type>(packed.size());
614 return iterator{packed, pos};
615 }
616
619 return begin();
620 }
621
628 return iterator{packed, {}};
629 }
630
633 return end();
634 }
635
646 return std::make_reverse_iterator(end());
647 }
648
653
660 return std::make_reverse_iterator(begin());
661 }
662
667
674 [[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
675 return contains(entt) ? to_iterator(entt) : end();
676 }
677
683 [[nodiscard]] bool contains(const entity_type entt) const noexcept {
684 const auto *elem = sparse_ptr(entt);
685 constexpr auto cap = traits_type::entity_mask;
686 constexpr auto mask = traits_type::to_integral(null) & ~cap;
687 // testing versions permits to avoid accessing the packed array
689 }
690
697 [[nodiscard]] version_type current(const entity_type entt) const noexcept {
698 const auto *elem = sparse_ptr(entt);
701 }
702
713 [[nodiscard]] size_type index(const entity_type entt) const noexcept {
714 ENTT_ASSERT(contains(entt), "Set does not contain entity");
715 return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
716 }
717
723 [[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
724 ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
725 return packed[pos];
726 }
727
738 [[nodiscard]] const void *value(const entity_type entt) const noexcept {
739 return get_at(index(entt));
740 }
741
743 [[nodiscard]] void *value(const entity_type entt) noexcept {
744 return const_cast<void *>(std::as_const(*this).value(entt));
745 }
746
759 iterator push(const entity_type entt, const void *elem = nullptr) {
760 return try_emplace(entt, false, elem);
761 }
762
776 template<typename It>
777 iterator push(It first, It last) {
778 auto curr = end();
779
780 for(; first != last; ++first) {
781 curr = try_emplace(*first, true);
782 }
783
784 return curr;
785 }
786
798 auto &elem = sparse_ref(entt);
799 ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
801 packed[static_cast<size_type>(traits_type::to_entity(elem))] = entt;
803 }
804
814 void erase(const entity_type entt) {
815 const auto it = to_iterator(entt);
816 pop(it, it + 1u);
817 }
818
828 template<typename It>
829 void erase(It first, It last) {
830 if constexpr(std::is_same_v<It, basic_iterator>) {
831 pop(first, last);
832 } else {
833 for(; first != last; ++first) {
834 erase(*first);
835 }
836 }
837 }
838
844 bool remove(const entity_type entt) {
845 return contains(entt) && (erase(entt), true);
846 }
847
855 template<typename It>
856 size_type remove(It first, It last) {
857 size_type count{};
858
859 if constexpr(std::is_same_v<It, basic_iterator>) {
860 while(first != last) {
861 while(first != last && !contains(*first)) {
862 ++first;
863 }
864
865 const auto it = first;
866
867 while(first != last && contains(*first)) {
868 ++first;
869 }
870
871 count += std::distance(it, first);
872 erase(it, first);
873 }
874 } else {
875 for(; first != last; ++first) {
876 count += remove(*first);
877 }
878 }
879
880 return count;
881 }
882
884 void compact() {
885 if(mode == deletion_policy::in_place) {
886 size_type from = packed.size();
887 size_type pos = std::exchange(head, max_size);
888
889 for(; from && packed[from - 1u] == tombstone; --from) {}
890
891 while(pos != max_size) {
892 if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) {
893 --from;
894 swap_or_move(from, to);
895
896 packed[to] = packed[from];
897 const auto elem = static_cast<typename traits_type::entity_type>(to);
898 sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to]));
899
900 for(; from && packed[from - 1u] == tombstone; --from) {}
901 }
902 }
903
904 packed.erase(packed.begin() + from, packed.end());
905 }
906 }
907
922 const auto from = index(lhs);
923 const auto to = index(rhs);
924
925 // basic no-leak guarantee if swapping throws
926 swap_or_move(from, to);
927 swap_at(from, to);
928 }
929
960 template<typename Compare, typename Sort = std_sort, typename... Args>
961 void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
962 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
963 ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
964
965 algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
966
967 for(size_type pos{}; pos < length; ++pos) {
968 auto curr = pos;
969 auto next = index(packed[curr]);
970
971 while(curr != next) {
972 const auto idx = index(packed[next]);
973 const auto entt = packed[curr];
974
975 swap_or_move(next, idx);
976 const auto elem = static_cast<typename traits_type::entity_type>(curr);
978 curr = std::exchange(next, idx);
979 }
980 }
981 }
982
995 template<typename Compare, typename Sort = std_sort, typename... Args>
996 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
997 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
998 sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
999 }
1000
1014 template<typename It>
1015 iterator sort_as(It first, It last) {
1016 ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
1017 const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
1018 auto it = end() - static_cast<typename iterator::difference_type>(len);
1019
1020 for(const auto other = end(); (it != other) && (first != last); ++first) {
1021 if(const auto curr = *first; contains(curr)) {
1022 if(const auto entt = *it; entt != curr) {
1023 // basic no-leak guarantee (with invalid state) if swapping throws
1025 }
1026
1027 ++it;
1028 }
1029 }
1030
1031 return it;
1032 }
1033
1035 void clear() {
1036 pop_all();
1037 // sanity check to avoid subtle issues due to storage classes
1038 ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
1039 head = policy_to_head();
1040 packed.clear();
1041 }
1042
1048 return *info;
1049 }
1050
1057 template<typename Type>
1058 [[deprecated("avoid wrapping elements with basic_any")]] std::enable_if_t<std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
1059 bind(Type &&value) noexcept {
1060 // backward compatibility
1061 bind_any(std::forward<Type>(value));
1062 }
1063
1070 template<typename Type>
1071 std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
1072 bind(Type &&value) noexcept {
1073 bind_any(forward_as_any(std::forward<Type>(value)));
1074 }
1075
1076private:
1077 sparse_container_type sparse;
1078 packed_container_type packed;
1079 const type_info *info;
1080 deletion_policy mode;
1081 size_type head;
1082};
1083
1084} // namespace entt
1085
1086#endif
typename Traits::entity_type entity_type
Underlying entity type.
Definition entity.hpp:71
static constexpr entity_type version_mask
Version mask size.
Definition entity.hpp:78
static constexpr value_type next(const value_type value) noexcept
Returns the successor of a given identifier.
Definition entity.hpp:116
static constexpr entity_type to_integral(const value_type value) noexcept
Converts an entity to its underlying type.
Definition entity.hpp:85
static constexpr entity_type entity_mask
Entity mask size.
Definition entity.hpp:76
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
typename Traits::value_type value_type
Value type.
Definition entity.hpp:69
typename Traits::version_type version_type
Underlying version type.
Definition entity.hpp:73
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
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
Sparse set implementation.
void erase(const entity_type entt)
Erases an entity from a sparse set.
std::enable_if_t< std::is_same_v< std::remove_const_t< std::remove_reference_t< Type > >, basic_any<> > > bind(Type &&value) noexcept
Forwards variables to derived classes, if any.
std::enable_if_t<!std::is_same_v< std::remove_const_t< std::remove_reference_t< Type > >, basic_any<> > > bind(Type &&value) noexcept
Forwards variables to derived classes, if any.
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.
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.
iterator const_iterator
Constant random access iterator type.
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.
basic_sparse_set & operator=(const basic_sparse_set &)=delete
Default copy assignment operator, deleted on purpose.
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.
typename packed_container_type::const_pointer pointer
Pointer type to contained entities.
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.
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.
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.
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.
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.
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_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.
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 & type() const noexcept
Returned value type, if any.
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:22
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition memory.hpp:219
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:62
constexpr null_t null
Compile-time constant for null entities.
Definition entity.hpp:375
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition entity.hpp:384
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:17
@ 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.
@ ref
Aliasing mode, the object points to a non-const element.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:508
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Entity traits.
Definition entity.hpp:163
static constexpr std::size_t page_size
Page size, default is ENTT_SPARSE_PAGE.
Definition entity.hpp:167
Function object to wrap std::sort in a class type.
Definition algorithm.hpp:21
Implementation specific information about a type.