|
@@ -2,7 +2,6 @@
|
|
|
#define ENTT_ENTITY_SPARSE_SET_HPP
|
|
#define ENTT_ENTITY_SPARSE_SET_HPP
|
|
|
|
|
|
|
|
|
|
|
|
|
-#include <algorithm>
|
|
|
|
|
#include <iterator>
|
|
#include <iterator>
|
|
|
#include <utility>
|
|
#include <utility>
|
|
|
#include <vector>
|
|
#include <vector>
|
|
@@ -47,19 +46,20 @@ namespace entt {
|
|
|
*/
|
|
*/
|
|
|
template<typename Entity>
|
|
template<typename Entity>
|
|
|
class sparse_set {
|
|
class sparse_set {
|
|
|
- using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
|
|
|
|
-
|
|
|
|
|
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0));
|
|
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0));
|
|
|
- static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(typename traits_type::entity_type);
|
|
|
|
|
|
|
+ static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity);
|
|
|
|
|
+
|
|
|
|
|
+ using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
|
|
|
|
+ using page_type = std::unique_ptr<Entity[]>;
|
|
|
|
|
|
|
|
class sparse_set_iterator final {
|
|
class sparse_set_iterator final {
|
|
|
friend class sparse_set<Entity>;
|
|
friend class sparse_set<Entity>;
|
|
|
|
|
|
|
|
- using direct_type = std::vector<Entity>;
|
|
|
|
|
|
|
+ using packed_type = std::vector<Entity>;
|
|
|
using index_type = typename traits_type::difference_type;
|
|
using index_type = typename traits_type::difference_type;
|
|
|
|
|
|
|
|
- sparse_set_iterator(const direct_type &ref, const index_type idx) ENTT_NOEXCEPT
|
|
|
|
|
- : direct{&ref}, index{idx}
|
|
|
|
|
|
|
+ sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT
|
|
|
|
|
+ : packed{&ref}, index{idx}
|
|
|
{}
|
|
{}
|
|
|
|
|
|
|
|
public:
|
|
public:
|
|
@@ -113,7 +113,7 @@ class sparse_set {
|
|
|
|
|
|
|
|
reference operator[](const difference_type value) const {
|
|
reference operator[](const difference_type value) const {
|
|
|
const auto pos = size_type(index-value-1);
|
|
const auto pos = size_type(index-value-1);
|
|
|
- return (*direct)[pos];
|
|
|
|
|
|
|
+ return (*packed)[pos];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
|
bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
|
@@ -142,7 +142,7 @@ class sparse_set {
|
|
|
|
|
|
|
|
pointer operator->() const {
|
|
pointer operator->() const {
|
|
|
const auto pos = size_type(index-1);
|
|
const auto pos = size_type(index-1);
|
|
|
- return &(*direct)[pos];
|
|
|
|
|
|
|
+ return &(*packed)[pos];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
reference operator*() const {
|
|
reference operator*() const {
|
|
@@ -150,7 +150,7 @@ class sparse_set {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
- const direct_type *direct;
|
|
|
|
|
|
|
+ const packed_type *packed;
|
|
|
index_type index;
|
|
index_type index;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -162,18 +162,20 @@ class sparse_set {
|
|
|
return std::size_t{to_integral(entt) & (entt_per_page - 1)};
|
|
return std::size_t{to_integral(entt) & (entt_per_page - 1)};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Entity * assure(const std::size_t pos) {
|
|
|
|
|
- if(!(pos < reverse.size())) {
|
|
|
|
|
- reverse.resize(pos+1);
|
|
|
|
|
|
|
+ page_type & assure(const std::size_t pos) {
|
|
|
|
|
+ if(!(pos < sparse.size())) {
|
|
|
|
|
+ sparse.resize(pos+1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if(!reverse[pos]) {
|
|
|
|
|
- reverse[pos] = std::make_unique<entity_type[]>(entt_per_page);
|
|
|
|
|
|
|
+ if(!sparse[pos]) {
|
|
|
|
|
+ sparse[pos] = std::make_unique<entity_type[]>(entt_per_page);
|
|
|
// null is safe in all cases for our purposes
|
|
// null is safe in all cases for our purposes
|
|
|
- std::fill_n(reverse[pos].get(), entt_per_page, null);
|
|
|
|
|
|
|
+ for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) {
|
|
|
|
|
+ *first = null;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return reverse[pos].get();
|
|
|
|
|
|
|
+ return sparse[pos];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
public:
|
|
@@ -205,7 +207,7 @@ public:
|
|
|
* @param cap Desired capacity.
|
|
* @param cap Desired capacity.
|
|
|
*/
|
|
*/
|
|
|
void reserve(const size_type cap) {
|
|
void reserve(const size_type cap) {
|
|
|
- direct.reserve(cap);
|
|
|
|
|
|
|
+ packed.reserve(cap);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -214,18 +216,18 @@ public:
|
|
|
* @return Capacity of the sparse set.
|
|
* @return Capacity of the sparse set.
|
|
|
*/
|
|
*/
|
|
|
size_type capacity() const ENTT_NOEXCEPT {
|
|
size_type capacity() const ENTT_NOEXCEPT {
|
|
|
- return direct.capacity();
|
|
|
|
|
|
|
+ return packed.capacity();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*! @brief Requests the removal of unused capacity. */
|
|
/*! @brief Requests the removal of unused capacity. */
|
|
|
void shrink_to_fit() {
|
|
void shrink_to_fit() {
|
|
|
// conservative approach
|
|
// conservative approach
|
|
|
- if(direct.empty()) {
|
|
|
|
|
- reverse.clear();
|
|
|
|
|
|
|
+ if(packed.empty()) {
|
|
|
|
|
+ sparse.clear();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- reverse.shrink_to_fit();
|
|
|
|
|
- direct.shrink_to_fit();
|
|
|
|
|
|
|
+ sparse.shrink_to_fit();
|
|
|
|
|
+ packed.shrink_to_fit();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -239,7 +241,7 @@ public:
|
|
|
* @return Extent of the sparse set.
|
|
* @return Extent of the sparse set.
|
|
|
*/
|
|
*/
|
|
|
size_type extent() const ENTT_NOEXCEPT {
|
|
size_type extent() const ENTT_NOEXCEPT {
|
|
|
- return reverse.size() * entt_per_page;
|
|
|
|
|
|
|
+ return sparse.size() * entt_per_page;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -253,7 +255,7 @@ public:
|
|
|
* @return Number of elements.
|
|
* @return Number of elements.
|
|
|
*/
|
|
*/
|
|
|
size_type size() const ENTT_NOEXCEPT {
|
|
size_type size() const ENTT_NOEXCEPT {
|
|
|
- return direct.size();
|
|
|
|
|
|
|
+ return packed.size();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -261,7 +263,7 @@ public:
|
|
|
* @return True if the sparse set is empty, false otherwise.
|
|
* @return True if the sparse set is empty, false otherwise.
|
|
|
*/
|
|
*/
|
|
|
bool empty() const ENTT_NOEXCEPT {
|
|
bool empty() const ENTT_NOEXCEPT {
|
|
|
- return direct.empty();
|
|
|
|
|
|
|
+ return packed.empty();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -280,7 +282,7 @@ public:
|
|
|
* @return A pointer to the internal packed array.
|
|
* @return A pointer to the internal packed array.
|
|
|
*/
|
|
*/
|
|
|
const entity_type * data() const ENTT_NOEXCEPT {
|
|
const entity_type * data() const ENTT_NOEXCEPT {
|
|
|
- return direct.data();
|
|
|
|
|
|
|
+ return packed.data();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -297,8 +299,8 @@ public:
|
|
|
* @return An iterator to the first entity of the internal packed array.
|
|
* @return An iterator to the first entity of the internal packed array.
|
|
|
*/
|
|
*/
|
|
|
iterator begin() const ENTT_NOEXCEPT {
|
|
iterator begin() const ENTT_NOEXCEPT {
|
|
|
- const typename traits_type::difference_type pos = direct.size();
|
|
|
|
|
- return iterator{direct, pos};
|
|
|
|
|
|
|
+ const typename traits_type::difference_type pos = packed.size();
|
|
|
|
|
+ return iterator{packed, pos};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -316,7 +318,7 @@ public:
|
|
|
* internal packed array.
|
|
* internal packed array.
|
|
|
*/
|
|
*/
|
|
|
iterator end() const ENTT_NOEXCEPT {
|
|
iterator end() const ENTT_NOEXCEPT {
|
|
|
- return iterator{direct, {}};
|
|
|
|
|
|
|
+ return iterator{packed, {}};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -336,8 +338,8 @@ public:
|
|
|
*/
|
|
*/
|
|
|
bool contains(const entity_type entt) const {
|
|
bool contains(const entity_type entt) const {
|
|
|
const auto curr = page(entt);
|
|
const auto curr = page(entt);
|
|
|
- // testing against null permits to avoid accessing the direct vector
|
|
|
|
|
- return (curr < reverse.size() && reverse[curr] && reverse[curr][offset(entt)] != null);
|
|
|
|
|
|
|
+ // testing against null permits to avoid accessing the packed array
|
|
|
|
|
+ return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*! @copydoc contains */
|
|
/*! @copydoc contains */
|
|
@@ -360,7 +362,7 @@ public:
|
|
|
*/
|
|
*/
|
|
|
size_type index(const entity_type entt) const {
|
|
size_type index(const entity_type entt) const {
|
|
|
ENTT_ASSERT(contains(entt));
|
|
ENTT_ASSERT(contains(entt));
|
|
|
- return size_type(reverse[page(entt)][offset(entt)]);
|
|
|
|
|
|
|
+ return size_type(sparse[page(entt)][offset(entt)]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -376,8 +378,8 @@ public:
|
|
|
*/
|
|
*/
|
|
|
void emplace(const entity_type entt) {
|
|
void emplace(const entity_type entt) {
|
|
|
ENTT_ASSERT(!contains(entt));
|
|
ENTT_ASSERT(!contains(entt));
|
|
|
- assure(page(entt))[offset(entt)] = entity_type(direct.size());
|
|
|
|
|
- direct.push_back(entt);
|
|
|
|
|
|
|
+ assure(page(entt))[offset(entt)] = entity_type(packed.size());
|
|
|
|
|
+ packed.push_back(entt);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*! @copydoc emplace */
|
|
/*! @copydoc emplace */
|
|
@@ -401,12 +403,14 @@ public:
|
|
|
*/
|
|
*/
|
|
|
template<typename It>
|
|
template<typename It>
|
|
|
void insert(It first, It last) {
|
|
void insert(It first, It last) {
|
|
|
- std::for_each(first, last, [this, next = direct.size()](const auto entt) mutable {
|
|
|
|
|
|
|
+ auto next = packed.size();
|
|
|
|
|
+ packed.insert(packed.end(), first, last);
|
|
|
|
|
+
|
|
|
|
|
+ while(first != last) {
|
|
|
|
|
+ const auto entt = *(first++);
|
|
|
ENTT_ASSERT(!contains(entt));
|
|
ENTT_ASSERT(!contains(entt));
|
|
|
assure(page(entt))[offset(entt)] = entity_type(next++);
|
|
assure(page(entt))[offset(entt)] = entity_type(next++);
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- direct.insert(direct.end(), first, last);
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*! @copydoc insert */
|
|
/*! @copydoc insert */
|
|
@@ -431,10 +435,10 @@ public:
|
|
|
ENTT_ASSERT(contains(entt));
|
|
ENTT_ASSERT(contains(entt));
|
|
|
const auto curr = page(entt);
|
|
const auto curr = page(entt);
|
|
|
const auto pos = offset(entt);
|
|
const auto pos = offset(entt);
|
|
|
- direct[size_type(reverse[curr][pos])] = entity_type(direct.back());
|
|
|
|
|
- reverse[page(direct.back())][offset(direct.back())] = reverse[curr][pos];
|
|
|
|
|
- reverse[curr][pos] = null;
|
|
|
|
|
- direct.pop_back();
|
|
|
|
|
|
|
+ packed[size_type(sparse[curr][pos])] = entity_type(packed.back());
|
|
|
|
|
+ sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos];
|
|
|
|
|
+ sparse[curr][pos] = null;
|
|
|
|
|
+ packed.pop_back();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*! @copydoc erase */
|
|
/*! @copydoc erase */
|
|
@@ -459,9 +463,9 @@ public:
|
|
|
* @param rhs A valid entity identifier.
|
|
* @param rhs A valid entity identifier.
|
|
|
*/
|
|
*/
|
|
|
virtual void swap(const entity_type lhs, const entity_type rhs) {
|
|
virtual void swap(const entity_type lhs, const entity_type rhs) {
|
|
|
- auto &from = reverse[page(lhs)][offset(lhs)];
|
|
|
|
|
- auto &to = reverse[page(rhs)][offset(rhs)];
|
|
|
|
|
- std::swap(direct[size_type(from)], direct[size_type(to)]);
|
|
|
|
|
|
|
+ auto &from = sparse[page(lhs)][offset(lhs)];
|
|
|
|
|
+ auto &to = sparse[page(rhs)][offset(rhs)];
|
|
|
|
|
+ std::swap(packed[size_type(from)], packed[size_type(to)]);
|
|
|
std::swap(from, to);
|
|
std::swap(from, to);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -511,13 +515,13 @@ public:
|
|
|
|
|
|
|
|
const auto length = std::distance(first, last);
|
|
const auto length = std::distance(first, last);
|
|
|
const auto skip = std::distance(last, end());
|
|
const auto skip = std::distance(last, end());
|
|
|
- const auto to = direct.rend() - skip;
|
|
|
|
|
|
|
+ const auto to = packed.rend() - skip;
|
|
|
const auto from = to - length;
|
|
const auto from = to - length;
|
|
|
|
|
|
|
|
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
|
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
|
|
|
|
|
|
|
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
|
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
|
|
- reverse[page(direct[pos])][offset(direct[pos])] = entity_type(pos);
|
|
|
|
|
|
|
+ sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -555,21 +559,21 @@ public:
|
|
|
|
|
|
|
|
const auto length = std::distance(first, last);
|
|
const auto length = std::distance(first, last);
|
|
|
const auto skip = std::distance(last, end());
|
|
const auto skip = std::distance(last, end());
|
|
|
- const auto to = direct.rend() - skip;
|
|
|
|
|
|
|
+ const auto to = packed.rend() - skip;
|
|
|
const auto from = to - length;
|
|
const auto from = to - length;
|
|
|
|
|
|
|
|
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
|
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
|
|
|
|
|
|
|
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
|
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
|
|
auto curr = pos;
|
|
auto curr = pos;
|
|
|
- auto next = index(direct[curr]);
|
|
|
|
|
|
|
+ auto next = index(packed[curr]);
|
|
|
|
|
|
|
|
while(curr != next) {
|
|
while(curr != next) {
|
|
|
- apply(direct[curr], direct[next]);
|
|
|
|
|
- reverse[page(direct[curr])][offset(direct[curr])] = entity_type(curr);
|
|
|
|
|
|
|
+ apply(packed[curr], packed[next]);
|
|
|
|
|
+ sparse[page(packed[curr])][offset(packed[curr])] = entity_type(curr);
|
|
|
|
|
|
|
|
curr = next;
|
|
curr = next;
|
|
|
- next = index(direct[curr]);
|
|
|
|
|
|
|
+ next = index(packed[curr]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -598,12 +602,12 @@ public:
|
|
|
const auto to = other.end();
|
|
const auto to = other.end();
|
|
|
auto from = other.begin();
|
|
auto from = other.begin();
|
|
|
|
|
|
|
|
- size_type pos = direct.size() - 1;
|
|
|
|
|
|
|
+ size_type pos = packed.size() - 1;
|
|
|
|
|
|
|
|
while(pos && from != to) {
|
|
while(pos && from != to) {
|
|
|
if(contains(*from)) {
|
|
if(contains(*from)) {
|
|
|
- if(*from != direct[pos]) {
|
|
|
|
|
- swap(direct[pos], *from);
|
|
|
|
|
|
|
+ if(*from != packed[pos]) {
|
|
|
|
|
+ swap(packed[pos], *from);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
--pos;
|
|
--pos;
|
|
@@ -617,13 +621,13 @@ public:
|
|
|
* @brief Clears a sparse set.
|
|
* @brief Clears a sparse set.
|
|
|
*/
|
|
*/
|
|
|
void clear() ENTT_NOEXCEPT {
|
|
void clear() ENTT_NOEXCEPT {
|
|
|
- reverse.clear();
|
|
|
|
|
- direct.clear();
|
|
|
|
|
|
|
+ sparse.clear();
|
|
|
|
|
+ packed.clear();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
- std::vector<std::unique_ptr<entity_type[]>> reverse;
|
|
|
|
|
- std::vector<entity_type> direct;
|
|
|
|
|
|
|
+ std::vector<page_type> sparse;
|
|
|
|
|
+ std::vector<entity_type> packed;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|