EnTT  3.5.0
sparse_set.hpp
1 #ifndef ENTT_ENTITY_SPARSE_SET_HPP
2 #define ENTT_ENTITY_SPARSE_SET_HPP
3 
4 
5 #include <iterator>
6 #include <utility>
7 #include <vector>
8 #include <memory>
9 #include <cstddef>
10 #include <type_traits>
11 #include "../config/config.h"
12 #include "../core/algorithm.hpp"
13 #include "entity.hpp"
14 #include "fwd.hpp"
15 
16 
17 namespace entt {
18 
19 
42 template<typename Entity>
43 class sparse_set {
44  static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
45  static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity);
46 
48  using page_type = std::unique_ptr<Entity[]>;
49 
50  class sparse_set_iterator final {
51  friend class sparse_set<Entity>;
52 
53  using packed_type = std::vector<Entity>;
54  using index_type = typename traits_type::difference_type;
55 
56  sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT
57  : packed{&ref}, index{idx}
58  {}
59 
60  public:
61  using difference_type = index_type;
62  using value_type = Entity;
63  using pointer = const value_type *;
64  using reference = const value_type &;
65  using iterator_category = std::random_access_iterator_tag;
66 
67  sparse_set_iterator() ENTT_NOEXCEPT = default;
68 
69  sparse_set_iterator & operator++() ENTT_NOEXCEPT {
70  return --index, *this;
71  }
72 
73  sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
74  iterator orig = *this;
75  return ++(*this), orig;
76  }
77 
78  sparse_set_iterator & operator--() ENTT_NOEXCEPT {
79  return ++index, *this;
80  }
81 
82  sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
83  sparse_set_iterator orig = *this;
84  return operator--(), orig;
85  }
86 
87  sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
88  index -= value;
89  return *this;
90  }
91 
92  sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
93  sparse_set_iterator copy = *this;
94  return (copy += value);
95  }
96 
97  sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
98  return (*this += -value);
99  }
100 
101  sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
102  return (*this + -value);
103  }
104 
105  difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
106  return other.index - index;
107  }
108 
109  [[nodiscard]] reference operator[](const difference_type value) const {
110  const auto pos = size_type(index-value-1u);
111  return (*packed)[pos];
112  }
113 
114  [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
115  return other.index == index;
116  }
117 
118  [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
119  return !(*this == other);
120  }
121 
122  [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
123  return index > other.index;
124  }
125 
126  [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
127  return index < other.index;
128  }
129 
130  [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
131  return !(*this > other);
132  }
133 
134  [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
135  return !(*this < other);
136  }
137 
138  [[nodiscard]] pointer operator->() const {
139  const auto pos = size_type(index-1u);
140  return &(*packed)[pos];
141  }
142 
143  [[nodiscard]] reference operator*() const {
144  return *operator->();
145  }
146 
147  private:
148  const packed_type *packed;
149  index_type index;
150  };
151 
152  [[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT {
153  return size_type{(to_integral(entt) & traits_type::entity_mask) / entt_per_page};
154  }
155 
156  [[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT {
157  return size_type{to_integral(entt) & (entt_per_page - 1)};
158  }
159 
160  [[nodiscard]] page_type & assure(const std::size_t pos) {
161  if(!(pos < sparse.size())) {
162  sparse.resize(pos+1);
163  }
164 
165  if(!sparse[pos]) {
166  sparse[pos].reset(new entity_type[entt_per_page]);
167  // null is safe in all cases for our purposes
168  for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) {
169  *first = null;
170  }
171  }
172 
173  return sparse[pos];
174  }
175 
176 public:
178  using entity_type = Entity;
180  using size_type = std::size_t;
182  using iterator = sparse_set_iterator;
184  using reverse_iterator = const entity_type *;
185 
187  sparse_set() = default;
188 
190  sparse_set(sparse_set &&) = default;
191 
193  virtual ~sparse_set() = default;
194 
196  sparse_set & operator=(sparse_set &&) = default;
197 
206  void reserve(const size_type cap) {
207  packed.reserve(cap);
208  }
209 
215  [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
216  return packed.capacity();
217  }
218 
220  void shrink_to_fit() {
221  // conservative approach
222  if(packed.empty()) {
223  sparse.clear();
224  }
225 
226  sparse.shrink_to_fit();
227  packed.shrink_to_fit();
228  }
229 
240  [[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
241  return sparse.size() * entt_per_page;
242  }
243 
254  [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
255  return packed.size();
256  }
257 
262  [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
263  return packed.empty();
264  }
265 
278  [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
279  return packed.data();
280  }
281 
291  [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
292  const typename traits_type::difference_type pos = packed.size();
293  return iterator{packed, pos};
294  }
295 
306  [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
307  return iterator{packed, {}};
308  }
309 
320  [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
321  return packed.data();
322  }
323 
334  [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
335  return rbegin() + packed.size();
336  }
337 
344  [[nodiscard]] iterator find(const entity_type entt) const {
345  return contains(entt) ? --(end() - index(entt)) : end();
346  }
347 
353  [[nodiscard]] bool contains(const entity_type entt) const {
354  const auto curr = page(entt);
355  // testing against null permits to avoid accessing the packed array
356  return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null);
357  }
358 
371  [[nodiscard]] size_type index(const entity_type entt) const {
372  ENTT_ASSERT(contains(entt));
373  return size_type{to_integral(sparse[page(entt)][offset(entt)])};
374  }
375 
387  void emplace(const entity_type entt) {
388  ENTT_ASSERT(!contains(entt));
389  assure(page(entt))[offset(entt)] = entity_type(static_cast<typename traits_type::entity_type>(packed.size()));
390  packed.push_back(entt);
391  }
392 
406  template<typename It>
407  void insert(It first, It last) {
408  auto next = static_cast<typename traits_type::entity_type>(packed.size());
409  packed.insert(packed.end(), first, last);
410 
411  while(first != last) {
412  const auto entt = *(first++);
413  ENTT_ASSERT(!contains(entt));
414  assure(page(entt))[offset(entt)] = entity_type(next++);
415  }
416  }
417 
429  void erase(const entity_type entt) {
430  ENTT_ASSERT(contains(entt));
431  const auto curr = page(entt);
432  const auto pos = offset(entt);
433  packed[size_type{to_integral(sparse[curr][pos])}] = packed.back();
434  sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos];
435  sparse[curr][pos] = null;
436  packed.pop_back();
437  }
438 
454  virtual void swap(const entity_type lhs, const entity_type rhs) {
455  auto &from = sparse[page(lhs)][offset(lhs)];
456  auto &to = sparse[page(rhs)][offset(rhs)];
457  std::swap(packed[size_type{to_integral(from)}], packed[size_type{to_integral(to)}]);
458  std::swap(from, to);
459  }
460 
495  template<typename Compare, typename Sort = std_sort, typename... Args>
496  void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) {
497  ENTT_ASSERT(!(last < first));
498  ENTT_ASSERT(!(last > end()));
499 
500  const auto length = std::distance(first, last);
501  const auto skip = std::distance(last, end());
502  const auto to = packed.rend() - skip;
503  const auto from = to - length;
504 
505  algo(from, to, std::move(compare), std::forward<Args>(args)...);
506 
507  for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
508  sparse[page(packed[pos])][offset(packed[pos])] = entity_type(static_cast<typename traits_type::entity_type>(pos));
509  }
510  }
511 
539  template<typename Apply, typename Compare, typename Sort = std_sort, typename... Args>
540  void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) {
541  ENTT_ASSERT(!(last < first));
542  ENTT_ASSERT(!(last > end()));
543 
544  const auto length = std::distance(first, last);
545  const auto skip = std::distance(last, end());
546  const auto to = packed.rend() - skip;
547  const auto from = to - length;
548 
549  algo(from, to, std::move(compare), std::forward<Args>(args)...);
550 
551  for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
552  auto curr = pos;
553  auto next = index(packed[curr]);
554 
555  while(curr != next) {
556  apply(packed[curr], packed[next]);
557  sparse[page(packed[curr])][offset(packed[curr])] = entity_type(static_cast<typename traits_type::entity_type>(curr));
558 
559  curr = next;
560  next = index(packed[curr]);
561  }
562  }
563  }
564 
580  void respect(const sparse_set &other) {
581  const auto to = other.end();
582  auto from = other.begin();
583 
584  size_type pos = packed.size() - 1;
585 
586  while(pos && from != to) {
587  if(contains(*from)) {
588  if(*from != packed[pos]) {
589  swap(packed[pos], *from);
590  }
591 
592  --pos;
593  }
594 
595  ++from;
596  }
597  }
598 
602  void clear() ENTT_NOEXCEPT {
603  sparse.clear();
604  packed.clear();
605  }
606 
607 private:
608  std::vector<page_type> sparse;
609  std::vector<entity_type> packed;
610 };
611 
612 
613 }
614 
615 
616 #endif
entt::sparse_set::swap
virtual void swap(const entity_type lhs, const entity_type rhs)
Swaps two entities in the internal packed array.
Definition: sparse_set.hpp:454
entt::sparse_set::respect
void respect(const sparse_set &other)
Sort entities according to their order in another sparse set.
Definition: sparse_set.hpp:580
entt::to_integral
constexpr auto to_integral(const Entity entity) noexcept
Converts an entity type to its underlying type.
Definition: entity.hpp:119
entt::sparse_set::~sparse_set
virtual ~sparse_set()=default
Default destructor.
entt::sparse_set::capacity
size_type capacity() const noexcept
Returns the number of elements that a sparse set has currently allocated space for.
Definition: sparse_set.hpp:215
entt::sparse_set::find
iterator find(const entity_type entt) const
Finds an entity.
Definition: sparse_set.hpp:344
entt::sparse_set
Basic sparse set implementation.
Definition: sparse_set.hpp:43
entt::get
constexpr get_t< Type... > get
Variable template for lists of observed components.
Definition: utility.hpp:40
entt::sparse_set::insert
void insert(It first, It last)
Assigns one or more entities to a sparse set.
Definition: sparse_set.hpp:407
entt::sparse_set::sparse_set
sparse_set(sparse_set &&)=default
Default move constructor.
entt::sparse_set::begin
iterator begin() const noexcept
Returns an iterator to the beginning.
Definition: sparse_set.hpp:291
entt::sparse_set::sparse_set
sparse_set()=default
Default constructor.
entt::sparse_set::size
size_type size() const noexcept
Returns the number of elements in a sparse set.
Definition: sparse_set.hpp:254
entt::sparse_set::arrange
void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo=Sort{}, Args &&... args)
Sort elements according to the given comparison function.
Definition: sparse_set.hpp:540
entt::sparse_set< entity_type >::reverse_iterator
const entity_type * reverse_iterator
Reverse iterator type.
Definition: sparse_set.hpp:184
entt::sparse_set::end
iterator end() const noexcept
Returns an iterator to the end.
Definition: sparse_set.hpp:306
entt::sparse_set::iterator
sparse_set_iterator iterator
Random access iterator type.
Definition: sparse_set.hpp:182
entt::sparse_set::data
const entity_type * data() const noexcept
Direct access to the internal packed array.
Definition: sparse_set.hpp:278
entt::sparse_set::shrink_to_fit
void shrink_to_fit()
Requests the removal of unused capacity.
Definition: sparse_set.hpp:220
entt::sparse_set::erase
void erase(const entity_type entt)
Removes an entity from a sparse set.
Definition: sparse_set.hpp:429
entt
EnTT default namespace.
Definition: algorithm.hpp:13
entt::sparse_set::rend
reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
Definition: sparse_set.hpp:334
entt::sparse_set::extent
size_type extent() const noexcept
Returns the extent of a sparse set.
Definition: sparse_set.hpp:240
entt::sparse_set::size_type
std::size_t size_type
Unsigned integer type.
Definition: sparse_set.hpp:180
entt::sparse_set::index
size_type index(const entity_type entt) const
Returns the position of an entity in a sparse set.
Definition: sparse_set.hpp:371
entt::sparse_set::rbegin
reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
Definition: sparse_set.hpp:320
entt::sparse_set::empty
bool empty() const noexcept
Checks whether a sparse set is empty.
Definition: sparse_set.hpp:262
entt::sparse_set::operator=
sparse_set & operator=(sparse_set &&)=default
Default move assignment operator.
entt::sparse_set::sort
void sort(iterator first, iterator last, Compare compare, Sort algo=Sort{}, Args &&... args)
Sort elements according to the given comparison function.
Definition: sparse_set.hpp:496
entt::sparse_set::emplace
void emplace(const entity_type entt)
Assigns an entity to a sparse set.
Definition: sparse_set.hpp:387
entt::operator!=
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
Definition: hashed_string.hpp:227
entt::entt_traits
Entity traits.
Definition: entity.hpp:21
entt::sparse_set::clear
void clear() noexcept
Clears a sparse set.
Definition: sparse_set.hpp:602
entt::operator==
constexpr bool operator==(const Entity entity, null_t other) noexcept
Compares a null object and an entity identifier of any type.
Definition: entity.hpp:184
entt::sparse_set::reserve
void reserve(const size_type cap)
Increases the capacity of a sparse set.
Definition: sparse_set.hpp:206
entt::sparse_set::entity_type
Entity entity_type
Underlying entity identifier.
Definition: sparse_set.hpp:178
entt::sparse_set::contains
bool contains(const entity_type entt) const
Checks if a sparse set contains an entity.
Definition: sparse_set.hpp:353
entt::std_sort
Function object to wrap std::sort in a class type.
Definition: algorithm.hpp:24