Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
ArenaMap.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/Assert.h"
5#include "../Memory/Globals.h"
6#include "../Memory/Memory.h"
7#include "ArenaMapKey.h"
8#include "ContainersExport.h"
9
10namespace SC
11{
12template <typename T>
13struct ArenaMap;
14} // namespace SC
15
18
29template <typename T>
30struct SC::ArenaMap
31{
32 using Key = ArenaMapKey<T>;
33 using Gen = typename Key::Generation;
34
35 ArenaMap(Globals::Type globalsType = Globals::Global) : globalsType(globalsType) {}
36
37 ~ArenaMap() { clear(); }
38
39 ArenaMap(const ArenaMap& other) : globalsType(other.globalsType) { *this = other; }
40
41 ArenaMap(ArenaMap&& other) : globalsType(other.globalsType) { *this = move(other); }
42
43 ArenaMap& operator=(const ArenaMap& other)
44 {
45 clear();
46 auto& allocator = Globals::get(globalsType).allocator;
47 T* newItems = reinterpret_cast<T*>(allocator.allocate(this, other.itemsSize * sizeof(T), alignof(T)));
48 Gen* newGens = reinterpret_cast<Gen*>(allocator.allocate(this, other.itemsSize * sizeof(Gen), alignof(Gen)));
49 SC_ASSERT_RELEASE(newItems);
50 SC_ASSERT_RELEASE(newGens);
51 ::memset(newGens, 0, other.itemsSize * sizeof(Gen));
52 items = newItems;
53 generations = newGens;
54 itemsSize = other.itemsSize;
55 for (size_t idx = 0; idx < other.itemsSize; ++idx)
56 {
57 if (other.generations[idx].used)
58 {
59 placementNew(items[idx], other.items[idx]);
60 }
61 generations[idx] = other.generations[idx];
62 }
63 numUsed = other.numUsed;
64 return *this;
65 }
66
67 ArenaMap& operator=(ArenaMap&& other)
68 {
69 clear();
70 items = other.items;
71 itemsSize = other.itemsSize;
72 other.items = nullptr;
73 other.itemsSize = 0;
74 generations = other.generations;
75 other.generations = nullptr;
76 numUsed = other.numUsed;
77 other.numUsed = 0;
78 return *this;
79 }
80
82 uint32_t getNumAllocated() const { return static_cast<uint32_t>(itemsSize); }
83
84 template <typename MapType>
86 {
87 MapType* map = nullptr;
88 uint32_t index = 0;
89
90 void operator++()
91 {
92 const auto numAllocated = map->getNumAllocated();
93 for (++index; index < numAllocated; ++index)
94 {
95 if (map->generations[index].used)
96 {
97 break;
98 }
99 }
100 }
101
102 bool operator==(ArenaMapIterator it) const
103 {
104 SC_ASSERT_DEBUG(it.map == map and map != nullptr);
105 return it.index == index;
106 }
107 bool operator!=(ArenaMapIterator it) const
108 {
109 SC_ASSERT_DEBUG(it.map == map and map != nullptr);
110 return it.index != index;
111 }
112
113 auto& operator*() const { return map->items[index]; }
114 auto* operator->() const { return &map->items[index]; }
115 };
118
119 ConstIterator cbegin() const { return begin(); }
120 ConstIterator cend() const { return end(); }
121 ConstIterator begin() const
122 {
123 for (size_t idx = 0; idx < getNumAllocated(); ++idx)
124 {
125 if (generations[idx].used)
126 {
127 return {this, static_cast<uint32_t>(idx)};
128 }
129 }
130 return end();
131 }
132
133 ConstIterator end() const { return {this, getNumAllocated()}; }
134
135 Iterator begin()
136 {
137 for (size_t idx = 0; idx < getNumAllocated(); ++idx)
138 {
139 if (generations[idx].used)
140 {
141 return {this, static_cast<uint32_t>(idx)};
142 }
143 }
144 return end();
145 }
146
147 Iterator end() { return {this, getNumAllocated()}; }
148
149 void clear()
150 {
151 for (size_t idx = 0; idx < itemsSize; ++idx)
152 {
153 if (generations[idx].used)
154 {
155 items[idx].~T();
156 }
157 generations[idx].used = 0;
158 }
159 Globals::get(globalsType).allocator.release(items);
160 items = nullptr;
161 itemsSize = 0;
162 Globals::get(globalsType).allocator.release(generations);
163 generations = nullptr;
164 numUsed = 0;
165 }
166
168 [[nodiscard]] size_t size() const { return numUsed; }
169
171 [[nodiscard]] size_t capacity() const { return itemsSize; }
172
174 [[nodiscard]] bool isFull() const { return itemsSize == numUsed; }
175
180 [[nodiscard]] bool resize(size_t newSize)
181 {
182 if (numUsed != 0)
183 return false;
184 if (newSize > Key::MaxIndex)
185 return false;
186
187 Globals::get(globalsType).allocator.release(items);
188 items = nullptr;
189 Globals::get(globalsType).allocator.release(generations);
190 generations = nullptr;
191 auto& allocator = Globals::get(globalsType).allocator;
192 T* newItems = reinterpret_cast<T*>(allocator.allocate(this, newSize * sizeof(T), alignof(T)));
193 if (newItems == nullptr)
194 return false;
195 items = newItems;
196 Gen* newGens = reinterpret_cast<Gen*>(allocator.allocate(this, newSize * sizeof(Gen), alignof(Gen)));
197 if (newGens == nullptr)
198 return false;
199 ::memset(newGens, 0, newSize * sizeof(Gen));
200 generations = newGens;
201 itemsSize = newSize;
202 numUsed = 0;
203 return true;
204 }
205
206 template <typename Value>
207 [[nodiscard]] Key insert(const Value& object)
208 {
209 Key key = allocateNewKeySlot();
210 if (key.isValid())
211 {
212 placementNew(items[key.index], object);
213 }
214 return key;
215 }
216
217 [[nodiscard]] Key allocate()
218 {
219 Key key = allocateNewKeySlot();
220 if (key.isValid())
221 {
222 placementNew(items[key.index]);
223 }
224 return key;
225 }
226
227 template <typename Value>
228 [[nodiscard]] Key insert(Value&& object)
229 {
230 Key key = allocateNewKeySlot();
231 if (key.isValid())
232 {
233 placementNew(items[key.index], move(object));
234 }
235 return key;
236 }
237
238 [[nodiscard]] bool containsKey(Key key) const
239 {
240 return key.isValid() and generations[key.index].used != 0 and
241 generations[key.index].generation == key.generation.generation;
242 }
243
244 template <typename ComparableToValue>
245 [[nodiscard]] bool containsValue(const ComparableToValue& value, Key* optionalKey = nullptr) const
246 {
247 for (size_t idx = 0; idx < itemsSize; ++idx)
248 {
249 if (generations[idx].used and items[idx] == value)
250 {
251 if (optionalKey)
252 {
253 optionalKey->index = static_cast<uint32_t>(idx);
254 optionalKey->generation = generations[idx];
255 }
256 return true;
257 }
258 }
259 return false;
260 }
261
262 [[nodiscard]] bool remove(Key key)
263 {
264 if (generations[key.index] != key.generation)
265 return false;
266 if (generations[key.index].generation + 1 <= Key::MaxGenerations)
267 {
268 generations[key.index].generation++;
269 }
270 else
271 {
272 // TODO: Should we give option of failing on generation overflow?
273 generations[key.index].generation++;
274 }
275 generations[key.index].used = 0;
276 items[key.index].~T();
277 numUsed--;
278 return true;
279 }
280
281 [[nodiscard]] T* get(Key key)
282 {
283 if (generations[key.index] != key.generation)
284 return nullptr;
285 return &items[key.index];
286 }
287
288 [[nodiscard]] const T* get(Key key) const
289 {
290 if (generations[key.index] != key.generation)
291 return nullptr;
292 return &items[key.index];
293 }
294
295 private:
296 T* items = nullptr;
297 size_t itemsSize = 0;
298
299 Gen* generations = nullptr;
300
301 uint32_t numUsed = 0;
302
303 Globals::Type globalsType;
304
305 [[nodiscard]] Key allocateNewKeySlot()
306 {
307 for (size_t idx = 0; idx < itemsSize; ++idx)
308 {
309 if (generations[idx].used == 0)
310 {
311 generations[idx].used = 1;
312
313 Key key;
314 key.generation = generations[idx];
315 key.index = static_cast<uint32_t>(idx);
316 numUsed++;
317 return key;
318 }
319 }
320 return {};
321 }
322};
#define SC_ASSERT_DEBUG(e)
Assert expression e to be true.
Definition Assert.h:63
#define SC_ASSERT_RELEASE(e)
Assert expression e to be true.
Definition Assert.h:48
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:273
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition PrimitiveTypes.h:29
Definition ArenaMap.h:86
A sparse vector keeping objects at a stable memory location.
Definition ArenaMapKey.h:10
bool resize(size_t newSize)
Changes the size of the arena.
Definition ArenaMap.h:180
size_t size() const
Get the number of used slots in the arena.
Definition ArenaMap.h:168
uint32_t getNumAllocated() const
Get the maximum number of objects that can be stored in this map.
Definition ArenaMap.h:82
size_t capacity() const
Get the total number slots in the arena.
Definition ArenaMap.h:171
bool isFull() const
Returns true if size() == capacity(), that means the arena is full.
Definition ArenaMap.h:174
static Globals & get(Type type)
Obtains current set of Globals.
Type
Definition Globals.h:40
@ Global
Shared globals (NOT thread-safe)
Definition Globals.h:41
void release(void *memory)
Free memory allocated by MemoryAllocator::allocate and / or reallocated by MemoryAllocator::reallocat...
Definition Memory.h:87