Easys
A minimalist, header-only C++ ECS library for efficient and fuss-free entity and component management.
Loading...
Searching...
No Matches
sparse_set.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cassert>
5#include <concepts>
6#include <iostream>
7#include <limits>
8#include <string>
9#include <vector>
10
11namespace Easys {
12
13class KeyNotFoundException : public std::exception {
14 public:
15 KeyNotFoundException(const std::string& key) : msg_("KeyNotFoundException: " + key + " not found") {}
16
17 const char* what() const noexcept override { return msg_.c_str(); }
18
19 private:
20 std::string msg_;
21};
22
23// Since SparseSet uses std::vector internally and the key is used to index we want to make sure to only use natural
24// numbers. We could also use a map for the sparse container...
25template <typename T>
26concept UnsignedIntegral = std::is_integral_v<T> && std::is_unsigned_v<T>;
27
28template <UnsignedIntegral Key, typename Value>
29class SparseSet {
30 private:
31 std::vector<Key> sparse; // Large, indexed by keys
32 std::vector<Key> dense; // Compact, stores keys
33 std::vector<Value> values; // Parallel to dense, stores values
34
35 public:
36 // Ensure the sparse array can accommodate the given key
37 inline void accommodate(const Key key)
38 {
39 if (key >= maxSize())
40 {
41 throw std::length_error("Key exceeds the maximum size limit.");
42 }
43
44 if (key >= sparse.size())
45 {
46 sparse.resize(key * 2 + 1, std::numeric_limits<Key>::max());
47 }
48 }
49
50 // Associate a value with a key
51
52 // Associate a value with a key
53 inline void set(const Key key, const Value& value)
54 {
56
57 if (sparse[key] == std::numeric_limits<Key>::max())
58 { // max Key to indicate not set
59 sparse[key] = static_cast<Key>(values.size());
60 dense.push_back(key);
61 values.push_back(value);
62 } else
63 {
64 values[sparse[key]] = value; // Key already has a value, update it
65 }
66 }
67
68 // move semantics overload
69 inline void set(const Key key, Value&& value)
70 {
72
73 if (sparse[key] == std::numeric_limits<Key>::max())
74 {
75 sparse[key] = static_cast<Key>(values.size());
76 dense.push_back(key);
77 values.push_back(std::move(value));
78 } else
79 {
80 values[sparse[key]] = std::move(value);
81 }
82 }
83
84 // Retrieve a value by key
85 inline const Value& get(const Key key) const
86 {
87 if (!contains(key))
88 {
89 throw KeyNotFoundException(std::to_string(key));
90 }
91 return values[sparse[key]];
92 }
93
94 // Retrieve a value by key
95 inline Value& get(const Key key)
96 {
97 if (!contains(key))
98 {
99 throw KeyNotFoundException(std::to_string(key));
100 }
101 return values[sparse[key]];
102 }
103
104 inline const Value& operator[](const Key key) const { return values[sparse[key]]; }
105 inline Value& operator[](const Key key) { return values[sparse[key]]; }
106
107 // Remove a value associated with a key
108 inline void remove(const Key key)
109 {
110 if (contains(key))
111 {
112 // Move the last value to the removed spot to keep dense packed
113 Key indexOfRemoved = sparse[key];
114 values[indexOfRemoved] = values.back();
115 dense[indexOfRemoved] = dense.back();
116
117 // Update the sparse array for the moved key
118 sparse[dense.back()] = indexOfRemoved;
119
120 // Shrink the dense array and values
121 dense.pop_back();
122 values.pop_back();
123
124 // Mark the key as not set
125 sparse[key] = std::numeric_limits<Key>::max();
126 }
127 }
128
129 // Iterate over all values
130 template <typename Func>
131 inline void forEach(Func f)
132 {
133 for (size_t i = 0; i < values.size(); ++i)
134 {
135 f(dense[i], values[i]);
136 }
137 }
138
139 constexpr bool contains(const Key key) const
140 {
141 return key < sparse.size() && sparse[key] != std::numeric_limits<Key>::max();
142 }
143
144 constexpr size_t size() const { return dense.size(); }
145
146 inline const std::vector<Key>& getKeys() const { return dense; }
147
148 inline std::vector<Value>& getValues() { return values; }
149
150 inline const std::vector<Value>& getValues() const { return values; }
151
152 constexpr size_t maxSize() const noexcept
153 {
154 constexpr size_t maxKeyVal = static_cast<size_t>(std::numeric_limits<Key>::max());
155 return std::min({maxKeyVal, dense.max_size(), values.max_size()});
156 }
157
158 inline void clear()
159 {
160 sparse.clear();
161 dense.clear();
162 values.clear();
163 }
164};
165
166} // namespace Easys
Definition sparse_set.hpp:13
KeyNotFoundException(const std::string &key)
Definition sparse_set.hpp:15
const char * what() const noexcept override
Definition sparse_set.hpp:17
Definition sparse_set.hpp:29
const Value & operator[](const Key key) const
Definition sparse_set.hpp:104
constexpr size_t maxSize() const noexcept
Definition sparse_set.hpp:152
void remove(const Key key)
Definition sparse_set.hpp:108
void set(const Key key, Value &&value)
Definition sparse_set.hpp:69
std::vector< Value > & getValues()
Definition sparse_set.hpp:148
void accommodate(const Key key)
Definition sparse_set.hpp:37
Value & operator[](const Key key)
Definition sparse_set.hpp:105
const std::vector< Key > & getKeys() const
Definition sparse_set.hpp:146
constexpr size_t size() const
Definition sparse_set.hpp:144
void set(const Key key, const Value &value)
Definition sparse_set.hpp:53
void forEach(Func f)
Definition sparse_set.hpp:131
const std::vector< Value > & getValues() const
Definition sparse_set.hpp:150
Value & get(const Key key)
Definition sparse_set.hpp:95
constexpr bool contains(const Key key) const
Definition sparse_set.hpp:139
const Value & get(const Key key) const
Definition sparse_set.hpp:85
void clear()
Definition sparse_set.hpp:158
Definition sparse_set.hpp:26
Definition ecs.hpp:12