Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
darray_impl.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <initializer_list>
6#include <type_traits>
7#include <utility>
8
9#include "gaia/core/iterator.h"
10#include "gaia/core/utility.h"
11#include "gaia/mem/data_layout_policy.h"
12#include "gaia/mem/mem_sani.h"
13#include "gaia/mem/mem_utils.h"
14
15namespace gaia {
16 namespace cnt {
17 namespace darr_detail {
18 using difference_type = uint32_t;
19 using size_type = uint32_t;
20 } // namespace darr_detail
21
24 template <typename T, typename Allocator = mem::DefaultAllocatorAdaptor>
25 class darr {
26 public:
27 using value_type = T;
28 using reference = T&;
29 using const_reference = const T&;
30 using pointer = T*;
31 using const_pointer = const T*;
33 using difference_type = darr_detail::difference_type;
34 using size_type = darr_detail::size_type;
35
36 using iterator = pointer;
37 using const_iterator = const_pointer;
39
40 static constexpr size_t value_size = sizeof(T);
41
42 private:
43 uint8_t* m_pData = nullptr;
44 size_type m_cnt = size_type(0);
45 size_type m_cap = size_type(0);
46
47 void try_grow() {
48 const auto cnt = size();
49 const auto cap = capacity();
50
51 // Unless we reached the capacity don't do anything
52 if GAIA_LIKELY (cap != 0 && cnt < cap)
53 return;
54
55 // If no data is allocated go with at least 4 elements
56 if GAIA_UNLIKELY (m_pData == nullptr) {
57 m_pData = view_policy::template alloc<Allocator>(m_cap = 4);
58 return;
59 }
60
61 // We increase the capacity in multiples of 1.5 which is about the golden ratio (1.618).
62 // This effectively means we prefer more frequent allocations over memory fragmentation.
63 m_cap = (cap * 3 + 1) / 2;
64
65 auto* pDataOld = m_pData;
66 m_pData = view_policy::template alloc<Allocator>(m_cap);
67 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, m_cap, cnt);
68 mem::move_elements<T, false>(m_pData, pDataOld, cnt, 0, m_cap, cap);
69 view_policy::template free<Allocator>(pDataOld, cap, cnt);
70 }
71
72 public:
73 darr() noexcept = default;
74 darr(core::zero_t) noexcept {}
75
76 darr(size_type count, const_reference value) {
77 resize(count, value);
78 }
79
80 darr(size_type count) {
81 resize(count);
82 }
83
84 template <typename InputIt>
85 darr(InputIt first, InputIt last) {
86 const auto count = (size_type)core::distance(first, last);
87 resize(count);
88
89 if constexpr (std::is_pointer_v<InputIt>) {
90 for (size_type i = 0; i < count; ++i)
91 operator[](i) = first[i];
92 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
93 for (size_type i = 0; i < count; ++i)
94 operator[](i) = *(first[i]);
95 } else {
96 size_type i = 0;
97 for (auto it = first; it != last; ++it)
98 operator[](++i) = *it;
99 }
100 }
101
102 darr(std::initializer_list<T> il): darr(il.begin(), il.end()) {}
103
104 darr(const darr& other): darr(other.begin(), other.end()) {}
105
106 darr(darr&& other) noexcept: m_pData(other.m_pData), m_cnt(other.m_cnt), m_cap(other.m_cap) {
107 other.m_pData = nullptr;
108 other.m_cnt = size_type(0);
109 other.m_cap = size_type(0);
110 }
111
112 darr& operator=(std::initializer_list<T> il) {
113 *this = darr(il.begin(), il.end());
114 return *this;
115 }
116
117 darr& operator=(const darr& other) {
118 GAIA_ASSERT(core::addressof(other) != this);
119
120 resize(other.size());
121 mem::copy_elements<T, false>(
122 m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
123
124 return *this;
125 }
126
127 darr& operator=(darr&& other) noexcept {
128 GAIA_ASSERT(core::addressof(other) != this);
129
130 // Release previously allocated memory if there was anything
131 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
132
133 m_pData = other.m_pData;
134 m_cnt = other.m_cnt;
135 m_cap = other.m_cap;
136
137 other.m_pData = nullptr;
138 other.m_cnt = size_type(0);
139 other.m_cap = size_type(0);
140
141 return *this;
142 }
143
144 ~darr() {
145 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
146 }
147
148 GAIA_CLANG_WARNING_PUSH()
149 // Memory is aligned so we can silence this warning
150 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
151
152 GAIA_NODISCARD pointer data() noexcept {
153 return reinterpret_cast<pointer>(m_pData);
154 }
155
156 GAIA_NODISCARD const_pointer data() const noexcept {
157 return reinterpret_cast<const_pointer>(m_pData);
158 }
159
160 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
161 GAIA_ASSERT(pos < size());
162 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
163 }
164
165 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
166 GAIA_ASSERT(pos < size());
167 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
168 }
169
170 GAIA_CLANG_WARNING_POP()
171
172 void reserve(size_type cap) {
173 if (cap <= m_cap)
174 return;
175
176 auto* pDataOld = m_pData;
177 m_pData = view_policy::template alloc<Allocator>(cap);
178 if (pDataOld != nullptr) {
179 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cap, m_cnt);
180 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, cap, m_cap);
181 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
182 }
183
184 m_cap = cap;
185 }
186
187 void resize(size_type count) {
188 if (count == m_cnt)
189 return;
190
191 // Fresh allocation
192 if (m_pData == nullptr) {
193 if (count > 0) {
194 m_pData = view_policy::template alloc<Allocator>(count);
195 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
196 core::call_ctor_n(m_pData, count);
197 m_cap = count;
198 m_cnt = count;
199 }
200 return;
201 }
202
203 // Resizing to a smaller size
204 if (count < m_cnt) {
205 // Destroy elements at the end
206 core::call_dtor_n(&data()[count], m_cnt - count);
207 GAIA_MEM_SANI_POP_N(value_size, m_pData, m_cap, m_cnt, m_cnt - count);
208
209 m_cnt = count;
210 return;
211 }
212
213 // Resizing to a bigger size but still within allocated capacity
214 if (count <= m_cap) {
215 // Construct new elements
216 GAIA_MEM_SANI_PUSH_N(value_size, m_pData, m_cap, m_cnt, count - m_cnt);
217 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
218
219 m_cnt = count;
220 return;
221 }
222
223 auto* pDataOld = m_pData;
224 m_pData = view_policy::template alloc<Allocator>(count);
225 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
226 // Move old data to the new location
227 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, count, m_cap);
228 // Default-construct new items
229 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
230 // Release old memory
231 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
232
233 m_cap = count;
234 m_cnt = count;
235 }
236
237 void resize(size_type count, const_reference value) {
238 const auto oldCount = m_cnt;
239 resize(count);
240
241 if constexpr (std::is_copy_constructible_v<value_type>) {
242 const value_type valueCopy = value;
243 for (size_type i = oldCount; i < m_cnt; ++i)
244 operator[](i) = valueCopy;
245 } else {
246 for (size_type i = oldCount; i < m_cnt; ++i)
247 operator[](i) = value;
248 }
249 }
250
251 void push_back(const T& arg) {
252 try_grow();
253
254 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
255 auto* ptr = &data()[m_cnt++];
256 core::call_ctor(ptr, arg);
257 }
258
259 void push_back(T&& arg) {
260 try_grow();
261
262 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
263 auto* ptr = &data()[m_cnt++];
264 core::call_ctor(ptr, GAIA_MOV(arg));
265 }
266
267 template <typename... Args>
268 decltype(auto) emplace_back(Args&&... args) {
269 try_grow();
270
271 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
272 auto* ptr = &data()[m_cnt++];
273 core::call_ctor(ptr, GAIA_FWD(args)...);
274 return (reference)*ptr;
275 }
276
277 void pop_back() noexcept {
278 GAIA_ASSERT(!empty());
279
280 auto* ptr = &data()[m_cnt - 1];
281 core::call_dtor(ptr);
282 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
283
284 --m_cnt;
285 }
286
290 iterator insert(iterator pos, const T& arg) {
291 GAIA_ASSERT(pos >= data());
292 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
293
294 const auto idxSrc = (size_type)core::distance(begin(), pos);
295 try_grow();
296 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
297
298 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
299 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
300 auto* ptr = &data()[m_cnt];
301 core::call_ctor(ptr, arg);
302
303 ++m_cnt;
304
305 return iterator(&data()[idxSrc]);
306 }
307
311 iterator insert(iterator pos, T&& arg) {
312 GAIA_ASSERT(pos >= data());
313 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
314
315 const auto idxSrc = (size_type)core::distance(begin(), pos);
316 try_grow();
317 const auto idxDst = (size_type)core::distance(begin(), end());
318
319 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
320 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
321 auto* ptr = &data()[idxSrc];
322 core::call_ctor(ptr, GAIA_MOV(arg));
323
324 ++m_cnt;
325
326 return iterator(&data()[idxSrc]);
327 }
328
331 iterator erase(iterator pos) noexcept {
332 GAIA_ASSERT(pos >= data());
333 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
334
335 if (empty())
336 return end();
337
338 const auto idxSrc = (size_type)core::distance(begin(), pos);
339 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
340
341 mem::shift_elements_left<T, false>(m_pData, idxDst, idxSrc, m_cap);
342 // Destroy if it's the last element
343 auto* ptr = &data()[m_cnt - 1];
344 core::call_dtor(ptr);
345 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
346
347 --m_cnt;
348
349 return iterator(&data()[idxSrc]);
350 }
351
355 iterator erase(iterator first, iterator last) noexcept {
356 GAIA_ASSERT(first >= data())
357 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
358 GAIA_ASSERT(last > first);
359 GAIA_ASSERT(last <= iterator(data() + size()));
360
361 if (empty())
362 return end();
363
364 const auto idxSrc = (size_type)core::distance(begin(), first);
365 const auto idxDst = size();
366 const auto cnt = (size_type)(last - first);
367
368 mem::shift_elements_left_fast<T, false>(m_pData, idxDst, idxSrc, cnt, m_cap);
369 // Destroy if it's the last element
370 auto* ptr = &data()[m_cnt - cnt];
371 core::call_dtor_n(ptr, cnt);
372 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, cnt);
373
374 m_cnt -= cnt;
375
376 return iterator(&data()[idxSrc]);
377 }
378
379 void clear() noexcept {
380 resize(0);
381 }
382
383 void shrink_to_fit() {
384 const auto cap = capacity();
385 const auto cnt = size();
386
387 if (cap == cnt)
388 return;
389
390 auto* pDataOld = m_pData;
391 m_pData = view_policy::template alloc<Allocator>(m_cap = cnt);
392 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, m_cap, m_cnt);
393 mem::move_elements<T, false>(m_pData, pDataOld, cnt, 0);
394 GAIA_MEM_SANI_DEL_BLOCK(value_size, pDataOld, cap, cnt);
395 view_policy::template free<Allocator>(pDataOld);
396 }
397
401 template <typename Func>
402 auto retain(Func&& func) {
403 size_type erased = 0;
404 size_type idxDst = 0;
405 size_type idxSrc = 0;
406
407 while (idxSrc < m_cnt) {
408 if (func(operator[](idxSrc))) {
409 if (idxDst < idxSrc) {
410 mem::move_element<T, false>(m_pData, m_pData, idxDst, idxSrc, m_cap, m_cap);
411 auto* ptr = &data()[idxSrc];
412 core::call_dtor(ptr);
413 }
414 ++idxDst;
415 } else {
416 auto* ptr = &data()[idxSrc];
417 core::call_dtor(ptr);
418 ++erased;
419 }
420
421 ++idxSrc;
422 }
423
424 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, erased);
425
426 m_cnt -= erased;
427 return idxDst;
428 }
429
430 GAIA_NODISCARD size_type size() const noexcept {
431 return m_cnt;
432 }
433
434 GAIA_NODISCARD bool empty() const noexcept {
435 return size() == 0;
436 }
437
438 GAIA_NODISCARD size_type capacity() const noexcept {
439 return m_cap;
440 }
441
442 GAIA_NODISCARD size_type max_size() const noexcept {
443 return static_cast<size_type>(-1);
444 }
445
446 GAIA_NODISCARD decltype(auto) front() noexcept {
447 GAIA_ASSERT(!empty());
448 return (reference)*begin();
449 }
450
451 GAIA_NODISCARD decltype(auto) front() const noexcept {
452 GAIA_ASSERT(!empty());
453 return (const_reference)*begin();
454 }
455
456 GAIA_NODISCARD decltype(auto) back() noexcept {
457 GAIA_ASSERT(!empty());
458 return (reference)(operator[](m_cnt - 1));
459 }
460
461 GAIA_NODISCARD decltype(auto) back() const noexcept {
462 GAIA_ASSERT(!empty());
463 return (const_reference) operator[](m_cnt - 1);
464 }
465
466 GAIA_NODISCARD auto begin() noexcept {
467 return iterator(data());
468 }
469
470 GAIA_NODISCARD auto begin() const noexcept {
471 return cbegin();
472 }
473
474 GAIA_NODISCARD auto cbegin() const noexcept {
475 return const_iterator(data());
476 }
477
478 GAIA_NODISCARD auto rbegin() noexcept {
479 return iterator((pointer)&back());
480 }
481
482 GAIA_NODISCARD auto rbegin() const noexcept {
483 return const_iterator((const_pointer)&back());
484 }
485
486 GAIA_NODISCARD auto crbegin() const noexcept {
487 return const_iterator((const_pointer)&back());
488 }
489
490 GAIA_NODISCARD auto end() noexcept {
491 return iterator(data() + size());
492 }
493
494 GAIA_NODISCARD auto end() const noexcept {
495 return const_iterator(data() + size());
496 }
497
498 GAIA_NODISCARD auto cend() const noexcept {
499 return const_iterator(data() + size());
500 }
501
502 GAIA_NODISCARD auto rend() noexcept {
503 return iterator(data() - 1);
504 }
505
506 GAIA_NODISCARD auto rend() const noexcept {
507 return const_iterator(data() - 1);
508 }
509
510 GAIA_NODISCARD auto crend() const noexcept {
511 return const_iterator(data() - 1);
512 }
513
514 GAIA_NODISCARD bool operator==(const darr& other) const noexcept {
515 if (m_cnt != other.m_cnt)
516 return false;
517 const size_type n = size();
518 for (size_type i = 0; i < n; ++i)
519 if (!(operator[](i) == other[i]))
520 return false;
521 return true;
522 }
523
524 GAIA_NODISCARD constexpr bool operator!=(const darr& other) const noexcept {
525 return !operator==(other);
526 }
527 };
528 } // namespace cnt
529
530} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
iterator insert(iterator pos, T &&arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:311
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:290
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_impl.h:355
auto retain(Func &&func)
Removes all elements that fail the predicate.
Definition darray_impl.h:402
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_impl.h:331
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition utility.h:87
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:112