1#ifndef ENTT_ENTITY_SPARSE_SET_HPP
2#define ENTT_ENTITY_SPARSE_SET_HPP
5#include "../config/config.h"
6#include "../core/algorithm.hpp"
7#include "../core/any.hpp"
8#include "../core/bit.hpp"
9#include "../core/type_info.hpp"
10#include "../stl/concepts.hpp"
11#include "../stl/cstddef.hpp"
12#include "../stl/iterator.hpp"
13#include "../stl/memory.hpp"
14#include "../stl/type_traits.hpp"
15#include "../stl/utility.hpp"
16#include "../stl/vector.hpp"
25template<
typename Container>
26struct sparse_set_iterator final {
27 using value_type = Container::value_type;
28 using pointer = Container::const_pointer;
29 using reference = Container::const_reference;
30 using difference_type = Container::difference_type;
31 using iterator_category = stl::random_access_iterator_tag;
33 constexpr sparse_set_iterator() noexcept
37 constexpr sparse_set_iterator(
const Container &ref,
const difference_type idx) noexcept
41 constexpr sparse_set_iterator &operator++() noexcept {
42 return --offset, *
this;
45 constexpr sparse_set_iterator operator++(
int)
noexcept {
46 const sparse_set_iterator orig = *
this;
47 return ++(*this), orig;
50 constexpr sparse_set_iterator &operator--() noexcept {
51 return ++offset, *
this;
54 constexpr sparse_set_iterator operator--(
int)
noexcept {
55 const sparse_set_iterator orig = *
this;
56 return operator--(), orig;
59 constexpr sparse_set_iterator &operator+=(
const difference_type value)
noexcept {
64 constexpr sparse_set_iterator
operator+(
const difference_type value)
const noexcept {
65 sparse_set_iterator copy = *
this;
66 return (copy += value);
69 constexpr sparse_set_iterator &operator-=(
const difference_type value)
noexcept {
70 return (*
this += -value);
73 constexpr sparse_set_iterator operator-(
const difference_type value)
const noexcept {
74 return (*
this + -value);
77 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
78 return (*packed)[
static_cast<Container::size_type
>(index() - value)];
81 [[nodiscard]]
constexpr pointer operator->() const noexcept {
82 return stl::addressof(
operator[](0));
85 [[nodiscard]]
constexpr reference operator*() const noexcept {
89 [[nodiscard]]
constexpr stl::ptrdiff_t operator-(
const sparse_set_iterator &other)
const noexcept {
91 return other.offset - offset;
94 [[nodiscard]]
constexpr bool operator==(
const sparse_set_iterator &other)
const noexcept {
95 return offset == other.offset;
98 [[nodiscard]]
constexpr auto operator<=>(
const sparse_set_iterator &other)
const noexcept {
100 return other.offset <=> offset;
103 [[nodiscard]]
constexpr pointer data() const noexcept {
104 return packed ? packed->data() :
nullptr;
107 [[nodiscard]]
constexpr difference_type index() const noexcept {
112 const Container *packed;
113 difference_type offset;
138template<
typename Entity,
typename Allocator>
140 using alloc_traits = stl::allocator_traits<Allocator>;
141 static_assert(stl::is_same_v<typename alloc_traits::value_type, Entity>,
"Invalid value type");
142 using sparse_container_type = stl::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
143 using packed_container_type = stl::vector<Entity, Allocator>;
149 [[nodiscard]] stl::size_t policy_to_head()
const noexcept {
153 [[nodiscard]]
auto entity_to_pos(
const Entity
entt)
const noexcept {
157 [[nodiscard]]
auto pos_to_page(
const stl::size_t pos)
const noexcept {
161 [[nodiscard]]
auto sparse_ptr(
const Entity
entt)
const {
162 const auto pos = entity_to_pos(
entt);
163 const auto page = pos_to_page(pos);
167 [[nodiscard]]
auto &sparse_ref(
const Entity
entt)
const {
168 ENTT_ASSERT(sparse_ptr(
entt),
"Invalid element");
169 const auto pos = entity_to_pos(
entt);
173 [[nodiscard]]
auto to_iterator(
const Entity
entt)
const {
177 [[nodiscard]]
auto &assure_at_least(
const Entity
entt) {
178 const auto pos = entity_to_pos(
entt);
179 const auto page = pos_to_page(pos);
181 if(!(page < sparse.size())) {
182 sparse.resize(page + 1u,
nullptr);
187 auto page_allocator{packed.get_allocator()};
195 void release_sparse_pages() {
196 for(
auto page_allocator{packed.get_allocator()};
auto &&page: sparse) {
197 if(page !=
nullptr) {
205 void swap_at(
const stl::size_t lhs,
const stl::size_t rhs) {
206 auto &from = packed[lhs];
207 auto &to = packed[rhs];
216 [[nodiscard]]
virtual const void *get_at(
const stl::size_t)
const {
220 virtual void swap_or_move([[maybe_unused]]
const stl::size_t lhs, [[maybe_unused]]
const stl::size_t rhs) {
236 swap_at(pos, head -= (pos < head));
245 auto &self = sparse_ref(
entt);
248 packed[
static_cast<size_type>(pos)] = packed.back();
251 ENTT_ASSERT((packed.back() =
null,
true),
"");
263 const auto pos = entity_to_pos(stl::exchange(sparse_ref(
entt),
null));
275 for(; first != last; ++first) {
280 for(; first != last; ++first) {
285 for(; first != last; ++first) {
294 if(!packed.empty()) {
296 for(
auto &&elem: sparse) {
305 head = policy_to_head();
317 auto &elem = assure_at_least(
entt);
322 if(head != max_size && !force_back) {
324 ENTT_ASSERT(elem ==
null,
"Slot not available");
326 head = entity_to_pos(stl::exchange(packed[pos],
entt));
331 packed.push_back(
entt);
332 ENTT_ASSERT(elem ==
null,
"Slot not available");
337 packed.push_back(
entt);
340 ENTT_ASSERT(!(entity_to_pos(elem) < head),
"Slot not available");
345 swap_at(entity_to_pos(elem), pos);
368 using pointer = packed_container_type::const_pointer;
409 head{policy_to_head()} {
421 : sparse{stl::move(other.sparse)},
422 packed{stl::move(other.packed)},
423 descriptor{other.descriptor},
425 head{stl::exchange(other.head, policy_to_head())} {}
433 : sparse{
stl::move(other.sparse), allocator},
434 packed{
stl::move(other.packed), allocator},
435 descriptor{other.descriptor},
437 head{
stl::exchange(other.head, policy_to_head())} {
438 ENTT_ASSERT(alloc_traits::is_always_equal::value ||
get_allocator() == other.get_allocator(),
"Copying a sparse set is not allowed");
443 release_sparse_pages();
458 ENTT_ASSERT(alloc_traits::is_always_equal::value ||
get_allocator() == other.get_allocator(),
"Copying a sparse set is not allowed");
469 swap(sparse, other.sparse);
470 swap(packed, other.packed);
471 swap(descriptor, other.descriptor);
472 swap(mode, other.mode);
473 swap(head, other.head);
481 return packed.get_allocator();
527 return packed.capacity();
532 sparse_container_type other{sparse.get_allocator()};
533 const auto len = sparse.size();
536 for(
size_type cnt{};
auto &&elem: stl::as_const(packed)) {
538 if(
const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] !=
nullptr) {
539 if(
const auto sz = page + 1u; sz > other.size()) {
540 other.resize(sz,
nullptr);
543 other[page] = stl::exchange(sparse[page],
nullptr);
553 release_sparse_pages();
556 sparse.shrink_to_fit();
557 packed.shrink_to_fit();
584 return packed.size();
591 [[nodiscard]]
bool empty() const noexcept {
592 return packed.empty();
608 return packed.data();
653 return stl::make_reverse_iterator(
end());
667 return stl::make_reverse_iterator(
begin());
691 const auto *elem = sparse_ptr(
entt);
705 const auto *elem = sparse_ptr(
entt);
721 ENTT_ASSERT(
contains(
entt),
"Set does not contain entity");
722 return entity_to_pos(sparse_ref(
entt));
731 ENTT_ASSERT(pos < packed.size(),
"Index out of bounds");
751 return const_cast<void *
>(stl::as_const(*this).value(
entt));
782 iterator push(stl::input_iterator
auto first, stl::input_iterator
auto last) {
785 for(; first != last; ++first) {
803 auto &elem = sparse_ref(
entt);
804 ENTT_ASSERT(
entt !=
null && elem !=
tombstone,
"Cannot set the required version");
806 packed[entity_to_pos(elem)] =
entt;
820 const auto it = to_iterator(
entt);
833 template<stl::input_iterator It>
835 if constexpr(stl::is_same_v<It, basic_iterator>) {
838 for(; first != last; ++first) {
860 template<stl::input_iterator It>
864 if constexpr(stl::is_same_v<It, basic_iterator>) {
865 while(first != last) {
866 while(first != last && !
contains(*first)) {
870 const auto it = first;
872 while(first != last &&
contains(*first)) {
876 count +=
static_cast<size_type>(stl::distance(it, first));
880 for(; first != last; ++first) {
892 size_type pos = stl::exchange(head, max_size);
894 for(; from && packed[from - 1u] ==
tombstone; --from) {}
896 while(pos != max_size) {
897 if(
const auto to = stl::exchange(pos, entity_to_pos(packed[pos])); to < from) {
899 swap_or_move(from, to);
901 packed[to] = packed[from];
905 for(; from && packed[from - 1u] ==
tombstone; --from) {}
909 packed.erase(packed.begin() +
static_cast<difference_type>(from), packed.end());
927 const auto from =
index(lhs);
928 const auto to =
index(rhs);
931 swap_or_move(from, to);
965 template<
typename Compare,
typename Sort =
std_sort,
typename... Args>
966 void sort_n(
const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
968 ENTT_ASSERT(!(length > packed.size()),
"Length exceeds the number of elements");
970 algo(packed.rend() -
static_cast<difference_type>(length), packed.rend(), stl::move(compare), stl::forward<Args>(args)...);
972 for(
size_type pos{}; pos < length; ++pos) {
974 auto next =
index(packed[curr]);
976 while(curr != next) {
977 const auto idx =
index(packed[next]);
978 const auto entt = packed[curr];
980 swap_or_move(next, idx);
983 curr = stl::exchange(next, idx);
1000 template<
typename Compare,
typename Sort = std_sort,
typename... Args>
1001 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1003 sort_n(len, stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
1019 template<stl::input_iterator It>
1025 for(
const auto other =
end(); (it != other) && (first != last); ++first) {
1026 if(
const auto curr = *first;
contains(curr)) {
1027 if(
const auto entt = *it;
entt != curr) {
1043 ENTT_ASSERT((
compact(),
size()) == 0u,
"Non-empty set");
1044 head = policy_to_head();
1061 template<
typename Type>
1067 sparse_container_type sparse;
1068 packed_container_type packed;
static constexpr entity_type version_mask
static constexpr value_type next(const value_type value) noexcept
static constexpr entity_type to_integral(const value_type value) noexcept
static constexpr entity_type entity_mask
internal::entt_traits< Type >::entity_type entity_type
static constexpr entity_type to_entity(const value_type value) noexcept
internal::entt_traits< Type >::value_type value_type
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
internal::entt_traits< Type >::version_type version_type
static constexpr version_type to_version(const value_type value) noexcept
Sparse set implementation.
void erase(const entity_type entt)
Erases an entity from a sparse set.
iterator begin() const noexcept
Returns an iterator to the beginning.
virtual size_type capacity() const noexcept
Returns the number of elements that a sparse set has currently allocated space for.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
basic_sparse_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void *=nullptr)
Assigns an entity to a sparse set.
virtual void pop_all()
Erases all entities of a sparse set.
void erase(It first, It last)
Erases entities from a set.
traits_type::value_type entity_type
Underlying entity identifier.
packed_container_type::const_pointer pointer
void swap_elements(const entity_type lhs, const entity_type rhs)
Swaps two entities in a sparse set.
iterator end() const noexcept
Returns an iterator to the end.
void * value(const entity_type entt) noexcept
Returns the element assigned to an entity, if any.
reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
void sort_n(const size_type length, Compare compare, Sort algo=Sort{}, Args &&...args)
Sort the first count elements according to the given comparison function.
pointer data() const noexcept
Direct access to the internal packed array.
basic_sparse_set & operator=(basic_sparse_set &&other) noexcept
Move assignment operator.
virtual void reserve(const size_type cap)
Increases the capacity of a sparse set.
void clear()
Clears a sparse set.
void bind(Type &&value) noexcept
Forwards variables to derived classes, if any.
size_type size() const noexcept
Returns the number of elements in a sparse set.
stl::reverse_iterator< const_iterator > const_reverse_iterator
basic_sparse_set & operator=(const basic_sparse_set &)=delete
Default copy assignment operator, deleted on purpose.
void swap_only(const Entity entt)
Erases an entity from a sparse set.
bool contiguous() const noexcept
Checks whether a sparse set is fully packed.
version_type bump(const entity_type entt)
Bump the version number of an entity.
virtual void shrink_to_fit()
Requests the removal of unused capacity.
size_type extent() const noexcept
Returns the extent of a sparse set.
void swap(basic_sparse_set &other) noexcept
Exchanges the contents with those of a given sparse set.
const_iterator find(const entity_type entt) const noexcept
Finds an entity.
iterator push(const entity_type entt, const void *elem=nullptr)
Assigns an entity to a sparse set.
void in_place_pop(const Entity entt)
Erases an entity from a sparse set.
basic_sparse_set(deletion_policy pol, const allocator_type &allocator={})
Constructs an empty container with the given policy and allocator.
virtual void pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
deletion_policy policy() const noexcept
Returns the deletion policy of a sparse set.
iterator push(stl::input_iterator auto first, stl::input_iterator auto last)
Assigns one or more entities to a sparse set.
virtual void bind_any(any) noexcept
Forwards variables to derived classes, if any.
version_type current(const entity_type entt) const noexcept
Returns the contained version for an identifier.
bool contains(const entity_type entt) const noexcept
Checks if a sparse set contains an entity.
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
Allocator-extended move constructor.
stl::ptrdiff_t difference_type
Signed integer type.
iterator sort_as(It first, It last)
Sort entities according to their order in a range.
const_iterator cend() const noexcept
Returns an iterator to the end.
stl::reverse_iterator< iterator > reverse_iterator
basic_sparse_set(basic_sparse_set &&other) noexcept
Move constructor.
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the end.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
size_type remove(It first, It last)
Removes entities from a sparse set if they exist.
const void * value(const entity_type entt) const noexcept
reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
entity_type operator[](const size_type pos) const noexcept
Returns the entity at specified location.
size_type free_list() const noexcept
Returns data on the free list whose meaning depends on the mode.
void swap_and_pop(const Entity entt)
Erases an entity from a sparse set.
stl::size_t size_type
Unsigned integer type.
internal::sparse_set_iterator< packed_container_type > basic_iterator
virtual ~basic_sparse_set()
Default destructor.
void free_list(const size_type value) noexcept
Sets data on the free list whose meaning depends on the mode.
basic_sparse_set(const basic_sparse_set &)=delete
Default copy constructor, deleted on purpose.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
bool empty() const noexcept
Checks whether a sparse set is empty.
size_type index(const entity_type entt) const noexcept
Returns the position of an entity in a sparse set.
const type_info & info() const noexcept
Returns a type info object for the value type, if any.
traits_type::version_type version_type
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort all elements according to the given comparison function.
bool remove(const entity_type entt)
Removes an entity from a sparse set if it exists.
void compact()
Removes all tombstones from a sparse set.
basic_sparse_set(const type_info &elem, deletion_policy pol=deletion_policy::swap_and_pop, const allocator_type &allocator={})
Constructs an empty container with the given value type, policy and allocator.
basic_sparse_set()
Default constructor.
Custom EnTT namespace for the standard template library.
deletion_policy
Storage deletion policy.
@ swap_only
Swap-only deletion policy.
@ swap_and_pop
Swap-and-pop deletion policy.
@ in_place
In-place deletion policy.
constexpr null_t null
Compile-time constant for null entities.
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
basic_any<> any
Alias declaration for the most common use case.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
@ ref
Aliasing mode, non-const reference.
const type_info & type_id() noexcept
Returns the type info object associated to a given type.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
constexpr Type fast_mod(const Type value, const stl::size_t mod) noexcept
Fast module utility function (powers of two only).
static constexpr stl::size_t page_size
Function object to wrap stl::sort in a class type.
Implementation specific information about a type.