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/memory.hpp"
14 #include "../core/type_info.hpp"
27 template<
typename Container>
28 struct sparse_set_iterator final {
29 using value_type =
typename Container::value_type;
30 using pointer =
typename Container::const_pointer;
31 using reference =
typename Container::const_reference;
32 using difference_type =
typename Container::difference_type;
33 using iterator_category = std::random_access_iterator_tag;
35 sparse_set_iterator() ENTT_NOEXCEPT
39 sparse_set_iterator(
const Container &ref,
const difference_type idx) ENTT_NOEXCEPT
40 : packed{std::addressof(ref)},
43 sparse_set_iterator &operator++() ENTT_NOEXCEPT {
44 return --offset, *
this;
47 sparse_set_iterator operator++(
int) ENTT_NOEXCEPT {
48 sparse_set_iterator orig = *
this;
49 return ++(*this), orig;
52 sparse_set_iterator &operator--() ENTT_NOEXCEPT {
53 return ++offset, *
this;
56 sparse_set_iterator operator--(
int) ENTT_NOEXCEPT {
57 sparse_set_iterator orig = *
this;
58 return operator--(), orig;
61 sparse_set_iterator &operator+=(
const difference_type value) ENTT_NOEXCEPT {
66 sparse_set_iterator
operator+(
const difference_type value)
const ENTT_NOEXCEPT {
67 sparse_set_iterator copy = *
this;
68 return (copy += value);
71 sparse_set_iterator &operator-=(
const difference_type value) ENTT_NOEXCEPT {
72 return (*
this += -value);
75 sparse_set_iterator operator-(
const difference_type value)
const ENTT_NOEXCEPT {
76 return (*
this + -value);
79 [[nodiscard]] reference operator[](
const difference_type value)
const ENTT_NOEXCEPT {
80 return packed->data()[index() - value];
83 [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
84 return packed->data() + index();
87 [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
91 [[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
96 const Container *packed;
97 difference_type offset;
100 template<
typename Type,
typename Other>
101 [[nodiscard]] std::ptrdiff_t operator-(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
102 return rhs.index() - lhs.index();
105 template<
typename Type,
typename Other>
106 [[nodiscard]]
bool operator==(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
107 return lhs.index() == rhs.index();
110 template<
typename Type,
typename Other>
111 [[nodiscard]]
bool operator!=(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
112 return !(lhs == rhs);
115 template<
typename Type,
typename Other>
116 [[nodiscard]]
bool operator<(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
117 return lhs.index() > rhs.index();
120 template<
typename Type,
typename Other>
121 [[nodiscard]]
bool operator>(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
122 return lhs.index() < rhs.index();
125 template<
typename Type,
typename Other>
126 [[nodiscard]]
bool operator<=(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
130 template<
typename Type,
typename Other>
131 [[nodiscard]]
bool operator>=(
const sparse_set_iterator<Type> &lhs,
const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
173 template<
typename Entity,
typename Allocator>
175 using alloc_traits = std::allocator_traits<Allocator>;
176 static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>,
"Invalid value type");
177 using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
178 using packed_container_type = std::vector<Entity, Allocator>;
181 [[nodiscard]]
auto sparse_ptr(
const Entity
entt)
const {
187 [[nodiscard]]
auto &sparse_ref(
const Entity
entt)
const {
188 ENTT_ASSERT(sparse_ptr(
entt),
"Invalid element");
193 [[nodiscard]]
auto &assure_at_least(
const Entity
entt) {
197 if(!(page < sparse.size())) {
198 sparse.resize(page + 1u,
nullptr);
202 auto page_allocator{packed.get_allocator()};
212 void release_sparse_pages() {
213 auto page_allocator{packed.get_allocator()};
215 for(
auto &&page: sparse) {
216 if(page !=
nullptr) {
225 virtual const void *get_at(
const std::size_t)
const {
229 virtual void swap_at(
const std::size_t,
const std::size_t) {}
230 virtual void move_element(
const std::size_t,
const std::size_t) {}
242 for(; first != last; ++first) {
244 const auto entt = std::exchange(packed[first.index()], packed.back());
246 ENTT_ASSERT((packed.back() =
tombstone,
true),
"");
248 sparse_ref(
entt) =
null;
259 for(; first != last; ++first) {
260 sparse_ref(*first) =
null;
272 ENTT_ASSERT(!
contains(
entt),
"Set already contains entity");
274 if(
auto &elem = assure_at_least(
entt); free_list ==
null || force_back) {
275 packed.push_back(
entt);
281 free_list = std::exchange(packed[pos],
entt);
282 return --(
end() - pos);
294 using size_type =
typename packed_container_type::size_type;
296 using pointer =
typename packed_container_type::const_pointer;
344 : sparse{std::move(other.sparse)},
345 packed{std::move(other.packed)},
347 free_list{std::exchange(other.free_list,
tombstone)},
356 : sparse{std::move(other.sparse), allocator},
357 packed{std::move(other.packed), allocator},
359 free_list{std::exchange(other.free_list,
tombstone)},
361 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(),
"Copying a sparse set is not allowed");
366 release_sparse_pages();
375 ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(),
"Copying a sparse set is not allowed");
377 release_sparse_pages();
378 sparse = std::move(other.sparse);
379 packed = std::move(other.packed);
381 free_list = std::exchange(other.free_list,
tombstone);
392 swap(sparse, other.sparse);
393 swap(packed, other.packed);
394 swap(info, other.info);
395 swap(free_list, other.free_list);
396 swap(mode, other.mode);
404 return packed.get_allocator();
433 return packed.capacity();
438 packed.shrink_to_fit();
466 return packed.size();
473 [[nodiscard]]
bool empty() const ENTT_NOEXCEPT {
474 return packed.empty();
482 return packed.data();
495 const auto pos =
static_cast<typename iterator::difference_type
>(packed.size());
534 return std::make_reverse_iterator(
end());
553 return std::make_reverse_iterator(
begin());
577 const auto elem = sparse_ptr(
entt);
590 const auto elem = sparse_ptr(
entt);
606 ENTT_ASSERT(
contains(
entt),
"Set does not contain entity");
616 return pos < packed.size() ? packed[pos] :
null;
625 ENTT_ASSERT(pos < packed.size(),
"Position is out of bounds");
645 return const_cast<void *
>(std::as_const(*this).get(
entt));
692 template<
typename It>
694 for(
auto it = first; it != last; ++it) {
698 return first == last ?
end() :
find(*first);
724 template<
typename It>
726 if constexpr(std::is_same_v<It, basic_iterator>) {
729 for(; first != last; ++first) {
751 template<
typename It>
755 for(; first != last; ++first) {
765 for(; from && packed[from - 1u] ==
tombstone; --from) {}
770 move_element(from, to);
773 swap(packed[from], packed[to]);
778 for(; from && packed[from - 1u] ==
tombstone; --from) {}
800 ENTT_ASSERT(
contains(lhs) &&
contains(rhs),
"Set does not contain entities");
802 auto &
entt = sparse_ref(lhs);
803 auto &other = sparse_ref(rhs);
814 swap(packed[from], packed[to]);
847 template<
typename Compare,
typename Sort =
std_sort,
typename... Args>
848 void sort_n(
const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
849 ENTT_ASSERT(!(length > packed.size()),
"Length exceeds the number of elements");
850 ENTT_ASSERT(free_list ==
null,
"Partial sorting with tombstones is not supported");
852 algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
854 for(
size_type pos{}; pos < length; ++pos) {
856 auto next =
index(packed[curr]);
858 while(curr != next) {
859 const auto idx =
index(packed[next]);
860 const auto entt = packed[curr];
865 curr = std::exchange(next, idx);
882 template<
typename Compare,
typename Sort = std_sort,
typename... Args>
883 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
885 sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
906 const auto to = other.
end();
907 auto from = other.
begin();
909 for(
size_type pos = packed.size() - 1; pos && from != to; ++from) {
911 if(*from != packed[pos]) {
923 if(
const auto last =
end(); free_list ==
null) {
926 for(
auto &&
entity: *
this) {
928 if(
const auto it =
find(
entity); it != last) {
950 sparse_container_type sparse;
951 packed_container_type packed;
Basic sparse set implementation.
deletion_policy policy() const
Returns the deletion policy of a sparse set.
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.
const_reverse_iterator crbegin() const
Returns a reverse iterator to the beginning.
size_type index(const entity_type entt) const
Returns the position of an entity in a sparse set.
basic_sparse_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
reverse_iterator rend() const
Returns a reverse iterator to the end.
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void *=nullptr)
Assigns an entity to a sparse set.
void erase(It first, It last)
Erases entities from a set.
const_reverse_iterator crend() const
Returns a reverse iterator to the end.
void swap_elements(const entity_type lhs, const entity_type rhs)
Swaps two entities in a sparse set.
iterator end() const
Returns an iterator to the end.
bool empty() const
Checks whether a sparse set is empty.
iterator const_iterator
Constant random access iterator type.
entity_type operator[](const size_type pos) const
Returns the entity at specified location, without bounds checking.
virtual void bind(any)
Forwards variables to mixins, if any.
basic_sparse_set(const type_info &value, deletion_policy pol=deletion_policy::swap_and_pop, const allocator_type &allocator={})
Constructs an empty container with the given value type, policy and allocator.
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.
reverse_iterator const_reverse_iterator
Constant reverse iterator type.
version_type current(const entity_type entt) const
Returns the contained version for an identifier.
virtual void reserve(const size_type cap)
Increases the capacity of a sparse set.
iterator emplace(const entity_type entt, const void *value=nullptr)
Assigns an entity to a sparse set.
const void * get(const entity_type entt) const
Returns the element assigned to an entity, if any.
void clear()
Clears a sparse set.
basic_iterator iterator
Random access iterator type.
typename packed_container_type::size_type size_type
Unsigned integer type.
size_type size() const
Returns the number of elements in a sparse set.
virtual void shrink_to_fit()
Requests the removal of unused capacity.
virtual void swap_and_pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
iterator find(const entity_type entt) const
Finds an entity.
typename packed_container_type::const_pointer pointer
Pointer type to contained entities.
basic_sparse_set(deletion_policy pol, const allocator_type &allocator={})
Constructs an empty container with the given policy and allocator.
typename entity_traits::version_type version_type
Underlying version type.
Entity entity_type
Underlying entity identifier.
iterator insert(It first, It last)
Assigns one or more entities to a sparse set.
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
Allocator-extended move constructor.
size_type extent() const
Returns the extent of a sparse set.
entity_type at(const size_type pos) const
Returns the entity at specified location, with bounds checking.
const_iterator begin() const
Returns an iterator to the beginning.
const_iterator cbegin() const
Returns an iterator to the beginning.
size_type remove(It first, It last)
Removes entities from a sparse set if they exist.
basic_sparse_set(basic_sparse_set &&other)
Move constructor.
const type_info & type() const
Returned value type, if any.
virtual size_type capacity() const
Returns the number of elements that a sparse set has currently allocated space for.
internal::sparse_set_iterator< packed_container_type > basic_iterator
Random access iterator type.
virtual ~basic_sparse_set()
Default destructor.
bool contains(const entity_type entt) const
Checks if a sparse set contains an entity.
void respect(const basic_sparse_set &other)
Sort entities according to their order in another sparse set.
pointer data() const
Direct access to the internal packed array.
void bump(const entity_type entt)
Bump the version number of an entity.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort all elements according to the given comparison function.
const_reverse_iterator rbegin() const
Returns a reverse iterator to the beginning.
const_iterator cend() const
Returns an iterator to the end.
bool remove(const entity_type entt)
Removes an entity from a sparse set if it exists.
void * get(const entity_type entt)
Returns the element assigned to an entity, if any.
void compact()
Removes all tombstones from the packed array of a sparse set.
Allocator allocator_type
Allocator type.
basic_sparse_set()
Default constructor.
basic_sparse_set & operator=(basic_sparse_set &&other)
Move assignment operator.
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
virtual void in_place_pop(basic_iterator first, basic_iterator last)
Erases entities from a sparse set.
static constexpr auto page_size
Page size, default is ENTT_SPARSE_PAGE.
typename base_type::entity_type entity_type
Underlying entity type.
static constexpr entity_type reserved
Reserved identifier.
static constexpr entity_type to_integral(const value_type value)
Converts an entity to its underlying type.
static constexpr entity_type to_entity(const value_type value)
Returns the entity part once converted to the underlying type.
static constexpr value_type combine(const entity_type lhs, const entity_type rhs)
Combines two identifiers in a single one.
typename base_type::version_type version_type
Underlying version type.
static constexpr version_type to_version(const value_type value)
Returns the version part once converted to the underlying type.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
entity
Default entity identifier.
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod)
Fast module utility function (powers of two only).
const type_info & type_id()
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)
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
deletion_policy
Sparse set deletion policy.
@ swap_and_pop
Swap-and-pop deletion policy.
@ in_place
In-place deletion policy.
bool operator!=(const basic_any< Len, Align > &lhs, const basic_any< Len, Align > &rhs)
Checks if two wrappers differ in their content.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
Function object to wrap std::sort in a class type.
Implementation specific information about a type.