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/fwd.hpp"
52 template<
typename Entity,
typename Allocator>
54 static constexpr
auto growth_factor = 1.5;
55 static constexpr
auto sparse_page = ENTT_SPARSE_PAGE;
59 using alloc_traits =
typename std::allocator_traits<Allocator>::template rebind_traits<Entity>;
60 using alloc_pointer =
typename alloc_traits::pointer;
61 using alloc_const_pointer =
typename alloc_traits::const_pointer;
63 using bucket_alloc_traits =
typename std::allocator_traits<Allocator>::template rebind_traits<alloc_pointer>;
64 using bucket_alloc_pointer =
typename bucket_alloc_traits::pointer;
66 static_assert(alloc_traits::propagate_on_container_move_assignment::value);
67 static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value);
69 struct sparse_set_iterator final {
71 using value_type = Entity;
72 using pointer =
const value_type *;
73 using reference =
const value_type &;
74 using iterator_category = std::random_access_iterator_tag;
76 sparse_set_iterator() ENTT_NOEXCEPT =
default;
78 sparse_set_iterator(
const alloc_const_pointer *ref,
const difference_type idx) ENTT_NOEXCEPT
83 sparse_set_iterator & operator++() ENTT_NOEXCEPT {
84 return --
index, *
this;
87 sparse_set_iterator operator++(
int) ENTT_NOEXCEPT {
89 return ++(*this), orig;
92 sparse_set_iterator & operator--() ENTT_NOEXCEPT {
93 return ++
index, *
this;
96 sparse_set_iterator operator--(
int) ENTT_NOEXCEPT {
97 sparse_set_iterator orig = *
this;
98 return operator--(), orig;
101 sparse_set_iterator & operator+=(
const difference_type value) ENTT_NOEXCEPT {
106 sparse_set_iterator
operator+(
const difference_type value)
const ENTT_NOEXCEPT {
107 sparse_set_iterator copy = *
this;
108 return (copy += value);
111 sparse_set_iterator & operator-=(
const difference_type value) ENTT_NOEXCEPT {
112 return (*
this += -value);
115 sparse_set_iterator operator-(
const difference_type value)
const ENTT_NOEXCEPT {
116 return (*
this + -value);
119 difference_type operator-(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
120 return other.index -
index;
123 [[nodiscard]] reference
operator[](
const difference_type value)
const {
125 return (*packed)[pos];
128 [[nodiscard]]
bool operator==(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
129 return other.index ==
index;
132 [[nodiscard]]
bool operator!=(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
133 return !(*
this == other);
136 [[nodiscard]]
bool operator<(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
137 return index > other.index;
140 [[nodiscard]]
bool operator>(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
141 return index < other.index;
144 [[nodiscard]]
bool operator<=(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
145 return !(*
this > other);
148 [[nodiscard]]
bool operator>=(
const sparse_set_iterator &other)
const ENTT_NOEXCEPT {
149 return !(*
this < other);
152 [[nodiscard]]
pointer operator->()
const {
154 return std::addressof((*packed)[pos]);
157 [[nodiscard]] reference operator*()
const {
158 return *operator->();
162 const alloc_const_pointer *packed;
163 difference_type
index;
166 [[nodiscard]]
static auto page(
const Entity
entt) ENTT_NOEXCEPT {
170 [[nodiscard]]
static auto offset(
const Entity
entt) ENTT_NOEXCEPT {
174 [[nodiscard]]
auto assure_page(
const std::size_t idx) {
175 if(!(idx < bucket)) {
177 const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz);
179 std::uninitialized_value_construct(mem + bucket, mem + sz);
180 std::uninitialized_copy(sparse, sparse + bucket, mem);
182 std::destroy(sparse, sparse + bucket);
183 bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket);
190 sparse[idx] = alloc_traits::allocate(allocator, sparse_page);
191 std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page,
null);
197 void resize_packed(
const std::size_t req) {
198 ENTT_ASSERT((req != reserved) && !(req < count),
"Invalid request");
199 const auto mem = alloc_traits::allocate(allocator, req);
201 std::uninitialized_copy(packed, packed + count, mem);
202 std::uninitialized_fill(mem + count, mem + req,
tombstone);
204 std::destroy(packed, packed + reserved);
205 alloc_traits::deallocate(allocator, packed, reserved);
211 void release_memory() {
213 for(
size_type pos{}; pos < bucket; ++pos) {
215 std::destroy(sparse[pos], sparse[pos] + sparse_page);
216 alloc_traits::deallocate(allocator, sparse[pos], sparse_page);
220 std::destroy(packed, packed + reserved);
221 std::destroy(sparse, sparse + bucket);
222 alloc_traits::deallocate(allocator, packed, reserved);
223 bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket);
233 virtual void swap_at([[maybe_unused]]
const std::size_t lhs, [[maybe_unused]]
const std::size_t rhs) {}
240 virtual void move_and_pop([[maybe_unused]]
const std::size_t from, [[maybe_unused]]
const std::size_t to) {}
248 auto &ref = sparse[page(
entt)][offset(
entt)];
250 ENTT_ASSERT(packed[pos] ==
entt,
"Invalid entity identifier");
251 auto &last = packed[--count];
254 sparse[page(last)][offset(last)] = ref;
258 ENTT_ASSERT((last =
tombstone,
true),
"");
267 auto &ref = sparse[page(
entt)][offset(
entt)];
269 ENTT_ASSERT(packed[pos] ==
entt,
"Invalid entity identifier");
297 bucket_allocator{alloc},
298 sparse{bucket_alloc_traits::allocate(bucket_allocator, 0u)},
299 packed{alloc_traits::allocate(allocator, 0u)},
320 : allocator{std::move(other.allocator)},
321 bucket_allocator{std::move(other.bucket_allocator)},
322 sparse{std::exchange(other.sparse, bucket_alloc_pointer{})},
323 packed{std::exchange(other.packed, alloc_pointer{})},
324 bucket{std::exchange(other.bucket, 0u)},
325 count{std::exchange(other.count, 0u)},
326 reserved{std::exchange(other.reserved, 0u)},
327 free_list{std::exchange(other.free_list,
tombstone)},
344 allocator = std::move(other.allocator);
345 bucket_allocator = std::move(other.bucket_allocator);
346 sparse = std::exchange(other.sparse, bucket_alloc_pointer{});
347 packed = std::exchange(other.packed, alloc_pointer{});
348 bucket = std::exchange(other.bucket, 0u);
349 count = std::exchange(other.count, 0u);
350 reserved = std::exchange(other.reserved, 0u);
351 free_list = std::exchange(other.free_list,
tombstone);
398 if(count < reserved) {
399 resize_packed(count);
414 return bucket * sparse_page;
435 [[nodiscard]]
bool empty() const ENTT_NOEXCEPT {
471 return iterator{std::addressof(packed), {}};
485 return std::make_reverse_iterator(
end());
499 return std::make_reverse_iterator(
begin());
519 const auto curr = page(
entt);
521 return (curr < bucket && sparse[curr] && sparse[curr][offset(
entt)] !=
null);
535 ENTT_ASSERT(
contains(
entt),
"Set does not contain entity");
545 return pos < count ? packed[pos] :
null;
554 ENTT_ASSERT(pos < count,
"Position is out of bounds");
569 ENTT_ASSERT(!
contains(
entt),
"Set already contains entity");
571 if(count == reserved) {
573 resize_packed(sz + !(sz > reserved));
577 packed[count] =
entt;
592 if(free_list ==
null) {
595 ENTT_ASSERT(!
contains(
entt),
"Set already contains entity");
598 free_list = std::exchange(packed[pos],
entt);
614 template<
typename It>
616 reserve(count + std::distance(first, last));
618 for(; first != last; ++first) {
619 const auto entt = *first;
620 ENTT_ASSERT(!
contains(
entt),
"Set already contains entity");
622 packed[count++] =
entt;
637 ENTT_ASSERT(
contains(
entt),
"Set does not contain entity");
651 template<
typename It>
652 void erase(It first, It last,
void *ud =
nullptr) {
653 for(; first != last; ++first) {
676 template<
typename It>
680 for(; first != last; ++first) {
681 found +=
remove(*first, ud);
690 for(; next && packed[next - 1u] ==
tombstone; --next);
692 for(
auto *it = &free_list; *it !=
null && next; it = std::addressof(packed[
traits_type::to_entity(*it)])) {
696 std::swap(packed[next], packed[pos]);
699 for(; next && packed[next - 1u] ==
tombstone; --next);
721 ENTT_ASSERT(
contains(lhs),
"Set does not contain entity");
722 ENTT_ASSERT(
contains(rhs),
"Set does not contain entity");
724 auto &
entt = sparse[page(lhs)][offset(lhs)];
725 auto &other = sparse[page(rhs)][offset(rhs)];
732 std::swap(
entt, other);
733 std::swap(packed[from], packed[to]);
766 template<
typename Compare,
typename Sort =
std_sort,
typename... Args>
767 void sort_n(
const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) {
769 ENTT_ASSERT(!(length > count),
"Length exceeds the number of elements");
772 algo(std::make_reverse_iterator(packed + length), std::make_reverse_iterator(packed), std::move(compare), std::forward<Args>(args)...);
774 for(
size_type pos{}; pos < length; ++pos) {
776 auto next =
index(packed[curr]);
778 while(curr != next) {
779 const auto idx =
index(packed[next]);
780 const auto entt = packed[curr];
784 curr = std::exchange(next, idx);
801 template<
typename Compare,
typename Sort = std_sort,
typename... Args>
802 void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
803 sort_n(count, std::move(compare), std::move(algo), std::forward<Args>(args)...);
824 const auto to = other.
end();
825 auto from = other.
begin();
827 for(
size_type pos = count - 1; pos && from != to; ++from) {
829 if(*from != packed[pos]) {
831 swap(packed[pos], *from);
844 for(
auto &&
entity: *
this) {
855 typename alloc_traits::allocator_type allocator;
856 typename bucket_alloc_traits::allocator_type bucket_allocator;
857 bucket_alloc_pointer sparse;
858 alloc_pointer packed;
861 std::size_t reserved;
Basic sparse set implementation.
virtual void swap_and_pop(const Entity entt, [[maybe_unused]] void *ud)
Attempts to erase an entity from the internal packed array.
size_type remove(It first, It last, void *ud=nullptr)
Removes entities from a sparse set if they exist.
deletion_policy policy() const
Returns the deletion policy of a sparse set.
sparse_set_iterator iterator
Random access iterator type.
void reserve(const size_type cap)
Increases the capacity of a sparse set.
iterator begin() const
Returns an iterator to the beginning.
virtual void move_and_pop([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to)
Moves an entity in the internal packed array.
void clear(void *ud=nullptr)
Clears a sparse set.
void insert(It first, It last)
Assigns one or more entities to a sparse set.
size_type index(const entity_type entt) const
Returns the position of an entity in a sparse set.
virtual void in_place_pop(const Entity entt, [[maybe_unused]] void *ud)
Attempts to erase an entity from the internal packed array.
size_type capacity() const
Returns the number of elements that a sparse set has currently allocated space for.
reverse_iterator rend() const
Returns a reverse iterator to the end.
void shrink_to_fit()
Requests the removal of unused capacity.
size_type emplace(const entity_type entt)
Assigns an entity to a sparse set.
std::size_t size_type
Unsigned integer type.
alloc_const_pointer pointer
Pointer type to contained entities.
iterator end() const
Returns an iterator to the end.
bool empty() const
Checks whether a sparse set is empty.
size_type slot() const
Returns the next slot available for insertion.
entity_type operator[](const size_type pos) const
Returns the entity at specified location, without bounds checking.
basic_sparse_set(deletion_policy pol, const allocator_type &alloc={})
Constructs an empty container with the given 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.
basic_sparse_set(const allocator_type &alloc={})
Constructs an empty container with the given allocator.
size_type size() const
Returns the number of elements in a sparse set.
reverse_iterator rbegin() const
Returns a reverse iterator to the beginning.
iterator find(const entity_type entt) const
Finds an entity.
void swap(const entity_type lhs, const entity_type rhs)
Swaps two entities in the internal packed array.
Entity entity_type
Underlying entity identifier.
bool remove(const entity_type entt, void *ud=nullptr)
Removes an entity from a sparse set if it exists.
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.
basic_sparse_set(basic_sparse_set &&other)
Move constructor.
size_type emplace_back(const entity_type entt)
Appends an entity to a sparse set.
void sort(Compare compare, Sort algo=Sort{}, Args &&... args)
Sort all elements according to the given comparison function.
void erase(It first, It last, void *ud=nullptr)
Erases entities from a set.
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 compact()
Removes all tombstones from the packed array of a sparse set.
void erase(const entity_type entt, void *ud=nullptr)
Erases an entity from a sparse set.
virtual void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs)
Swaps two entities in the internal packed array.
basic_sparse_set & operator=(basic_sparse_set &&other)
Move assignment operator.
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
typename alloc_traits::allocator_type allocator_type
Allocator type.
static constexpr entity_type to_entity(const value_type value)
Returns the entity part once converted to the underlying type.
typename traits_type::entity_type entity_type
Underlying entity type.
static constexpr value_type construct(const entity_type entity=traits_type::entity_mask, const version_type version=traits_type::version_mask)
Constructs an identifier from its parts.
typename traits_type::difference_type difference_type
Difference type.
entity
Default entity identifier.
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.
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 Entity entity, const null_t other)
Compares a null object and an entity identifier of any type.
Function object to wrap std::sort in a class type.