co_ecs 0.9.0
Cobalt ECS
Loading...
Searching...
No Matches
sparse_table.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <stdexcept>
5#include <vector>
6
7namespace co_ecs::detail {
8
17template<typename K, typename T, bool is_map, typename Allocator>
18class sparse_table {
19public:
20 using key_type = K;
21 using mapped_type = T;
22 using value_type = std::conditional_t<is_map, std::pair<key_type, mapped_type>, key_type>;
23 using size_type = std::size_t;
24 using difference_type = std::ptrdiff_t;
25 using allocator_type = Allocator;
26 using reference = value_type&;
27 using const_reference = const value_type&;
28 using pointer = typename std::allocator_traits<allocator_type>::pointer;
29 using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
30 using iterator = typename std::vector<value_type>::iterator;
31 using const_iterator = typename std::vector<value_type>::const_iterator;
32
33 static_assert(sizeof(key_type) <= sizeof(std::size_t));
34
36 constexpr sparse_table() = default;
37
41 constexpr sparse_table(sparse_table&& rhs) noexcept = default;
42
46 constexpr sparse_table(const sparse_table& rhs) = default;
47
52 constexpr auto operator=(sparse_table&& rhs) noexcept -> sparse_table& = default;
53
58 constexpr auto operator=(const sparse_table& rhs) -> sparse_table& = default;
59
61 constexpr ~sparse_table() = default;
62
66 constexpr sparse_table(std::initializer_list<value_type> list) {
67 _dense.reserve(list.size());
68 for (const auto& entry : list) {
69 insert(entry);
70 }
71 }
72
76 constexpr auto begin() noexcept -> iterator {
77 return _dense.begin();
78 }
79
83 constexpr auto end() noexcept -> iterator {
84 return _dense.end();
85 }
86
90 constexpr auto begin() const noexcept -> const_iterator {
91 return _dense.begin();
92 }
93
97 constexpr auto end() const noexcept -> const_iterator {
98 return _dense.end();
99 }
100
104 constexpr auto cbegin() const noexcept -> const_iterator {
105 return _dense.cbegin();
106 }
107
111 constexpr auto cend() const noexcept -> const_iterator {
112 return _dense.cend();
113 }
114
118 [[nodiscard]] constexpr auto size() const -> std::size_t {
119 return _size;
120 }
121
125 [[nodiscard]] constexpr auto capacity() const -> std::size_t {
126 return _sparse_capacity;
127 }
128
130 [[nodiscard]] constexpr auto empty() const -> bool {
131 return size() == 0;
132 }
133
135 constexpr void clear() noexcept {
136 _dense.clear();
137 _size = 0;
138 }
139
143 constexpr void reserve_dense(std::size_t capacity) {
144 _dense.reserve(capacity);
145 }
146
150 constexpr void reserve_sparse(std::size_t capacity) {
151 if (capacity > _sparse_capacity) {
152 _sparse.resize(capacity);
153 _sparse_capacity = capacity;
154 }
155 }
156
163 constexpr auto find(key_type key) const noexcept -> const_iterator {
164 return find_impl(*this, key);
165 }
166
173 constexpr auto find(key_type key) noexcept -> iterator {
174 return find_impl(*this, key);
175 }
176
181 constexpr auto contains(key_type key) const noexcept -> bool {
182 return find(key) != end();
183 }
184
191 constexpr auto insert(const value_type& entry) -> std::pair<iterator, bool> {
192 return emplace(get_key(entry), std::move(get_value(entry)));
193 }
194
203 template<typename... Args>
204 constexpr auto emplace(key_type key, Args&&... args) -> std::pair<iterator, bool> {
205 if (contains(key)) {
206 return std::pair(_dense.begin() + _sparse[key], false);
207 }
208
209 reserve_sparse(static_cast<std::size_t>(key) + 1);
210
211 if constexpr (is_map) {
212 _dense.emplace_back(std::piecewise_construct,
213 std::forward_as_tuple(key),
214 std::forward_as_tuple(std::forward<Args>(args)...));
215 } else {
216 _dense.emplace_back(key);
217 }
218 _sparse[key] = _size;
219 ++_size;
220 return std::pair(_dense.begin() + _sparse[key], true);
221 }
222
229 constexpr auto at(const key_type& key) -> mapped_type&
230 requires(is_map)
231 {
232 if (!contains(key)) {
233 throw std::out_of_range("Key is not in the BaseSparseSet");
234 }
235 return get_value(_dense[_sparse[key]]);
236 }
237
244 constexpr auto at(const key_type& key) const -> const mapped_type&
245 requires(is_map)
246 {
247 if (!contains(key)) {
248 throw std::out_of_range("Key is not in the BaseSparseSet");
249 }
250 return get_value(_dense[_sparse[key]]);
251 }
252
258 constexpr auto operator[](const key_type& key) noexcept -> mapped_type&
259 requires(is_map)
260 {
261 auto [iter, _] = emplace(key);
262 mapped_type& value = get_value(*iter);
263 return value;
264 }
265
270 constexpr auto erase(key_type key) noexcept -> std::size_t {
271 if (contains(key)) {
272 _dense[_sparse[key]] = std::move(_dense.back());
273 _sparse[get_key(_dense[_size - 1])] = _sparse[key];
274 _dense.pop_back();
275 _size--;
276 return 1;
277 }
278 return 0;
279 }
280
281private:
282 constexpr static auto find_impl(auto& self, auto& key) -> decltype(auto) {
283 if (key < self._sparse_capacity && self._sparse[key] < self._size
284 && get_key(self._dense[self._sparse[key]]) == key) {
285 return self.begin() + self._sparse[key];
286 }
287 return self.end();
288 }
289
290 constexpr static auto get_key(auto& entry) -> auto& {
291 if constexpr (is_map) {
292 return entry.first;
293 } else {
294 return entry;
295 }
296 }
297
298 constexpr static auto get_value(auto& entry) -> auto& {
299 if constexpr (is_map) {
300 return entry.second;
301 } else {
302 return entry;
303 }
304 }
305
306 std::vector<value_type, Allocator> _dense;
307 std::vector<key_type> _sparse;
308
309 key_type _size{};
310 size_t _sparse_capacity{};
311};
312
313} // namespace co_ecs::detail
Definition component.hpp:17
STL namespace.