Skip to main content

SparseSet.ixx File

Generic sparse set data structure for efficient entity-keyed storage. More...

Included Headers​

#include <cassert> #include <functional> #include <vector> #include <cstddef> #include <helios.ecs.types.TypeDefs> #include <helios.ecs.types.EntityHandle>

Namespaces Index​

namespacehelios
namespaceecs

Classes Index​

classSparseSetBase

Abstract base class for type-erased sparse set access. More...

classSparseSet<T>

A generic sparse set providing O(1) insertion, lookup, and removal. More...

structIterator

Forward iterator for traversing the sparse set. More...

structConstIterator

Const forward iterator for traversing the sparse set. More...

Description​

Generic sparse set data structure for efficient entity-keyed storage.

File Listing​

The file content with the documentation metadata removed is:

1
5module;
6
7#include <cassert>
8#include <functional>
9#include <vector>
10#include <cstddef>
11
12export module helios.ecs.SparseSet;
13
14import helios.ecs.types.EntityHandle;
15import helios.ecs.types.TypeDefs;
16
17
18using namespace helios::ecs::types;
19export namespace helios::ecs {
20
32
33 public:
34
38 virtual ~SparseSetBase() = default;
39
48 virtual bool remove(EntityId id) = 0;
49
56 virtual void clear() = 0;
57
65 [[nodiscard]] virtual bool contains(EntityId id) const = 0;
66
74 [[nodiscard]] virtual void* raw(EntityId id) = 0;
75 };
76
77
83 constexpr auto Tombstone = EntityTombstone;
84
85
125 template <typename T>
126 class SparseSet : public SparseSetBase {
127
128
134 std::vector<size_t> sparse_;
135
141 std::vector<EntityId> denseToSparse_;
142
146 std::vector<T> storage_;
147
148 public:
149
153 SparseSet() = default;
154
160 explicit SparseSet(const size_t capacity) {
161 sparse_.reserve(capacity);
162 storage_.reserve(capacity);
163 denseToSparse_.reserve(capacity);
164 };
165
169 SparseSet(const SparseSet&) = delete;
170
174 SparseSet& operator=(const SparseSet&) = delete;
175
179 SparseSet(SparseSet&&) noexcept = default;
180
184 SparseSet& operator=(SparseSet&&) noexcept = default;
185
186
199 template <typename... Args>
200 [[nodiscard]] T* emplace(const EntityId idx, Args&& ...args) {
201
202 // already in use
203 if (idx < sparse_.size() && sparse_[idx] != Tombstone) {
204 return nullptr;
205 }
206
207 if (idx >= sparse_.size()) {
208 sparse_.resize(idx + 1, Tombstone);
209 }
210
211 const auto denseIndex = storage_.size();
212
213 denseToSparse_.push_back(idx);
214 storage_.emplace_back(std::forward<Args>(args)...);
215
216 sparse_[idx] = denseIndex;
217
218 return &storage_.back();
219 }
220
232 [[nodiscard]] T* insert(const EntityId idx, T&& obj) {
233
234 // already in use
235 if (idx < sparse_.size() && sparse_[idx] != Tombstone) {
236 return nullptr;
237 }
238
239 if (idx >= sparse_.size()) {
240 sparse_.resize(idx + 1, Tombstone);
241 }
242
243 const auto denseIndex = storage_.size();
244
245 denseToSparse_.push_back(idx);
246 storage_.emplace_back(std::move(obj));
247
248 sparse_[idx] = denseIndex;
249
250 return &storage_.back();
251 }
252
266 [[nodiscard]] bool remove(const EntityId idx) override {
267
268 if (idx >= sparse_.size() || sparse_[idx] == Tombstone) {
269 return false;
270 }
271
272 const auto denseIndex = sparse_[idx];
273 const auto sparseIdx = denseToSparse_[denseIndex];
274
275 assert(sparseIdx == idx && "Sparse index mismatch");
276
277 if (denseIndex != storage_.size() - 1) {
278 storage_[denseIndex] = std::move(storage_.back());
279 const auto newSparseIndex = denseToSparse_.back();
280 sparse_[newSparseIndex] = denseIndex;
281 denseToSparse_[denseIndex] = newSparseIndex;
282 }
283
284 storage_.pop_back();
285 denseToSparse_.pop_back();
286
287 sparse_[idx] = Tombstone;
288
289 return true;
290 }
291
299 [[nodiscard]] T* get(const EntityId idx) {
300
301 if (idx >= sparse_.size() || sparse_[idx] == Tombstone) {
302 return nullptr;
303 }
304
305 return &storage_[sparse_[idx]];
306 }
307
315 [[nodiscard]] const T* get(const EntityId idx) const {
316
317 if (idx >= sparse_.size() || sparse_[idx] == Tombstone) {
318 return nullptr;
319 }
320
321 return &storage_[sparse_[idx]];
322 }
323
324
332 [[nodiscard]] bool contains(const EntityId idx) const override {
333 return idx < sparse_.size() && sparse_[idx] != Tombstone;
334 }
335
339 [[nodiscard]] void* raw(const EntityId id) override {
340 T* ptr = get(id);
341 return static_cast<void*>(ptr);
342 }
343
347 void clear() override {
348 sparse_.clear();
349 denseToSparse_.clear();
350 storage_.clear();
351 }
352
353
360 struct Iterator {
361 using DataIt = typename std::vector<T>::iterator;
362 using IdIt = typename std::vector<EntityId>::iterator;
363
368
373
374 using iterator_category = std::forward_iterator_tag;
375 using value_type = T;
376 using difference_type = std::ptrdiff_t;
377 using pointer = T*;
378 using reference = T&;
379
380 Iterator() = default;
381
382 Iterator(DataIt dataIt, IdIt idIt) : dataIt_(dataIt), idIt_(idIt) {}
383
384 reference operator*() const { return *dataIt_; }
385 pointer operator->() const { return &*dataIt_; }
386
392 [[nodiscard]] EntityId entityId() const { return *idIt_; }
393
394 [[nodiscard]] bool operator==(const Iterator& other) const { return dataIt_ == other.dataIt_;}
395 [[nodiscard]] bool operator!=(const Iterator& other) const { return dataIt_ != other.dataIt_;}
396
398 Iterator tmp = *this;
399 ++(*this);
400 return tmp;
401 }
402
404 ++dataIt_; ++idIt_; return *this;
405 }
406 };
407
414 using DataIt = typename std::vector<T>::const_iterator;
415 using IdIt = typename std::vector<EntityId>::const_iterator;
416
421
426
427 using iterator_category = std::forward_iterator_tag;
428 using value_type = T;
429 using difference_type = std::ptrdiff_t;
430 using pointer = const T*;
431 using reference = const T&;
432
433 ConstIterator() = default;
434
435 ConstIterator(DataIt dataIt, IdIt idIt) : dataIt_(dataIt), idIt_(idIt) {}
436
437 reference operator*() const { return *dataIt_; }
438 pointer operator->() const { return &*dataIt_; }
439
445 [[nodiscard]] EntityId entityId() const { return *idIt_; }
446
447 [[nodiscard]] bool operator==(const ConstIterator& other) const { return dataIt_ == other.dataIt_;}
448 [[nodiscard]] bool operator!=(const ConstIterator& other) const { return dataIt_ != other.dataIt_;}
449
451 ConstIterator tmp = *this;
452 ++(*this);
453 return tmp;
454 }
455
457 ++dataIt_; ++idIt_; return *this;
458 }
459 };
460
466 [[nodiscard]] Iterator begin() {
467 return Iterator(storage_.begin(), denseToSparse_.begin());
468 }
469
475 [[nodiscard]] Iterator end() {
476 return Iterator(storage_.end(), denseToSparse_.end());
477 }
478
484 [[nodiscard]] ConstIterator begin() const {
485 return ConstIterator(storage_.begin(), denseToSparse_.begin());
486 }
487
493 [[nodiscard]] ConstIterator end() const {
494 return ConstIterator(storage_.end(), denseToSparse_.end());
495 }
496
497 };
498
499
500
501}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.9.8.