1#ifndef ENTT_ENTITY_SPARSE_SET_HPP
2#define ENTT_ENTITY_SPARSE_SET_HPP
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"
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;
31 constexpr sparse_set_iterator() noexcept
35 constexpr sparse_set_iterator(
const Container &ref,
const difference_type idx) noexcept
39 constexpr sparse_set_iterator &operator++() noexcept {
40 return --offset, *
this;
43 constexpr sparse_set_iterator operator++(
int)
noexcept {
44 const sparse_set_iterator orig = *
this;
45 return ++(*this), orig;
48 constexpr sparse_set_iterator &operator--() noexcept {
49 return ++offset, *
this;
52 constexpr sparse_set_iterator operator--(
int)
noexcept {
53 const sparse_set_iterator orig = *
this;
54 return operator--(), orig;
57 constexpr sparse_set_iterator &operator+=(
const difference_type value)
noexcept {
62 constexpr sparse_set_iterator
operator+(
const difference_type value)
const noexcept {
63 sparse_set_iterator copy = *
this;
64 return (copy += value);
67 constexpr sparse_set_iterator &operator-=(
const difference_type value)
noexcept {
68 return (*
this += -value);
71 constexpr sparse_set_iterator operator-(
const difference_type value)
const noexcept {
72 return (*
this + -value);
75 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
76 return (*packed)[
static_cast<typename Container::size_type
>(index() - value)];
79 [[nodiscard]]
constexpr pointer operator->() const noexcept {
80 return std::addressof(
operator[](0));
83 [[nodiscard]]
constexpr reference operator*() const noexcept {
87 [[nodiscard]]
constexpr pointer data() const noexcept {
88 return packed ? packed->data() :
nullptr;
91 [[nodiscard]]
constexpr difference_type index() const noexcept {
96 const Container *packed;
97 difference_type offset;
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();
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();
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);
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();
120template<
typename Container>
121[[nodiscard]]
constexpr bool operator>(
const sparse_set_iterator<Container> &lhs,
const sparse_set_iterator<Container> &rhs)
noexcept {
125template<
typename Container>
126[[nodiscard]]
constexpr bool operator<=(
const sparse_set_iterator<Container> &lhs,
const sparse_set_iterator<Container> &rhs)
noexcept {
130template<
typename Container>
131[[nodiscard]]
constexpr bool operator>=(
const sparse_set_iterator<Container> &lhs,
const sparse_set_iterator<Container> &rhs)
noexcept {
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>;
168 [[nodiscard]] std::size_t policy_to_head()
const noexcept {
172 [[nodiscard]]
auto entity_to_pos(
const Entity
entt)
const noexcept {
176 [[nodiscard]]
auto pos_to_page(
const std::size_t pos)
const noexcept {
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);
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);
192 [[nodiscard]]
auto to_iterator(
const Entity
entt)
const {
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);
200 if(!(page < sparse.size())) {
201 sparse.resize(page + 1u,
nullptr);
206 auto page_allocator{packed.get_allocator()};
214 void release_sparse_pages() {
215 auto page_allocator{packed.get_allocator()};
217 for(
auto &&page: sparse) {
218 if(page !=
nullptr) {
226 void swap_at(
const std::size_t lhs,
const std::size_t rhs) {
227 auto &from = packed[lhs];
228 auto &to = packed[rhs];
237 [[nodiscard]]
virtual const void *get_at(
const std::size_t)
const {
241 virtual void swap_or_move([[maybe_unused]]
const std::size_t lhs, [[maybe_unused]]
const std::size_t rhs) {
255 const auto pos =
index(*it);
257 swap_at(pos, head -= (pos < head));
266 auto &self = sparse_ref(*it);
272 ENTT_ASSERT((packed.back() =
null,
true),
"");
284 const auto pos = entity_to_pos(std::exchange(sparse_ref(*it),
null));
296 for(; first != last; ++first) {
301 for(; first != last; ++first) {
306 for(; first != last; ++first) {
317 if(head != max_size) {
318 for(
auto &&elem: packed) {
320 sparse_ref(elem) =
null;
328 for(
auto &&elem: packed) {
329 sparse_ref(elem) =
null;
334 head = policy_to_head();
346 auto &elem = assure_at_least(
entt);
351 if(head != max_size && !force_back) {
353 ENTT_ASSERT(elem ==
null,
"Slot not available");
355 head = entity_to_pos(std::exchange(packed[pos],
entt));
360 packed.push_back(
entt);
361 ENTT_ASSERT(elem ==
null,
"Slot not available");
366 packed.push_back(
entt);
369 ENTT_ASSERT(!(entity_to_pos(elem) < head),
"Slot not available");
374 swap_at(entity_to_pos(elem), pos);
397 using pointer =
typename packed_container_type::const_pointer;
438 head{policy_to_head()} {
450 : sparse{std::move(other.sparse)},
451 packed{std::move(other.packed)},
454 head{std::exchange(other.head, policy_to_head())} {}
462 : sparse{std::move(other.sparse), allocator},
463 packed{std::move(other.packed), allocator},
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");
472 release_sparse_pages();
487 ENTT_ASSERT(alloc_traits::is_always_equal::value ||
get_allocator() == other.get_allocator(),
"Copying a sparse set is not allowed");
498 swap(sparse, other.sparse);
499 swap(packed, other.packed);
500 swap(info, other.info);
501 swap(mode, other.mode);
502 swap(head, other.head);
510 return packed.get_allocator();
556 return packed.capacity();
561 sparse_container_type other{sparse.get_allocator()};
562 const auto len = sparse.size();
567 for(
auto &&elem: std::as_const(packed)) {
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);
574 other[page] = std::exchange(sparse[page],
nullptr);
584 release_sparse_pages();
587 sparse.shrink_to_fit();
588 packed.shrink_to_fit();
615 return packed.size();
622 [[nodiscard]]
bool empty() const noexcept {
623 return packed.empty();
639 return packed.data();
684 return std::make_reverse_iterator(
end());
698 return std::make_reverse_iterator(
begin());
722 const auto *elem = sparse_ptr(
entt);
736 const auto *elem = sparse_ptr(
entt);
752 ENTT_ASSERT(
contains(
entt),
"Set does not contain entity");
753 return entity_to_pos(sparse_ref(
entt));
762 ENTT_ASSERT(pos < packed.size(),
"Index out of bounds");
782 return const_cast<void *
>(std::as_const(*this).value(
entt));
814 template<
typename It>
818 for(; first != last; ++first) {
836 auto &elem = sparse_ref(
entt);
837 ENTT_ASSERT(
entt !=
null && elem !=
tombstone,
"Cannot set the required version");
839 packed[entity_to_pos(elem)] =
entt;
853 const auto it = to_iterator(
entt);
866 template<
typename It>
868 if constexpr(std::is_same_v<It, basic_iterator>) {
871 for(; first != last; ++first) {
893 template<
typename It>
897 if constexpr(std::is_same_v<It, basic_iterator>) {
898 while(first != last) {
899 while(first != last && !
contains(*first)) {
903 const auto it = first;
905 while(first != last &&
contains(*first)) {
909 count +=
static_cast<size_type>(std::distance(it, first));
913 for(; first != last; ++first) {
925 size_type pos = std::exchange(head, max_size);
927 for(; from && packed[from - 1u] ==
tombstone; --from) {}
929 while(pos != max_size) {
930 if(
const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) {
932 swap_or_move(from, to);
934 packed[to] = packed[from];
938 for(; from && packed[from - 1u] ==
tombstone; --from) {}
942 packed.erase(packed.begin() +
static_cast<difference_type>(from), packed.end());
960 const auto from =
index(lhs);
961 const auto to =
index(rhs);
964 swap_or_move(from, to);
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) {
1001 ENTT_ASSERT(!(length > packed.size()),
"Length exceeds the number of elements");
1003 algo(packed.rend() -
static_cast<difference_type>(length), packed.rend(), std::move(compare), std::forward<Args>(args)...);
1005 for(
size_type pos{}; pos < length; ++pos) {
1007 auto next =
index(packed[curr]);
1009 while(curr != next) {
1010 const auto idx =
index(packed[next]);
1011 const auto entt = packed[curr];
1013 swap_or_move(next, idx);
1016 curr = std::exchange(next, idx);
1033 template<
typename Compare,
typename Sort = std_sort,
typename... Args>
1034 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1036 sort_n(len, std::move(compare), std::move(algo), std::forward<Args>(args)...);
1052 template<
typename It>
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) {
1076 ENTT_ASSERT((
compact(),
size()) == 0u,
"Non-empty set");
1077 head = policy_to_head();
1094 template<
typename Type>
1100 sparse_container_type sparse;
1101 packed_container_type packed;
typename Traits::entity_type entity_type
Underlying entity type.
static constexpr entity_type version_mask
Version mask size.
static constexpr value_type next(const value_type value) noexcept
Returns the successor of a given identifier.
static constexpr entity_type to_integral(const value_type value) noexcept
Converts an entity to its underlying type.
static constexpr entity_type entity_mask
Entity mask size.
static constexpr entity_type to_entity(const value_type value) noexcept
Returns the entity part once converted to the underlying type.
typename Traits::value_type value_type
Value type.
typename Traits::version_type version_type
Underlying version type.
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
Combines two identifiers in a single one.
static constexpr version_type to_version(const value_type value) noexcept
Returns the version part once converted to the underlying type.
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.
typename traits_type::version_type 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
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.
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.
std::ptrdiff_t difference_type
Signed integer type.
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.
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
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
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
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.
basic_sparse_set()
Default constructor.
std::reverse_iterator< iterator > reverse_iterator
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).
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 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.
@ 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.
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
Function object to wrap std::sort in a class type.
Implementation specific information about a type.