Gaia-ECS v1.0.0
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 {
18 namespace darr_detail {
19 using difference_type = uint32_t;
20 using size_type = uint32_t;
21 } // namespace darr_detail
23
26 template <typename T, typename Allocator = mem::DefaultAllocatorAdaptor>
27 class darr {
28 public:
30 using value_type = T;
32 using reference = T&;
34 using const_reference = const T&;
36 using pointer = T*;
38 using const_pointer = const T*;
42 using difference_type = darr_detail::difference_type;
44 using size_type = darr_detail::size_type;
45
52
54 static constexpr size_t value_size = sizeof(T);
55
56 private:
57 uint8_t* m_pData = nullptr;
58 size_type m_cnt = size_type(0);
59 size_type m_cap = size_type(0);
60
61 void try_grow() {
62 const auto cnt = size();
63 const auto cap = capacity();
64
65 // Unless we reached the capacity don't do anything
66 if GAIA_LIKELY (cap != 0 && cnt < cap)
67 return;
68
69 // If no data is allocated go with at least 4 elements
70 if GAIA_UNLIKELY (m_pData == nullptr) {
71 m_pData = view_policy::template alloc<Allocator>(m_cap = 4);
72 return;
73 }
74
75 // We increase the capacity in multiples of 1.5 which is about the golden ratio (1.618).
76 // This effectively means we prefer more frequent allocations over memory fragmentation.
77 m_cap = (cap * 3 + 1) / 2;
78
79 auto* pDataOld = m_pData;
80 m_pData = view_policy::template alloc<Allocator>(m_cap);
81 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, m_cap, cnt);
82 mem::move_elements<T, false>(m_pData, pDataOld, cnt, 0, m_cap, cap);
83 view_policy::template free<Allocator>(pDataOld, cap, cnt);
84 }
85
86 public:
87 darr() noexcept = default;
89 darr(core::zero_t) noexcept {}
90
95 resize(count, value);
96 }
97
101 resize(count);
102 }
103
108 template <typename InputIt>
109 darr(InputIt first, InputIt last) {
110 const auto count = (size_type)core::distance(first, last);
111 resize(count);
112
113 if constexpr (std::is_pointer_v<InputIt>) {
114 for (size_type i = 0; i < count; ++i)
115 operator[](i) = first[i];
116 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
117 for (size_type i = 0; i < count; ++i)
118 operator[](i) = *(first[i]);
119 } else {
120 size_type i = 0;
121 for (auto it = first; it != last; ++it)
122 operator[](++i) = *it;
123 }
124 }
125
128 darr(std::initializer_list<T> il): darr(il.begin(), il.end()) {}
129
132 darr(const darr& other): darr(other.begin(), other.end()) {}
133
136 darr(darr&& other) noexcept: m_pData(other.m_pData), m_cnt(other.m_cnt), m_cap(other.m_cap) {
137 other.m_pData = nullptr;
138 other.m_cnt = size_type(0);
139 other.m_cap = size_type(0);
140 }
141
145 darr& operator=(std::initializer_list<T> il) {
146 *this = darr(il.begin(), il.end());
147 return *this;
148 }
149
153 darr& operator=(const darr& other) {
154 GAIA_ASSERT(core::addressof(other) != this);
155
156 resize(other.size());
157 mem::copy_elements<T, false>(
158 m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
159
160 return *this;
161 }
162
166 darr& operator=(darr&& other) noexcept {
167 GAIA_ASSERT(core::addressof(other) != this);
168
169 // Release previously allocated memory if there was anything
170 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
171
172 m_pData = other.m_pData;
173 m_cnt = other.m_cnt;
174 m_cap = other.m_cap;
175
176 other.m_pData = nullptr;
177 other.m_cnt = size_type(0);
178 other.m_cap = size_type(0);
179
180 return *this;
181 }
182
183 ~darr() {
184 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
185 }
186
187 GAIA_CLANG_WARNING_PUSH()
188 // Memory is aligned so we can silence this warning
189 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
190
193 GAIA_NODISCARD pointer data() noexcept {
194 return reinterpret_cast<pointer>(m_pData);
195 }
196
199 GAIA_NODISCARD const_pointer data() const noexcept {
200 return reinterpret_cast<const_pointer>(m_pData);
201 }
202
206 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
207 GAIA_ASSERT(pos < size());
208 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
209 }
210
214 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
215 GAIA_ASSERT(pos < size());
216 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
217 }
218
219 GAIA_CLANG_WARNING_POP()
220
221
224 if (cap <= m_cap)
225 return;
226
227 auto* pDataOld = m_pData;
228 m_pData = view_policy::template alloc<Allocator>(cap);
229 if (pDataOld != nullptr) {
230 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cap, m_cnt);
231 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, cap, m_cap);
232 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
233 }
234
235 m_cap = cap;
236 }
237
240 void resize(size_type count) {
241 if (count == m_cnt)
242 return;
243
244 // Fresh allocation
245 if (m_pData == nullptr) {
246 if (count > 0) {
247 m_pData = view_policy::template alloc<Allocator>(count);
248 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
249 core::call_ctor_n(m_pData, count);
250 m_cap = count;
251 m_cnt = count;
252 }
253 return;
254 }
255
256 // Resizing to a smaller size
257 if (count < m_cnt) {
258 // Destroy elements at the end
259 core::call_dtor_n(&data()[count], m_cnt - count);
260 GAIA_MEM_SANI_POP_N(value_size, m_pData, m_cap, m_cnt, m_cnt - count);
261
262 m_cnt = count;
263 return;
264 }
265
266 // Resizing to a bigger size but still within allocated capacity
267 if (count <= m_cap) {
268 // Construct new elements
269 GAIA_MEM_SANI_PUSH_N(value_size, m_pData, m_cap, m_cnt, count - m_cnt);
270 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
271
272 m_cnt = count;
273 return;
274 }
275
276 auto* pDataOld = m_pData;
277 m_pData = view_policy::template alloc<Allocator>(count);
278 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
279 // Move old data to the new location
280 mem::move_elements<T, false>(m_pData, pDataOld, m_cnt, 0, count, m_cap);
281 // Default-construct new items
282 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
283 // Release old memory
284 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
285
286 m_cap = count;
287 m_cnt = count;
288 }
289
293 void resize(size_type count, const_reference value) {
294 const auto oldCount = m_cnt;
295 resize(count);
296
297 if constexpr (std::is_copy_constructible_v<value_type>) {
298 const value_type valueCopy = value;
299 for (size_type i = oldCount; i < m_cnt; ++i)
300 operator[](i) = valueCopy;
301 } else {
302 for (size_type i = oldCount; i < m_cnt; ++i)
303 operator[](i) = value;
304 }
305 }
306
309 void push_back(const T& arg) {
310 try_grow();
311
312 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
313 auto* ptr = &data()[m_cnt++];
314 core::call_ctor(ptr, arg);
315 }
316
319 void push_back(T&& arg) {
320 try_grow();
321
322 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
323 auto* ptr = &data()[m_cnt++];
324 core::call_ctor(ptr, GAIA_MOV(arg));
325 }
326
331 template <typename... Args>
332 decltype(auto) emplace_back(Args&&... args) {
333 try_grow();
334
335 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
336 auto* ptr = &data()[m_cnt++];
337 core::call_ctor(ptr, GAIA_FWD(args)...);
338 return (reference)*ptr;
339 }
340
343 GAIA_ASSERT(!empty());
344
345 auto* ptr = &data()[m_cnt - 1];
346 core::call_dtor(ptr);
347 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
348
349 --m_cnt;
350 }
351
357 GAIA_ASSERT(pos >= data());
358 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
359
360 const auto idxSrc = (size_type)core::distance(begin(), pos);
361 try_grow();
362 const auto idxDst = (size_type)core::distance(begin(), end()) + 1;
363
364 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
365 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
366 auto* ptr = &data()[m_cnt];
367 core::call_ctor(ptr, arg);
368
369 ++m_cnt;
370
371 return iterator(&data()[idxSrc]);
372 }
373
379 GAIA_ASSERT(pos >= data());
380 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
381
382 const auto idxSrc = (size_type)core::distance(begin(), pos);
383 try_grow();
384 const auto idxDst = (size_type)core::distance(begin(), end());
385
386 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
387 mem::shift_elements_right<T, false>(m_pData, idxDst, idxSrc, m_cap);
388 auto* ptr = &data()[idxSrc];
389 core::call_ctor(ptr, GAIA_MOV(arg));
390
391 ++m_cnt;
392
393 return iterator(&data()[idxSrc]);
394 }
395
400 GAIA_ASSERT(pos >= data());
401 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
402
403 if (empty())
404 return end();
405
406 const auto idxSrc = (size_type)core::distance(begin(), pos);
407 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
408
409 mem::shift_elements_left<T, false>(m_pData, idxDst, idxSrc, m_cap);
410 // Destroy if it's the last element
411 auto* ptr = &data()[m_cnt - 1];
412 core::call_dtor(ptr);
413 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
414
415 --m_cnt;
416
417 return iterator(&data()[idxSrc]);
418 }
419
424 iterator erase(iterator first, iterator last) noexcept {
425 GAIA_ASSERT(first >= data())
426 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
427 GAIA_ASSERT(last > first);
428 GAIA_ASSERT(last <= iterator(data() + size()));
429
430 if (empty())
431 return end();
432
433 const auto idxSrc = (size_type)core::distance(begin(), first);
434 const auto idxDst = size();
435 const auto cnt = (size_type)(last - first);
436
437 mem::shift_elements_left_fast<T, false>(m_pData, idxDst, idxSrc, cnt, m_cap);
438 // Destroy if it's the last element
439 auto* ptr = &data()[m_cnt - cnt];
440 core::call_dtor_n(ptr, cnt);
441 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, cnt);
442
443 m_cnt -= cnt;
444
445 return iterator(&data()[idxSrc]);
446 }
447
450 resize(0);
451 }
452
455 const auto cap = capacity();
456 const auto cnt = size();
457
458 if (cap == cnt)
459 return;
460
461 auto* pDataOld = m_pData;
462 m_pData = view_policy::template alloc<Allocator>(m_cap = cnt);
463 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, m_cap, m_cnt);
464 mem::move_elements<T, false>(m_pData, pDataOld, cnt, 0);
465 GAIA_MEM_SANI_DEL_BLOCK(value_size, pDataOld, cap, cnt);
466 view_policy::template free<Allocator>(pDataOld);
467 }
468
473 template <typename Func>
474 auto retain(Func&& func) {
475 size_type erased = 0;
476 size_type idxDst = 0;
477 size_type idxSrc = 0;
478
479 while (idxSrc < m_cnt) {
480 if (func(operator[](idxSrc))) {
481 if (idxDst < idxSrc) {
482 mem::move_element<T, false>(m_pData, m_pData, idxDst, idxSrc, m_cap, m_cap);
483 auto* ptr = &data()[idxSrc];
484 core::call_dtor(ptr);
485 }
486 ++idxDst;
487 } else {
488 auto* ptr = &data()[idxSrc];
489 core::call_dtor(ptr);
490 ++erased;
491 }
492
493 ++idxSrc;
494 }
495
496 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, erased);
497
498 m_cnt -= erased;
499 return idxDst;
500 }
501
504 GAIA_NODISCARD size_type size() const noexcept {
505 return m_cnt;
506 }
507
510 GAIA_NODISCARD bool empty() const noexcept {
511 return size() == 0;
512 }
513
516 GAIA_NODISCARD size_type capacity() const noexcept {
517 return m_cap;
518 }
519
522 GAIA_NODISCARD size_type max_size() const noexcept {
523 return static_cast<size_type>(-1);
524 }
525
528 GAIA_NODISCARD decltype(auto) front() noexcept {
529 GAIA_ASSERT(!empty());
530 return (reference)*begin();
531 }
532
535 GAIA_NODISCARD decltype(auto) front() const noexcept {
536 GAIA_ASSERT(!empty());
537 return (const_reference)*begin();
538 }
539
542 GAIA_NODISCARD decltype(auto) back() noexcept {
543 GAIA_ASSERT(!empty());
544 return (reference)(operator[](m_cnt - 1));
545 }
546
549 GAIA_NODISCARD decltype(auto) back() const noexcept {
550 GAIA_ASSERT(!empty());
551 return (const_reference) operator[](m_cnt - 1);
552 }
553
556 GAIA_NODISCARD auto begin() noexcept {
557 return iterator(data());
558 }
559
562 GAIA_NODISCARD auto begin() const noexcept {
563 return cbegin();
564 }
565
568 GAIA_NODISCARD auto cbegin() const noexcept {
569 return const_iterator(data());
570 }
571
574 GAIA_NODISCARD auto rbegin() noexcept {
575 return iterator((pointer)&back());
576 }
577
580 GAIA_NODISCARD auto rbegin() const noexcept {
582 }
583
586 GAIA_NODISCARD auto crbegin() const noexcept {
588 }
589
592 GAIA_NODISCARD auto end() noexcept {
593 return iterator(data() + size());
594 }
595
598 GAIA_NODISCARD auto end() const noexcept {
599 return const_iterator(data() + size());
600 }
601
604 GAIA_NODISCARD auto cend() const noexcept {
605 return const_iterator(data() + size());
606 }
607
610 GAIA_NODISCARD auto rend() noexcept {
611 return iterator(data() - 1);
612 }
613
616 GAIA_NODISCARD auto rend() const noexcept {
617 return const_iterator(data() - 1);
618 }
619
622 GAIA_NODISCARD auto crend() const noexcept {
623 return const_iterator(data() - 1);
624 }
625
629 GAIA_NODISCARD bool operator==(const darr& other) const noexcept {
630 if (m_cnt != other.m_cnt)
631 return false;
632 const size_type n = size();
633 for (size_type i = 0; i < n; ++i)
634 if (!(operator[](i) == other[i]))
635 return false;
636 return true;
637 }
638
642 GAIA_NODISCARD constexpr bool operator!=(const darr& other) const noexcept {
643 return !operator==(other);
644 }
645 };
646 } // namespace cnt
647
648} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
darr(const darr &other)
Copy-constructs a container.
Definition darray_impl.h:132
iterator insert(iterator pos, T &&arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:378
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:356
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_impl.h:574
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first element.
Definition darray_impl.h:562
GAIA_NODISCARD auto rbegin() const noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_impl.h:580
const_pointer const_iterator
Read-only random-access iterator type.
Definition darray_impl.h:49
GAIA_NODISCARD auto cend() const noexcept
Returns a read-only iterator one past the last element.
Definition darray_impl.h:604
decltype(auto) emplace_back(Args &&... args)
Constructs and appends an element.
Definition darray_impl.h:332
darr_detail::size_type size_type
Unsigned type used for sizes and indices.
Definition darray_impl.h:44
GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept
Accesses an element without bounds checking in optimized builds.
Definition darray_impl.h:206
GAIA_NODISCARD decltype(auto) back() const noexcept
Accesses the last element.
Definition darray_impl.h:549
GAIA_NODISCARD auto cbegin() const noexcept
Returns a read-only iterator to the first element.
Definition darray_impl.h:568
darr & operator=(const darr &other)
Copy-assigns the container.
Definition darray_impl.h:153
darr(InputIt first, InputIt last)
Constructs a container from an iterator range.
Definition darray_impl.h:109
GAIA_NODISCARD decltype(auto) front() const noexcept
Accesses the first element.
Definition darray_impl.h:535
GAIA_NODISCARD size_type capacity() const noexcept
Returns the number of elements that fit without reallocation.
Definition darray_impl.h:516
darr & operator=(darr &&other) noexcept
Move-assigns the container.
Definition darray_impl.h:166
void reserve(size_type cap)
Ensures storage for at least the requested number of elements.
Definition darray_impl.h:223
GAIA_NODISCARD const_pointer data() const noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:199
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_impl.h:504
GAIA_NODISCARD decltype(auto) back() noexcept
Accesses the last element.
Definition darray_impl.h:542
void clear() noexcept
Removes all elements.
Definition darray_impl.h:449
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_impl.h:556
void shrink_to_fit()
Reduces allocated storage to match the current size when possible.
Definition darray_impl.h:454
GAIA_NODISCARD auto crend() const noexcept
Returns the read-only reverse traversal sentinel preceding the first element.
Definition darray_impl.h:622
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_impl.h:424
darr(size_type count, const_reference value)
Constructs a container with copies of a value.
Definition darray_impl.h:94
darr(size_type count)
Constructs a container with the requested number of value-initialized elements.
Definition darray_impl.h:100
void resize(size_type count)
Changes the number of elements.
Definition darray_impl.h:240
static constexpr size_t value_size
Size of one element in bytes.
Definition darray_impl.h:54
GAIA_NODISCARD auto rend() const noexcept
Returns the reverse traversal sentinel preceding the first element.
Definition darray_impl.h:616
darr & operator=(std::initializer_list< T > il)
Replaces the elements from an initializer list.
Definition darray_impl.h:145
GAIA_NODISCARD auto rend() noexcept
Returns the reverse traversal sentinel preceding the first element.
Definition darray_impl.h:610
auto retain(Func &&func)
Removes all elements that fail the predicate.
Definition darray_impl.h:474
GAIA_NODISCARD bool operator==(const darr &other) const noexcept
Compares two containers element by element.
Definition darray_impl.h:629
GAIA_NODISCARD decltype(auto) front() noexcept
Accesses the first element.
Definition darray_impl.h:528
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_impl.h:399
void push_back(T &&arg)
Appends an element.
Definition darray_impl.h:319
darr(std::initializer_list< T > il)
Constructs a container from an initializer list.
Definition darray_impl.h:128
pointer iterator
Mutable random-access iterator type.
Definition darray_impl.h:47
darr_detail::difference_type difference_type
Type used for iterator differences.
Definition darray_impl.h:42
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_impl.h:510
void pop_back() noexcept
Removes the last element.
Definition darray_impl.h:342
darr(darr &&other) noexcept
Move-constructs a container.
Definition darray_impl.h:136
void resize(size_type count, const_reference value)
Changes the size and initializes new elements from a value.
Definition darray_impl.h:293
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:193
void push_back(const T &arg)
Appends an element.
Definition darray_impl.h:309
GAIA_NODISCARD auto crbegin() const noexcept
Returns a read-only reverse traversal iterator to the last element.
Definition darray_impl.h:586
T * pointer
Mutable element pointer type.
Definition darray_impl.h:36
GAIA_NODISCARD size_type max_size() const noexcept
Returns the maximum number of elements supported by this container.
Definition darray_impl.h:522
GAIA_NODISCARD constexpr bool operator!=(const darr &other) const noexcept
Checks whether two containers differ.
Definition darray_impl.h:642
const T * const_pointer
Read-only element pointer type.
Definition darray_impl.h:38
GAIA_NODISCARD auto end() noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:592
GAIA_NODISCARD auto end() const noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:598
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:162
std::add_pointer_t< ValueType > TargetCastType
Pointer type used to address stored values.
Definition data_layout_policy.h:164
GAIA_NODISCARD static constexpr ValueType & set(std::span< ValueType > s, size_t idx) noexcept
Returns a mutable value reference from an AoS span.
Definition data_layout_policy.h:238
GAIA_NODISCARD static constexpr const ValueType & get(std::span< const ValueType > s, size_t idx) noexcept
Returns a read-only value reference from an AoS span.
Definition data_layout_policy.h:230