Sane C++ Libraries
C++ Platform Abstraction Libraries
Array.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "Internal/Segment.h"
5
6namespace SC
7{
8struct ArrayAllocator;
9template <typename T, int N>
10struct Array;
11} // namespace SC
12
15struct SC::ArrayAllocator
16{
17 [[nodiscard]] static SegmentHeader* reallocate(SegmentHeader* oldHeader, size_t newSize);
18 [[nodiscard]] static SegmentHeader* allocate(SegmentHeader* oldHeader, size_t numNewBytes, void* selfPointer);
19 static void release(SegmentHeader* oldHeader);
20
21 template <typename T>
22 static T* getItems(SegmentHeader* header)
23 {
24 return reinterpret_cast<T*>(reinterpret_cast<char*>(header) + sizeof(SegmentHeader));
25 }
26 template <typename T>
27 static const T* getItems(const SegmentHeader* header)
28 {
29 return reinterpret_cast<const T*>(reinterpret_cast<const char*>(header) + sizeof(SegmentHeader));
30 }
31};
32
41template <typename T, int N>
43{
44 protected:
45 static_assert(N > 0, "Array must have N > 0");
46
47 SegmentItems<T> segmentHeader;
48 union
49 {
50 T items[N];
51 };
52
53 using Parent = SegmentItems<T>;
54 using Operations = SegmentOperations<ArrayAllocator, T>;
55 template <int>
56 friend struct SmallString;
57 template <typename, int>
58 friend struct SmallVector;
59
60 public:
62 Array();
63
66 Array(std::initializer_list<T> list);
67
69 ~Array() { Operations::destroy(&segmentHeader); }
70
73 Array(const Array& other);
74
77 Array(Array&& other);
78
82 Array& operator=(const Array& other);
83
87 Array& operator=(Array&& other);
88
89 template <int M>
90 Array(const Array<T, M>& other);
91
92 template <int M>
93 Array(Array<T, M>&& other);
94
95 template <int M>
96 Array& operator=(const Array<T, M>& other);
97
98 template <int M>
99 Array& operator=(Array<T, M>&& other);
100
103 [[nodiscard]] Span<const T> toSpanConst() const SC_LANGUAGE_LIFETIME_BOUND { return Span<const T>(items, size()); }
104
107 [[nodiscard]] Span<T> toSpan() SC_LANGUAGE_LIFETIME_BOUND { return Span<T>(items, size()); }
108
112 [[nodiscard]] T& operator[](size_t index);
113
117 [[nodiscard]] const T& operator[](size_t index) const;
118
122 [[nodiscard]] bool push_front(const T& element) { return insert(0, {&element, 1}); }
123
127 [[nodiscard]] bool push_front(T&& element) { return insertMove(0, &element, 1); }
128
132 [[nodiscard]] bool push_back(const T& element);
133
137 [[nodiscard]] bool push_back(T&& element);
138
141 [[nodiscard]] bool pop_back();
142
145 [[nodiscard]] bool pop_front();
146
149 [[nodiscard]] T& front();
150
153 [[nodiscard]] const T& front() const;
154
157 [[nodiscard]] T& back();
158
161 [[nodiscard]] const T& back() const;
162
166 [[nodiscard]] bool reserve(size_t newCapacity) { return newCapacity <= capacity(); }
167
172 [[nodiscard]] bool resize(size_t newSize, const T& value = T());
173
179 [[nodiscard]] bool resizeWithoutInitializing(size_t newSize);
180
183 void clear() { Operations::clear(SegmentItems<T>::getSegment(items)); }
184
187
190 [[nodiscard]] bool shrink_to_fit() { return true; }
191
194 [[nodiscard]] bool isEmpty() const { return size() == 0; }
195
198 [[nodiscard]] size_t size() const { return segmentHeader.size(); }
199
202 [[nodiscard]] size_t capacity() const { return segmentHeader.capacity(); }
203
206 [[nodiscard]] T* begin() SC_LANGUAGE_LIFETIME_BOUND { return items; }
209 [[nodiscard]] const T* begin() const SC_LANGUAGE_LIFETIME_BOUND { return items; }
212 [[nodiscard]] T* end() SC_LANGUAGE_LIFETIME_BOUND { return items + size(); }
215 [[nodiscard]] const T* end() const SC_LANGUAGE_LIFETIME_BOUND { return items + size(); }
218 [[nodiscard]] T* data() SC_LANGUAGE_LIFETIME_BOUND { return items; }
221 [[nodiscard]] const T* data() const SC_LANGUAGE_LIFETIME_BOUND { return items; }
222
227 [[nodiscard]] bool insert(size_t idx, Span<const T> data);
228
232 [[nodiscard]] bool append(Span<const T> data);
233
237 template <typename U>
238 [[nodiscard]] bool append(Span<const U> data);
239
244 template <typename U>
245 [[nodiscard]] bool appendMove(U&& src);
246
253 template <typename U>
254 [[nodiscard]] bool contains(const U& value, size_t* foundIndex = nullptr) const;
255
261 template <typename Lambda>
262 [[nodiscard]] bool find(Lambda&& lambda, size_t* foundIndex = nullptr) const;
263
267 [[nodiscard]] bool removeAt(size_t index);
268
273 template <typename Lambda>
274 [[nodiscard]] bool removeAll(Lambda&& criteria);
275
280 template <typename U>
281 [[nodiscard]] bool remove(const U& value);
282
283 private:
284 [[nodiscard]] bool insertMove(size_t idx, T* src, size_t srcSize);
285
286 [[nodiscard]] bool appendMove(Span<T> data);
287};
289
290//-----------------------------------------------------------------------------------------------------------------------
291// Implementation details
292//-----------------------------------------------------------------------------------------------------------------------
293
294//-----------------------------------------------------------------------------------------------------------------------
295// ArrayAllocator
296//-----------------------------------------------------------------------------------------------------------------------
297
298inline SC::SegmentHeader* SC::ArrayAllocator::reallocate(SegmentHeader* oldHeader, size_t newSize)
299{
300 if (newSize <= oldHeader->sizeBytes)
301 {
302 return oldHeader;
303 }
304 return nullptr;
305}
306inline SC::SegmentHeader* SC::ArrayAllocator::allocate(SegmentHeader* oldHeader, size_t numNewBytes, void* selfPointer)
307{
308 SC_COMPILER_UNUSED(numNewBytes);
309 SC_COMPILER_UNUSED(selfPointer);
310 oldHeader->initDefaults();
311 return oldHeader;
312}
313
314inline void SC::ArrayAllocator::release(SegmentHeader* oldHeader) { SC_COMPILER_UNUSED(oldHeader); }
315
316//-----------------------------------------------------------------------------------------------------------------------
317// Array<T, N>
318//-----------------------------------------------------------------------------------------------------------------------
319
320template <typename T, int N>
322{
323 static_assert(alignof(Array) == alignof(uint64_t), "Array Alignment");
324 segmentHeader.sizeBytes = 0;
325 segmentHeader.capacityBytes = sizeof(T) * N;
326}
327
328template <typename T, int N>
329SC::Array<T, N>::Array(std::initializer_list<T> list)
330{
331 segmentHeader.capacityBytes = sizeof(T) * N;
332 const auto sz = min(static_cast<int>(list.size()), N);
333 Parent::copyConstructMultiple(items, 0, static_cast<size_t>(sz), list.begin());
334 segmentHeader.setSize(static_cast<size_t>(sz));
335}
336
337template <typename T, int N>
339{
340 const size_t numElements = size();
341 SC_ASSERT_RELEASE(numElements > 0);
342 return items[0];
343}
344
345template <typename T, int N>
347{
348 const size_t numElements = size();
349 SC_ASSERT_RELEASE(numElements > 0);
350 return items[0];
351}
352
353template <typename T, int N>
355{
356 const size_t numElements = size();
357 SC_ASSERT_RELEASE(numElements > 0);
358 return items[numElements - 1];
359}
360
361template <typename T, int N>
362const T& SC::Array<T, N>::back() const
363{
364 const size_t numElements = size();
365 SC_ASSERT_RELEASE(numElements > 0);
366 return items[numElements - 1];
367}
368template <typename T, int N>
370{
371 segmentHeader.sizeBytes = 0;
372 segmentHeader.capacityBytes = sizeof(T) * N;
373 (void)append(other.toSpanConst());
374}
375
376template <typename T, int N>
378{
379 segmentHeader.sizeBytes = 0;
380 segmentHeader.capacityBytes = sizeof(T) * N;
381 (void)appendMove(other.toSpan());
382}
383
384template <typename T, int N>
385template <int M>
387{
388 static_assert(M <= N, "Unsafe operation, cannot report failure inside constructor, use append instead");
389 segmentHeader.sizeBytes = 0;
390 segmentHeader.capacityBytes = sizeof(T) * N;
391 (void)append(other.toSpanConst());
392}
393
394template <typename T, int N>
395template <int M>
396SC::Array<T, N>::Array(Array<T, M>&& other)
397{
398 static_assert(M <= N, "Unsafe operation, cannot report failure inside constructor, use appendMove instead");
399 segmentHeader.sizeBytes = 0;
400 segmentHeader.capacityBytes = sizeof(T) * N;
401 (void)appendMove(other.items, other.size());
402}
403
404template <typename T, int N>
406{
407 SC_ASSERT_DEBUG(index < size());
408 return items[index];
409}
410
411template <typename T, int N>
412const T& SC::Array<T, N>::operator[](size_t index) const
413{
414 SC_ASSERT_DEBUG(index < size());
415 return items[index];
416}
417
418template <typename T, int N>
420{
421 if (&other != this)
422 {
423 T* oldItems = Array::items;
424 bool res = Operations::assign(oldItems, other.items, other.size());
425 (void)res;
426 SC_ASSERT_DEBUG(res);
427 }
428 return *this;
429}
430
431template <typename T, int N>
433{
434 if (&other != this)
435 {
436 Operations::clear(SegmentItems<T>::getSegment(items));
437 if (appendMove(other.toSpan()))
438 {
439 Operations::clear(SegmentItems<T>::getSegment(other.items));
440 }
441 }
442 return *this;
443}
444
445template <typename T, int N>
446template <int M>
448{
449 if (&other != this)
450 {
451 T* oldItems = items;
452 bool res = copy(oldItems, other.data(), other.size());
453 (void)res;
454 SC_ASSERT_DEBUG(res);
455 }
456 return *this;
457}
458
459template <typename T, int N>
460template <int M>
462{
463 if (&other != this)
464 {
465 Array::clear();
466 if (appendMove(other.items, other.size()))
467 {
468 other.clear();
469 }
470 }
471 return *this;
472}
473
474template <typename T, int N>
475bool SC::Array<T, N>::push_back(const T& element)
476{
477 T* oldItems = items;
478 return Operations::push_back(oldItems, element);
479}
480
481template <typename T, int N>
483{
484 T* oldItems = items;
485 return Operations::push_back(oldItems, move(element));
486}
487
488template <typename T, int N>
490{
491 return Operations::pop_back(items);
492}
493
494template <typename T, int N>
496{
497 return Operations::pop_front(items);
498}
499
500template <typename T, int N>
501bool SC::Array<T, N>::resize(size_t newSize, const T& value)
502{
503 T* oldItems = items;
504 static constexpr bool IsTrivial = TypeTraits::IsTriviallyCopyable<T>::value;
505 return Operations::template resizeInternal<IsTrivial, true>(oldItems, newSize, &value);
506}
507
508template <typename T, int N>
510{
511 T* oldItems = items;
512 static constexpr bool IsTrivial = TypeTraits::IsTriviallyCopyable<T>::value;
513 return Operations::template resizeInternal<IsTrivial, false>(oldItems, newSize, nullptr);
514}
515
516template <typename T, int N>
518{
519 T* oldItems = items;
520 return Operations::template insert<true>(oldItems, idx, data.data(), data.sizeInElements());
521}
522
523template <typename T, int N>
525{
526 T* oldItems = items;
527 return Operations::template insert<true>(oldItems, size(), data.data(), data.sizeInElements());
528}
529
530template <typename T, int N>
531template <typename U>
533{
534 T* oldItems = items;
535 return Operations::template insert<true>(oldItems, size(), data.data(), data.sizeInElements());
536}
537
538template <typename T, int N>
539template <typename U>
541{
542 if (appendMove({src.data(), src.size()}))
543 {
544 src.clear();
545 return true;
546 }
547 return false;
548}
549
550template <typename T, int N>
551template <typename U>
552bool SC::Array<T, N>::contains(const U& value, size_t* foundIndex) const
553{
555 items, 0, size(), [&](const T& element) { return element == value; }, foundIndex);
556}
557
558template <typename T, int N>
559template <typename Lambda>
560bool SC::Array<T, N>::find(Lambda&& lambda, size_t* foundIndex) const
561{
562 return SegmentItems<T>::findIf(items, 0, size(), forward<Lambda>(lambda), foundIndex);
563}
564
565template <typename T, int N>
567{
568 return Operations::removeAt(items, index);
569}
570
571template <typename T, int N>
572template <typename Lambda>
573bool SC::Array<T, N>::removeAll(Lambda&& criteria)
574{
575 return SegmentItems<T>::removeAll(items, 0, size(), forward<Lambda>(criteria));
576}
577
578template <typename T, int N>
579template <typename U>
580bool SC::Array<T, N>::remove(const U& value)
581{
582 return SegmentItems<T>::removeAll(items, 0, size(), [&](const auto& it) { return it == value; });
583}
584
585template <typename T, int N>
587{
588 T* oldItems = items;
589 return Operations::template insert<false>(oldItems, size(), data.data(), data.sizeInElements());
590}
591
592template <typename T, int N>
593bool SC::Array<T, N>::insertMove(size_t idx, T* src, size_t srcSize)
594{
595 T* oldItems = items;
596 return Operations::template insert<false>(oldItems, idx, src, srcSize);
597}
598
599namespace SC
600{
601// Allows using this type across Plugin boundaries
602SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 64>;
603SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 128 * sizeof(native_char_t)>;
604SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 255 * sizeof(native_char_t)>;
605SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 512 * sizeof(native_char_t)>;
606SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 1024 * sizeof(native_char_t)>;
607} // namespace SC
constexpr const T & min(const T &t1, const T &t2)
Finds the minimum of two values.
Definition: Compiler.h:300
constexpr ForwardIterator findIf(ForwardIterator first, ForwardIterator last, UnaryPredicate &&predicate)
Find item satisfying the given predicate.
Definition: AlgorithmFind.h:23
#define SC_ASSERT_DEBUG(e)
Assert expression e to be true.
Definition: Assert.h:82
#define SC_COMPILER_EXPORT
Macro for symbol visibility in non-MSVC compilers.
Definition: Compiler.h:78
#define SC_COMPILER_EXTERN
Define compiler-specific export macros for DLL visibility.
Definition: Compiler.h:74
#define SC_ASSERT_RELEASE(e)
Assert expression e to be true.
Definition: Assert.h:66
#define SC_COMPILER_UNUSED(param)
Silence an unused variable or unused parameter warning.
Definition: Compiler.h:139
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
char native_char_t
The native char for the platform (wchar_t (4 bytes) on Windows, char (1 byte) everywhere else )
Definition: PrimitiveTypes.h:34
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition: PrimitiveTypes.h:42
A contiguous sequence of elements kept inside its inline storage.
Definition: Array.h:43
bool resize(size_t newSize, const T &value=T())
Resizes this array to newSize, preserving existing elements.
Definition: Array.h:501
Span< const T > toSpanConst() const SC_LANGUAGE_LIFETIME_BOUND
Returns a Span wrapping the entire of current array.
Definition: Array.h:103
bool removeAll(Lambda &&criteria)
Removes all items matching criteria given by Lambda.
Definition: Array.h:573
T * end() SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to one after last element of the array.
Definition: Array.h:212
T & operator[](size_t index)
Access item at index.
Definition: Array.h:405
bool remove(const U &value)
Removes all values equal to value
Definition: Array.h:580
bool reserve(size_t newCapacity)
Reserves memory for newCapacity elements, allocating memory if necessary.
Definition: Array.h:166
bool append(Span< const T > data)
Appends a range of items copying them at the end of array.
Definition: Array.h:524
bool resizeWithoutInitializing(size_t newSize)
Resizes this array to newSize, preserving existing elements.
Definition: Array.h:509
size_t size() const
Gets size of the array.
Definition: Array.h:198
bool insert(size_t idx, Span< const T > data)
Inserts a range of items copying them at given index.
Definition: Array.h:517
bool push_back(const T &element)
Appends an element copying it at the end of the Array.
Definition: Array.h:475
bool contains(const U &value, size_t *foundIndex=nullptr) const
Check if the current array contains a given value.
Definition: Array.h:552
bool isEmpty() const
Check if the array is empty.
Definition: Array.h:194
bool appendMove(U &&src)
Appends another array moving its contents at the end of array.
Definition: Array.h:540
T * data() SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to first element of the array.
Definition: Array.h:218
const T * begin() const SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to first element of the array.
Definition: Array.h:209
bool push_front(T &&element)
Moves an element in front of the Array, at position 0.
Definition: Array.h:127
bool pop_back()
Removes the last element of the array.
Definition: Array.h:489
size_t capacity() const
Gets capacity of the array.
Definition: Array.h:202
const T * data() const SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to first element of the array.
Definition: Array.h:221
bool pop_front()
Removes the first element of the array.
Definition: Array.h:495
bool removeAt(size_t index)
Removes an item at a given index.
Definition: Array.h:566
Array & operator=(const Array &other)
Move assigns another array to this one.
Definition: Array.h:419
T * begin() SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to first element of the array.
Definition: Array.h:206
bool push_front(const T &element)
Copies an element in front of the Array, at position 0.
Definition: Array.h:122
void clear()
Removes all elements from container, calling destructor for each of them.
Definition: Array.h:183
T & front()
Access the first element of the Array.
Definition: Array.h:338
T & back()
Access the last element of the Array.
Definition: Array.h:354
void clearWithoutInitializing()
Sets size() to zero, without calling destructor on elements.
Definition: Array.h:186
const T * end() const SC_LANGUAGE_LIFETIME_BOUND
Gets pointer to one after last element of the array.
Definition: Array.h:215
bool find(Lambda &&lambda, size_t *foundIndex=nullptr) const
Finds the first item in array matching criteria given by the lambda.
Definition: Array.h:560
Span< T > toSpan() SC_LANGUAGE_LIFETIME_BOUND
Returns a Span wrapping the entire of current array.
Definition: Array.h:107
~Array()
Destroys the Array, releasing allocated memory.
Definition: Array.h:69
Array()
Constructs an empty Array.
Definition: Array.h:321
bool shrink_to_fit()
This operation is a no-op on Array.
Definition: Array.h:190
String with compile time configurable inline storage (small string optimization)
Definition: SmallString.h:21
A Vector that can hold up to N elements inline and > N on heap.
Definition: SmallVector.h:27
View over a contiguous sequence of items (pointer + size in elements).
Definition: Span.h:20
constexpr const Type * data() const
Returns pointer to first element of the span.
Definition: Span.h:88
constexpr SizeType sizeInElements() const
Size of Span in elements.
Definition: Span.h:104
IsTriviallyCopyable evaluates to true if the type T can be trivially copied, false otherwise.
Definition: TypeTraits.h:60