Sane C++ Libraries
C++ Platform Abstraction Libraries
Array.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Containers/Vector.h" // SegmentObject
5
6namespace SC
7{
10
19template <typename T, int N>
20struct Array
21{
24 {
25 header.sizeBytes = 0;
26 header.capacityBytes = N * sizeof(T);
27
28 header.isInlineBuffer = true;
29 header.isFollowedByInlineBuffer = false;
30 }
31
32 // clang-format off
33 Array(std::initializer_list<T> list) : Array() { SC_ASSERT_RELEASE(assign({list.begin(), list.size()})); }
34 ~Array() { ArraySegment().unsafeSetHeader(&header); }
35 Array(const Array& other) : Array() { SC_ASSERT_RELEASE(append(other.toSpanConst())); }
36 Array(Array&& other) : Array() { SC_ASSERT_RELEASE(appendMove(move(other))); }
37 Array& operator=(const Array& other) { SC_ASSERT_RELEASE(assign(other.toSpanConst())); return *this; }
38 Array& operator=(Array&& other) { SC_ASSERT_RELEASE(assign(move(other))); return *this; }
39 template <int M>
40 Array(const Array<T, M>& other) : Array() { SC_ASSERT_RELEASE(assign(other.toSpanConst()));}
41 template <int M>
42 Array(Array<T, M>&& other) : Array() { SC_ASSERT_RELEASE(assign(move(other))); }
43 template <int M>
44 Array& operator=(const Array<T, M>& other) { SC_ASSERT_RELEASE(assign(other.toSpanConst())); return *this; }
45 template <int M>
46 Array& operator=(Array<T, M>&& other) { SC_ASSERT_RELEASE(assign(move(other))); return *this; }
47 Array(Span<const T> span) : Array() { SC_ASSERT_RELEASE(assign(span)); }
48 template <typename U>
49 Array(Span<const U> span) : Array() { SC_ASSERT_RELEASE(assign(span)); }
50 // clang-format on
51
53 [[nodiscard]] Span<const T> toSpanConst() const { return {data(), size()}; }
54
56 [[nodiscard]] Span<T> toSpan() { return {data(), size()}; }
57
59 [[nodiscard]] bool push_front(const T& value)
60 {
61 return invoke([&value](ArraySegment& segment) { return segment.push_front(value); });
62 }
63
65 [[nodiscard]] bool push_back(const T& value)
66 {
67 return invoke([&value](ArraySegment& segment) { return segment.push_back(value); });
68 }
69
71 [[nodiscard]] bool push_back(T&& value)
72 {
73 return invoke([&value](ArraySegment& segment) { return segment.push_back(move(value)); });
74 }
75
78 [[nodiscard]] bool pop_back(T* removedValue = nullptr)
79 {
80 return invoke([&](ArraySegment& segment) { return segment.pop_back(removedValue); });
81 }
82
85 [[nodiscard]] bool pop_front(T* removedValue = nullptr)
86 {
87 return invoke([&](ArraySegment& segment) { return segment.pop_front(removedValue); });
88 }
89
91 [[nodiscard]] bool reserve(size_t newCapacity)
92 {
93 return invoke([newCapacity](ArraySegment& segment) { return segment.reserve(newCapacity); });
94 }
95
100 [[nodiscard]] bool resize(size_t newSize, const T& value = T())
101 {
102 return invoke([&](ArraySegment& segment) { return segment.resize(newSize, value); });
103 }
104
106 [[nodiscard]] bool resizeWithoutInitializing(size_t newSize)
107 {
108 return invoke([&](ArraySegment& segment) { return segment.resizeWithoutInitializing(newSize); });
109 }
110
112 void clear()
113 {
114 call([](ArraySegment& segment) { return segment.clear(); });
115 }
116
118 [[nodiscard]] bool shrink_to_fit() { return true; }
119
123 [[nodiscard]] bool insert(size_t idx, Span<const T> data)
124 {
125 return invoke([&](ArraySegment& segment) { return segment.insert(idx, data); });
126 }
127
129 [[nodiscard]] bool append(Span<const T> data)
130 {
131 return invoke([data](ArraySegment& segment) { return segment.append(data); });
132 }
133
135 [[nodiscard]] bool appendMove(Array&& other)
136 {
137 return invoke(
138 [&other](ArraySegment& segment)
139 {
140 ArraySegment otherSegment;
141 otherSegment.unsafeSetHeader(&other.header);
142 if (not segment.appendMove(move(otherSegment)))
143 {
144 otherSegment.unsafeSetHeader(nullptr);
145 return false;
146 }
147 return true;
148 });
149 }
150
152 [[nodiscard]] bool assign(Span<const T> data)
153 {
154 return invoke([&data](ArraySegment& segment) { return segment.assign(data); });
155 }
156
158 template <int M>
159 [[nodiscard]] bool assign(Array<T, M>&& other)
160 {
161 return invoke(
162 [&other](ArraySegment& segment)
163 {
164 ArraySegment otherSegment;
165 otherSegment.unsafeSetHeader(&other.unsafeGetHeader());
166 if (not segment.assignMove(move(otherSegment)))
167 {
168 otherSegment.unsafeSetHeader(nullptr);
169 return false;
170 }
171 return true;
172 });
173 }
174
176 [[nodiscard]] bool isEmpty() const { return header.sizeBytes == 0; }
177
179 [[nodiscard]] size_t size() const { return header.sizeBytes / sizeof(T); }
180
182 [[nodiscard]] size_t capacity() const { return header.capacityBytes / sizeof(T); }
183
185 [[nodiscard]] const T* data() const { return header.sizeBytes > 0 ? items : nullptr; }
186
188 [[nodiscard]] T* data() { return header.sizeBytes > 0 ? items : nullptr; }
189
190 // clang-format off
191 [[nodiscard]] T* begin() SC_LANGUAGE_LIFETIME_BOUND { return data(); }
192 [[nodiscard]] const T* begin() const SC_LANGUAGE_LIFETIME_BOUND { return data(); }
193 [[nodiscard]] T* end() SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
194 [[nodiscard]] const T* end() const SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
195
196 [[nodiscard]] T& back() SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(not isEmpty()); return *(data() + size() - 1);}
197 [[nodiscard]] T& front() SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(not isEmpty()); return *data();}
198 [[nodiscard]] T& operator[](size_t idx) SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
199 [[nodiscard]] const T& back() const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(not isEmpty()); return *(data() + size() - 1);}
200 [[nodiscard]] const T& front() const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(not isEmpty()); return *data();}
201 [[nodiscard]] const T& operator[](size_t idx) const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
202 // clang-format on
203
206 template <typename U>
207 [[nodiscard]] bool contains(const U& value, size_t* index = nullptr) const
208 {
209 return toSpanConst().contains(value, index);
210 }
211
217 template <typename Lambda>
218 [[nodiscard]] bool find(Lambda&& lambda, size_t* index = nullptr) const
219 {
220 return toSpanConst().find(move(lambda), index);
221 }
222
224 [[nodiscard]] bool removeAt(size_t index)
225 {
226 return invoke([=](ArraySegment& segment) { return segment.removeAt(index); });
227 }
228
230 template <typename Lambda>
231 [[nodiscard]] bool removeAll(Lambda&& criteria)
232 {
233 T* itBeg = begin();
234 T* itEnd = end();
235 T* it = Algorithms::removeIf(itBeg, itEnd, forward<Lambda>(criteria));
236
237 const size_t numBytes = static_cast<size_t>(itEnd - it) * sizeof(T);
238 const size_t offBytes = static_cast<size_t>(it - itBeg) * sizeof(T);
239 ArrayVTable::destruct(header, offBytes, numBytes);
240 header.sizeBytes -= static_cast<decltype(header.sizeBytes)>(numBytes);
241 return it != itEnd;
242 }
243
245 template <typename U>
246 [[nodiscard]] bool remove(const U& value)
247 {
248 return removeAll([&](auto& v) { return value == v; });
249 }
250
251 [[nodiscard]] SegmentHeader& unsafeGetHeader() { return header; }
252
253 private:
254 static_assert(N > 0, "Array must have N > 0");
255
256 template <typename Lambda>
257 auto invoke(Lambda&& lambda)
258 {
259 ArraySegment segment;
260 segment.unsafeSetHeader(&header);
261 auto res = lambda(segment);
262 segment.unsafeSetHeader(nullptr);
263 return res;
264 }
265
266 template <typename Lambda>
267 auto call(Lambda&& lambda)
268 {
269 ArraySegment segment;
270 segment.unsafeSetHeader(&header);
271 lambda(segment);
272 segment.unsafeSetHeader(nullptr);
273 }
274
275 struct ArrayVTable : public Internal::ObjectVTable<T>
276 {
277 static SegmentHeader* allocateNewHeader(size_t) { return nullptr; }
278 static SegmentHeader* reallocateExistingHeader(SegmentHeader& src, size_t newCapacityInBytes)
279 {
280 return newCapacityInBytes < sizeof(items) ? &src : nullptr;
281 }
282 static void destroyHeader(SegmentHeader&) {}
283 };
284
285 using ArraySegment = Segment<ArrayVTable>;
286
287 SegmentHeader header;
288 union
289 {
290 T items[N];
291 };
292};
293
295
296// Allows using this type across Plugin boundaries
297SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 64>;
298SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 128 * sizeof(native_char_t)>;
299SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 255 * sizeof(native_char_t)>;
300SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 512 * sizeof(native_char_t)>;
301SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT Array<char, 1024 * sizeof(native_char_t)>;
302} // namespace SC
ForwardIterator removeIf(ForwardIterator first, ForwardIterator last, UnaryPredicate &&predicate)
Removes all items in the given range, satisfying the given predicate.
Definition: AlgorithmRemove.h:22
#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
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
A contiguous sequence of elements kept inside its inline storage.
Definition: Array.h:21
bool resize(size_t newSize, const T &value=T())
Resizes this array to newSize, preserving existing elements.
Definition: Array.h:100
bool push_back(const T &value)
Appends an element copying it at the end of the Array.
Definition: Array.h:65
bool removeAll(Lambda &&criteria)
Removes all items matching criteria by Lambda / Functor with a bool operator()(const T&)
Definition: Array.h:231
Span< T > toSpan()
Returns content of the array in a span.
Definition: Array.h:56
bool remove(const U &value)
Removes all values equal to value
Definition: Array.h:246
bool push_front(const T &value)
Copies an element in front of the Array, at position 0.
Definition: Array.h:59
bool reserve(size_t newCapacity)
Reserves memory for newCapacity elements.
Definition: Array.h:91
bool append(Span< const T > data)
Appends a range of items copying them at the end of array.
Definition: Array.h:129
bool assign(Array< T, M > &&other)
Replaces contents of the array moving all elements from the other array.
Definition: Array.h:159
bool resizeWithoutInitializing(size_t newSize)
Resizes to newSize, preserving existing elements without initializing newly added ones.
Definition: Array.h:106
size_t size() const
Returns the size of the array.
Definition: Array.h:179
bool insert(size_t idx, Span< const T > data)
Inserts a range of items copying them at given index.
Definition: Array.h:123
Span< const T > toSpanConst() const
Returns content of the array in a span.
Definition: Array.h:53
bool find(Lambda &&lambda, size_t *index=nullptr) const
Finds the first item in array matching criteria given by the lambda.
Definition: Array.h:218
bool isEmpty() const
Returns true if the array is empty.
Definition: Array.h:176
bool pop_back(T *removedValue=nullptr)
Removes the last element of the array.
Definition: Array.h:78
bool push_back(T &&value)
Appends an element moving it at the end of the Array.
Definition: Array.h:71
size_t capacity() const
Returns the capacity of the array.
Definition: Array.h:182
bool removeAt(size_t index)
Removes an item at a given index.
Definition: Array.h:224
const T * data() const
Gets pointer to first element of the array (or nullptr if empty)
Definition: Array.h:185
void clear()
Destroys all elements in the container, making the array empty.
Definition: Array.h:112
T * data()
Gets pointer to first element of the array (or nullptr if empty)
Definition: Array.h:188
bool assign(Span< const T > data)
Replaces contents of the array copying elements from the span.
Definition: Array.h:152
bool contains(const U &value, size_t *index=nullptr) const
Return true if array contains value, returning index of found item (if != nullptr)
Definition: Array.h:207
bool appendMove(Array &&other)
Appends another array moving its contents at the end of array.
Definition: Array.h:135
Array()
Constructs an empty Array.
Definition: Array.h:23
bool shrink_to_fit()
This operation is a no-op on Array.
Definition: Array.h:118
bool pop_front(T *removedValue=nullptr)
Removes the first element of the array.
Definition: Array.h:85
Definition: Segment.h:11
A slice of contiguous memory, prefixed by and header containing size and capacity.
Definition: Segment.h:35
bool pop_back(T *removedValue=nullptr)
Removes the last element of the segment.
bool appendMove(Segment &&other)
Moves contents of another segment to the end of this segment.
bool append(Span< const T > span)
Appends a Span to the end of the segment.
void clear()
Sets size to zero without freeing any memory (use shrink_to_fit() to free memory)
bool resize(size_t newSize, const T &value=T())
Re-allocates to the requested new size, preserving its contents and setting new items to value.
bool resizeWithoutInitializing(size_t newSize)
Re-allocates to the requested new size, preserving its contents.
bool push_front(const T &value)
Appends a single element to the start of the segment.
Definition: Segment.h:97
bool assign(Span< const T > span)
Replaces contents with contents of the span.
bool insert(size_t index, Span< const T > data)
Insert a span at the given index.
bool removeAt(size_t index)
Removes the element at index.
Definition: Segment.h:164
bool push_back(const T &value)
Appends a single element to the end of the segment.
Definition: Segment.h:91
bool reserve(size_t newCapacity)
Reserves capacity to avoid heap-allocation during a future append, assign or resize.
void unsafeSetHeader(SegmentHeader *newHeader)
Sets the internal header handled by this class.
Definition: Segment.h:173
bool pop_front(T *removedValue=nullptr)
Removes the first element of the segment.
bool assignMove(Segment &&other)
Replaces content moving (possibly "stealing") content of another segment.
View over a contiguous sequence of items (pointer + size in elements).
Definition: Span.h:24