Cogs.Core
SmallVector.h
1#pragma once
2
3#include "../Memory/Allocator.h"
4
5#include <cstring>
6#include <utility>
7#include <initializer_list>
8
9namespace Cogs
10{
11 namespace Collections
12 {
13 class COGSFOUNDATION_API SmallVectorBase
14 {
15 protected:
16 SmallVectorBase(void * data, size_t capacity, Memory::Allocator * allocator) :
17 firstPtr(data),
18 lastPtr(data),
19 capacityPtr(static_cast<uint8_t*>(firstPtr) + capacity),
20 allocator(allocator)
21 {}
22
23 void growPod(const void* firstElement, const size_t minSizeBytes);
24
25 size_t sizeInBytes() const
26 {
27 return static_cast<size_t>(static_cast<uint8_t*>(lastPtr) - static_cast<uint8_t*>(firstPtr));
28 }
29
30 size_t capacityInBytes() const
31 {
32 return static_cast<size_t>(static_cast<uint8_t*>(capacityPtr) - static_cast<uint8_t*>(firstPtr));
33 }
34
35 void * firstPtr;
36 void * lastPtr;
37 void * capacityPtr;
38 Memory::Allocator * allocator;
39 };
40
41 template<typename T>
43 {
44 public:
45 bool empty() const { return lastPtr == firstPtr; }
46 size_t size() const { return end() - begin(); }
47 size_t capacity() const { return static_cast<T *>(capacityPtr) - begin(); }
48
49 T * data() { return static_cast<T *>(firstPtr); }
50 const T * data() const { return static_cast<const T *>(firstPtr); }
51
52 T * begin() { return static_cast<T *>(firstPtr); }
53 T * end() { return static_cast<T *>(lastPtr); }
54
55 const T * begin() const { return static_cast<const T *>(firstPtr); }
56 const T * end() const { return static_cast<const T *>(lastPtr); }
57 const T * cbegin() const { return static_cast<const T *>(firstPtr); }
58 const T * cend() const { return static_cast<const T *>(lastPtr); }
59
60 T & operator[](const size_t index)
61 {
62 assert(index < size() && "Index out of bounds");
63 return begin()[index];
64 }
65
66 const T & operator[](const size_t index) const
67 {
68 assert(index < size() && "Index out of bounds");
69 return begin()[index];
70 }
71
72 T & back()
73 {
74 assert(!empty());
75 return end()[-1];
76 }
77
78 const T & back() const
79 {
80 assert(!empty());
81 return end()[-1];
82 }
83
84 T & front()
85 {
86 assert(!empty());
87 return begin()[0];
88 }
89
90 const T & front() const
91 {
92 assert(!empty());
93 return begin()[0];
94 }
95
96 protected:
97 SmallVectorTemplateBase(size_t capacity, Memory::Allocator * allocator) :
98 SmallVectorBase(&firstElement, capacity * sizeof(T), allocator) {}
99
100 bool isSmall() const { return firstPtr == &firstElement; }
101
102 void setSize(size_t newSize)
103 {
104 assert(begin() + newSize <= capacityPtr && "Size exceeds capacity.");
105 lastPtr = begin() + newSize;
106 }
107
108 void reset()
109 {
110 firstPtr = lastPtr = capacityPtr = &firstElement;
111 }
112
113 alignas(T) uint8_t firstElement[sizeof(T)];
114 };
115
116 template<typename T, bool isPod>
118 {
119 public:
120 void push_back(const T & t)
121 {
122 if (this->size() == this->capacity()) {
123 grow();
124 }
125
126 new (this->end()) T(t);
127 this->setSize(this->size() + 1);
128 }
129
130 void push_back(T && t)
131 {
132 if (this->size() == this->capacity()) {
133 grow();
134 }
135
136 new (this->end()) T(std::move(t));
137 this->setSize(this->size() + 1);
138 }
139
140 template<typename... Args>
141 void emplace_back(Args &&... args)
142 {
143 if (this->size() == this->capacity()) {
144 grow();
145 }
146
147 new (this->end()) T(std::forward<Args>(args)...);
148 this->setSize(this->size() + 1);
149 }
150
151 void pop_back()
152 {
153 this->setSize(this->size() - 1);
154 this->back()->~T();
155 }
156
157 // Erase element.
158 void erase(const T* pos)
159 {
160 const size_t index = pos - this->begin();
161 assert(index < this->size() && "Index out of bounds");
162 (this->begin() + index)->~T();
163 this->move(this->begin()+index+1, this->end(), this->begin() + index);
164 this->setSize(this->size() - 1);
165 }
166
167 protected:
168 SmallVectorImplBase(size_t capacity, Memory::Allocator * allocator) :
169 SmallVectorTemplateBase<T>(capacity, allocator) {}
170
171 void grow(size_t minSize = 0)
172 {
173 size_t newCapacity = this->capacity() * 2;
174 if (minSize > newCapacity) newCapacity = minSize;
175
176 const size_t currentSize = this->size();
177
178 auto newBuffer = static_cast<T *>(this->allocator->allocate(newCapacity * sizeof(T)));
179
180 moveNew(this->begin(), this->end(), newBuffer);
181
182 destroy(this->begin(), this->end());
183
184 this->firstPtr = newBuffer;
185 this->lastPtr = newBuffer + currentSize;
186 this->capacityPtr = newBuffer + newCapacity;
187 }
188
189 void initialize(T* first, const T* last)
190 {
191 while (first != last) {
192 new (first++) T();
193 }
194 }
195
196 void destroy(T* first, const T* last)
197 {
198 while (first != last) {
199 (first++)->~T();
200 }
201 }
202
203 template<typename T1, typename T2>
204 void moveNew(T1 first, T1 last, T2 dest)
205 {
206 while (first != last) {
207 new (dest++) T(std::move(*first++));
208 }
209 }
210
211 template<typename T1, typename T2>
212 void move(T1 first, T1 last, T2 dest)
213 {
214 while (first != last) {
215 (*dest++) = std::move(*first++);
216 }
217 }
218
219 template<typename T1, typename T2>
220 void copy(T1 first, T1 last, T2 dest)
221 {
222 while (first != last) {
223 (*dest++) = *first++;
224 }
225 }
226
227 template<typename T1, typename T2>
228 void copyNew(T1 first, T1 last, T2 dest)
229 {
230 while (first != last) {
231 new (dest++) T(*first++);
232 }
233 }
234 };
235
236 template<typename T>
238 {
239 public:
240 void push_back(const T & t)
241 {
242 if (this->size() == this->capacity()) {
243 this->grow();
244 }
245
246 new (this->end()) T(t);
247 this->setSize(this->size() + 1);
248 }
249
250 void pop_back()
251 {
252 this->setSize(this->size() - 1);
253 }
254
255 // Erase element.
256 void erase(const T* pos)
257 {
258 const size_t index = pos - this->begin();
259 assert(index < this->size() && "Index out of bounds");
260 this->move(this->begin() + index + 1, this->end(), this->begin() + index);
261 this->setSize(this->size() - 1);
262 }
263
264 protected:
265 SmallVectorImplBase(size_t capacity, Memory::Allocator * allocator) :
266 SmallVectorTemplateBase<T>(capacity, allocator)
267 {}
268
269 void grow(size_t minSize = 0)
270 {
271 this->growPod(&this->firstElement, minSize * sizeof(T));
272 }
273
274 void initialize(T *, T *) {}
275 void destroy(T *, T *) {}
276
277 template<typename T1, typename T2>
278 void move(T1 first, T1 last, T2 dest)
279 {
280 copy(first, last, dest);
281 }
282
283 template<typename T1, typename T2>
284 void copy(T1 first, T1 last, T2 dest)
285 {
286 copyNew(first, last, dest);
287 }
288
289 template<typename T1, typename T2>
290 void copyNew(T1 first, T1 last, T2 dest)
291 {
292 std::memcpy(dest, first, (last - first) * sizeof(T));
293 }
294 };
295
296 template<typename T>
297 class SmallVectorImpl : public SmallVectorImplBase<T, std::is_trivially_copyable<T>::value>
298 {
299 public:
301 {
302 this->destroy(this->begin(), this->end());
303
304 if (!this->isSmall()) {
305 release();
306 }
307 }
308
309 SmallVectorImpl & operator=(const SmallVectorImpl & other)
310 {
311 if (this == &other) return *this;
312
313 clear();
314
315 if (this->capacity() < other.capacity()) {
316 this->grow(other.size());
317 }
318
319 this->copyNew(other.begin(), other.end(), this->begin());
320
321 this->setSize(other.size());
322
323 return *this;
324 }
325
326 SmallVectorImpl & operator=(SmallVectorImpl && other) noexcept
327 {
328 if (this == &other) return *this;
329
330 this->destroy(this->begin(), this->end());
331
332 if (!other.isSmall()) {
333 if (!this->isSmall()) {
334 release();
335 }
336
337 this->firstPtr = other.firstPtr;
338 this->lastPtr = other.lastPtr;
339 this->capacityPtr = other.capacityPtr;
340 this->allocator = other.allocator;
341
342 other.reset();
343
344 return *this;
345 }
346
347 if (this->capacity() < other.capacity()) {
348 this->grow(other.size());
349 }
350
351 this->move(other.begin(), other.end(), this->begin());
352
353 this->setSize(other.size());
354
355 return *this;
356 }
357
358 void resize(size_t newSize)
359 {
360 if (newSize > this->capacity()) {
361 this->grow(newSize);
362 }
363
364 this->setSize(newSize);
365 }
366
367 void reserve(size_t newCapacity)
368 {
369 if (newCapacity >= this->capacity()) {
370 this->grow(newCapacity);
371 }
372 }
373
374 void clear()
375 {
376 this->destroy(this->begin(), this->end());
377
378 this->setSize(0);
379 }
380
381 template<typename T1>
382 void assign(T1 first, T1 last)
383 {
384 clear();
385 const size_t newSize = last - first;
386
387 if (newSize > this->capacity()) {
388 this->grow(newSize);
389 }
390
391 this->copyNew(first, last, this->data());
392 this->setSize(newSize);
393 }
394
395 void assign(std::initializer_list<T> il)
396 {
397 assign(il.begin(), il.end());
398 }
399
400 template<typename T1>
401 void append(T1 first, T1 last)
402 {
403 const size_t newSize = last - first + this->size();
404
405 if (newSize > this->capacity()) {
406 this->grow(newSize);
407 }
408
409 this->copyNew(first, last, this->end());
410 this->setSize(newSize);
411 }
412
413 void append(std::initializer_list<T> il)
414 {
415 append(il.begin(), il.end());
416 }
417
418 protected:
421 {}
422
423 void release()
424 {
425 this->allocator->deallocate(this->firstPtr, this->capacityInBytes());
426 }
427 };
428
429 template<typename T, size_t Size>
430 class SmallVector : public SmallVectorImpl<T>
431 {
432 template<typename Ts, size_t Sizes>
434 {
435 alignas(Ts) uint8_t inlineStorage[sizeof(Ts) * Sizes];
436 };
437
438 template<typename Ts> struct SmallVectorStorage<Ts, 0> {};
439 template<typename Ts> struct SmallVectorStorage<Ts, 1> {};
440
441 public:
442 // using iterator_category = std::random_access_iterator_tag;
443 using value_type = T;
444 using difference_type = ptrdiff_t;
445 using pointer = const T*;
446 using reference = const T&;
447
448 SmallVector() : SmallVectorImpl<T>(Size) {};
449 explicit SmallVector(size_t size) : SmallVectorImpl<T>(Size) { this->resize(size); }
450 SmallVector(Memory::Allocator * allocator) : SmallVectorImpl<T>(Size, allocator) {};
451
452 SmallVector(SmallVector && other) noexcept : SmallVectorImpl<T>(Size)
453 {
454 SmallVectorImpl<T>::operator=(std::move(other));
455 }
456
457 SmallVector(SmallVectorImpl<T> && other) noexcept : SmallVectorImpl<T>(Size)
458 {
459 SmallVectorImpl<T>::operator=(std::move(other));
460 }
461
462 SmallVector(const SmallVector & other) : SmallVectorImpl<T>(Size)
463 {
464 SmallVectorImpl<T>::operator=(other);
465 }
466
467 SmallVector(const SmallVectorImpl<T> & other) : SmallVectorImpl<T>(Size)
468 {
469 SmallVectorImpl<T>::operator=(other);
470 }
471
472 SmallVector(std::initializer_list<T> il) : SmallVectorImpl<T>(Size)
473 {
474 this->assign(il);
475 }
476
477 SmallVector & operator=(const SmallVector & other)
478 {
479 SmallVectorImpl<T>::operator=(other);
480 return *this;
481 }
482
483 SmallVector & operator=(SmallVector && other) noexcept
484 {
485 SmallVectorImpl<T>::operator=(std::move(other));
486 return *this;
487 }
488
489 SmallVector & operator=(SmallVectorImpl<T> && other) noexcept
490 {
491 SmallVectorImpl<T>::operator=(std::move(other));
492 return *this;
493 }
494
495 private:
496 SmallVectorStorage<T, Size> inlineStorage;
497 };
498 }
499}
Base allocator implementation.
Definition: Allocator.h:30
virtual void deallocate(void *ptr, size_t size, MemBlockType type=MemBlockType::Block)
Deallocate the memory block at the given pointer, with the given size.
Definition: Allocator.cpp:58
virtual void * allocate(size_t size, size_t alignment=0, MemBlockType type=MemBlockType::Block)
Allocate raw memory.
Definition: Allocator.cpp:29
static Allocator * overflowAllocator()
Definition: Allocator.cpp:24
Contains all Cogs related functionality.
Definition: FieldSetter.h:23