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);
78 for (auto it: *this)
79 *it = value;
80 }
81
82 darr(size_type count) {
83 resize(count);
84 }
85
86 template <typename InputIt>
87 darr(InputIt first, InputIt last) {
88 const auto count = (size_type)core::distance(first, last);
89 resize(count);
90
91 if constexpr (std::is_pointer_v<InputIt>) {
92 for (size_type i = 0; i < count; ++i)
93 operator[](i) = first[i];
94 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
95 for (size_type i = 0; i < count; ++i)
96 operator[](i) = *(first[i]);
97 } else {
98 size_type i = 0;
99 for (auto it = first; it != last; ++it)
100 operator[](++i) = *it;
101 }
102 }
103
104 darr(std::initializer_list<T> il): darr(il.begin(), il.end()) {}
105
106 darr(const darr& other): darr(other.begin(), other.end()) {}
107
108 darr(darr&& other) noexcept: m_pData(other.m_pData), m_cnt(other.m_cnt), m_cap(other.m_cap) {
109 other.m_pData = nullptr;
110 other.m_cnt = size_type(0);
111 other.m_cap = size_type(0);
112 }
113
114 darr& operator=(std::initializer_list<T> il) {
115 *this = darr(il.begin(), il.end());
116 return *this;
117 }
118
119 darr& operator=(const darr& other) {
120 GAIA_ASSERT(core::addressof(other) != this);
121
122 resize(other.size());
123 mem::copy_elements<T, false>(
124 m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
125
126 return *this;
127 }
128
129 darr& operator=(darr&& other) noexcept {
130 GAIA_ASSERT(core::addressof(other) != this);
131
132 // Release previously allocated memory if there was anything
133 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
134
135 m_pData = other.m_pData;
136 m_cnt = other.m_cnt;
137 m_cap = other.m_cap;
138
139 other.m_pData = nullptr;
140 other.m_cnt = size_type(0);
141 other.m_cap = size_type(0);
142
143 return *this;
144 }
145
146 ~darr() {
147 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
148 }
149
150 GAIA_CLANG_WARNING_PUSH()
151 // Memory is aligned so we can silence this warning
152 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
153
154 GAIA_NODISCARD pointer data() noexcept {
155 return reinterpret_cast<pointer>(m_pData);
156 }
157
158 GAIA_NODISCARD const_pointer data() const noexcept {
159 return reinterpret_cast<const_pointer>(m_pData);
160 }
161
162 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
163 GAIA_ASSERT(pos < size());
164 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
165 }
166
167 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
168 GAIA_ASSERT(pos < size());
169 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
170 }
171
172 GAIA_CLANG_WARNING_POP()
173
174 void reserve(size_type cap) {
175 if (cap <= m_cap)
176 return;
177
178 auto* pDataOld = m_pData;
179 m_pData = view_policy::template alloc<Allocator>(cap);
180 if (pDataOld != nullptr) {
181 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cap, m_cnt);
182 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, cap, m_cap);
183 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
184 }
185
186 m_cap = cap;
187 }
188
189 void resize(size_type count) {
190 if (count == m_cnt)
191 return;
192
193 // Fresh allocation
194 if (m_pData == nullptr) {
195 if (count > 0) {
196 m_pData = view_policy::template alloc<Allocator>(count);
197 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
198 core::call_ctor_n(m_pData, count);
199 m_cap = count;
200 m_cnt = count;
201 }
202 return;
203 }
204
205 // Resizing to a smaller size
206 if (count < m_cnt) {
207 // Destroy elements at the end
208 core::call_dtor_n(&data()[count], m_cnt - count);
209 GAIA_MEM_SANI_POP_N(value_size, m_pData, m_cap, m_cnt, m_cnt - count);
210
211 m_cnt = count;
212 return;
213 }
214
215 // Resizing to a bigger size but still within allocated capacity
216 if (count <= m_cap) {
217 // Construct new elements
218 GAIA_MEM_SANI_PUSH_N(value_size, m_pData, m_cap, m_cnt, count - m_cnt);
219 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
220
221 m_cnt = count;
222 return;
223 }
224
225 auto* pDataOld = m_pData;
226 m_pData = view_policy::template alloc<Allocator>(count);
227 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
228 // Move old data to the new location
229 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, count, m_cap);
230 // Default-construct new items
231 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
232 // Release old memory
233 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
234
235 m_cap = count;
236 m_cnt = count;
237 }
238
239 void push_back(const T& arg) {
240 try_grow();
241
242 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
243 auto* ptr = &data()[m_cnt++];
244 core::call_ctor(ptr, arg);
245 }
246
247 void push_back(T&& arg) {
248 try_grow();
249
250 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
251 auto* ptr = &data()[m_cnt++];
252 core::call_ctor(ptr, GAIA_MOV(arg));
253 }
254
255 template <typename... Args>
256 decltype(auto) emplace_back(Args&&... args) {
257 try_grow();
258
259 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
260 auto* ptr = &data()[m_cnt++];
261 core::call_ctor(ptr, GAIA_FWD(args)...);
262 return (reference)*ptr;
263 }
264
265 void pop_back() noexcept {
266 GAIA_ASSERT(!empty());
267
268 auto* ptr = &data()[m_cnt];
269 core::call_dtor(ptr);
270 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
271
272 --m_cnt;
273 }
274
278 iterator insert(iterator pos, const T& arg) {
279 GAIA_ASSERT(pos >= data());
280 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
281
282 const auto idxSrc = (size_type)core::distance(begin(), pos);
283 try_grow();
284 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
285
286 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
287 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
288 auto* ptr = &data()[m_cnt];
289 core::call_ctor(ptr, arg);
290
291 ++m_cnt;
292
293 return iterator(&data()[idxSrc]);
294 }
295
299 iterator insert(iterator pos, T&& arg) {
300 GAIA_ASSERT(pos >= data());
301 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
302
303 const auto idxSrc = (size_type)core::distance(begin(), pos);
304 try_grow();
305 const auto idxDst = (size_type)core::distance(begin(), end());
306
307 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
308 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
309 auto* ptr = &data()[idxSrc];
310 core::call_ctor(ptr, GAIA_MOV(arg));
311
312 ++m_cnt;
313
314 return iterator(&data()[idxSrc]);
315 }
316
319 iterator erase(iterator pos) noexcept {
320 GAIA_ASSERT(pos >= data());
321 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
322
323 if (empty())
324 return end();
325
326 const auto idxSrc = (size_type)core::distance(begin(), pos);
327 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
328
329 mem::shift_elements_left<T, false>(m_pData, idxDst, idxSrc, m_cap);
330 // Destroy if it's the last element
331 auto* ptr = &data()[m_cnt - 1];
332 core::call_dtor(ptr);
333 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
334
335 --m_cnt;
336
337 return iterator(&data()[idxSrc]);
338 }
339
343 iterator erase(iterator first, iterator last) noexcept {
344 GAIA_ASSERT(first >= data())
345 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
346 GAIA_ASSERT(last > first);
347 GAIA_ASSERT(last <= iterator(data() + size()));
348
349 if (empty())
350 return end();
351
352 const auto idxSrc = (size_type)core::distance(begin(), first);
353 const auto idxDst = size();
354 const auto cnt = (size_type)(last - first);
355
356 mem::shift_elements_left_fast<T, false>(m_pData, idxDst, idxSrc, cnt, m_cap);
357 // Destroy if it's the last element
358 auto* ptr = &data()[m_cnt - cnt];
359 core::call_dtor_n(ptr, cnt);
360 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, cnt);
361
362 m_cnt -= cnt;
363
364 return iterator(&data()[idxSrc]);
365 }
366
367 void clear() noexcept {
368 resize(0);
369 }
370
371 void shrink_to_fit() {
372 const auto cap = capacity();
373 const auto cnt = size();
374
375 if (cap == cnt)
376 return;
377
378 auto* pDataOld = m_pData;
379 m_pData = view_policy::template alloc<Allocator>(m_cap = cnt);
380 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, m_cap, m_cnt);
381 mem::move_elements<T, false>(m_pData, pDataOld, cnt, 0);
382 GAIA_MEM_SANI_DEL_BLOCK(value_size, pDataOld, cap, cnt);
383 view_policy::template free<Allocator>(pDataOld);
384 }
385
389 template <typename Func>
390 auto retain(Func&& func) {
391 size_type erased = 0;
392 size_type idxDst = 0;
393 size_type idxSrc = 0;
394
395 while (idxSrc < m_cnt) {
396 if (func(operator[](idxSrc))) {
397 if (idxDst < idxSrc) {
398 mem::move_element<T, false>(m_pData, m_pData, idxDst, idxSrc, m_cap, m_cap);
399 auto* ptr = &data()[idxSrc];
400 core::call_dtor(ptr);
401 }
402 ++idxDst;
403 } else {
404 auto* ptr = &data()[idxSrc];
405 core::call_dtor(ptr);
406 ++erased;
407 }
408
409 ++idxSrc;
410 }
411
412 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, erased);
413
414 m_cnt -= erased;
415 return idxDst;
416 }
417
418 GAIA_NODISCARD size_type size() const noexcept {
419 return m_cnt;
420 }
421
422 GAIA_NODISCARD bool empty() const noexcept {
423 return size() == 0;
424 }
425
426 GAIA_NODISCARD size_type capacity() const noexcept {
427 return m_cap;
428 }
429
430 GAIA_NODISCARD size_type max_size() const noexcept {
431 return static_cast<size_type>(-1);
432 }
433
434 GAIA_NODISCARD decltype(auto) front() noexcept {
435 GAIA_ASSERT(!empty());
436 return (reference)*begin();
437 }
438
439 GAIA_NODISCARD decltype(auto) front() const noexcept {
440 GAIA_ASSERT(!empty());
441 return (const_reference)*begin();
442 }
443
444 GAIA_NODISCARD decltype(auto) back() noexcept {
445 GAIA_ASSERT(!empty());
446 return (reference)(operator[](m_cnt - 1));
447 }
448
449 GAIA_NODISCARD decltype(auto) back() const noexcept {
450 GAIA_ASSERT(!empty());
451 return (const_reference) operator[](m_cnt - 1);
452 }
453
454 GAIA_NODISCARD auto begin() noexcept {
455 return iterator(data());
456 }
457
458 GAIA_NODISCARD auto begin() const noexcept {
459 return cbegin();
460 }
461
462 GAIA_NODISCARD auto cbegin() const noexcept {
463 return const_iterator(data());
464 }
465
466 GAIA_NODISCARD auto rbegin() noexcept {
467 return iterator((pointer)&back());
468 }
469
470 GAIA_NODISCARD auto rbegin() const noexcept {
471 return const_iterator((const_pointer)&back());
472 }
473
474 GAIA_NODISCARD auto crbegin() const noexcept {
475 return const_iterator((const_pointer)&back());
476 }
477
478 GAIA_NODISCARD auto end() noexcept {
479 return iterator(data() + size());
480 }
481
482 GAIA_NODISCARD auto end() const noexcept {
483 return const_iterator(data() + size());
484 }
485
486 GAIA_NODISCARD auto cend() const noexcept {
487 return const_iterator(data() + size());
488 }
489
490 GAIA_NODISCARD auto rend() noexcept {
491 return iterator(data() - 1);
492 }
493
494 GAIA_NODISCARD auto rend() const noexcept {
495 return const_iterator(data() - 1);
496 }
497
498 GAIA_NODISCARD auto crend() const noexcept {
499 return const_iterator(data() - 1);
500 }
501
502 GAIA_NODISCARD bool operator==(const darr& other) const noexcept {
503 if (m_cnt != other.m_cnt)
504 return false;
505 const size_type n = size();
506 for (size_type i = 0; i < n; ++i)
507 if (!(operator[](i) == other[i]))
508 return false;
509 return true;
510 }
511
512 GAIA_NODISCARD constexpr bool operator!=(const darr& other) const noexcept {
513 return !operator==(other);
514 }
515 };
516 } // namespace cnt
517
518} // 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:299
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:278
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_impl.h:343
auto retain(Func &&func)
Removes all elements that fail the predicate.
Definition darray_impl.h:390
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_impl.h:319
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