Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Vector.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Containers/Algorithms/AlgorithmRemove.h" // removeIf
5#include "../Foundation/TypeTraits.h" // IsTriviallyCopyable
6#include "../Memory/Internal/Segment.inl" // IWYU pragma: keep
7#include "../Memory/Internal/SegmentTrivial.inl" // IWYU pragma: keep
8#include "ContainersExport.h"
9
10namespace SC
11{
12namespace detail
13{
14
15template <typename T, bool isTrivial = TypeTraits::IsTriviallyCopyable<T>::value>
16struct SegmentVTable : public SegmentTrivial<T>
17{
18};
19
20template <typename T>
21struct SegmentVTable<T, false>
22{
23 static void destruct(Span<T> data) noexcept
24 {
25 forEach(data, [](auto, T& item) { item.~T(); });
26 }
27
28 template <typename U>
29 static void copyConstructAs(Span<T> data, Span<const U> value) noexcept
30 {
31 const U& single = value[0];
32 forEach(data, [&single](auto, T& item) { placementNew(item, single); });
33 }
34
35 template <typename U>
36 static void copyConstruct(Span<T> data, const U* src) noexcept
37 {
38 forEach(data, [src](auto idx, T& item) { placementNew(item, src[idx]); });
39 }
40
41 template <typename U>
42 static void copyAssign(Span<T> data, const U* src) noexcept
43 {
44 forEach(data, [src](auto idx, T& item) { item = src[idx]; });
45 }
46
47 template <typename U>
48 static void moveConstruct(Span<T> data, U* src) noexcept
49 {
50 forEach(data, [src](auto idx, T& item) { placementNew(item, move(src[idx])); });
51 }
52
53 template <typename U>
54 static void moveAssign(Span<T> data, U* src) noexcept
55 {
56 forEach(data, [src](auto idx, T& item) { item = move(src[idx]); });
57 }
58
59 template <typename U>
60 static void copyInsert(Span<T> headerData, Span<const U> values) noexcept
61 {
62 // This function inserts values at the beginning of headerData
63 T* data = headerData.data();
64 const U* src = values.data();
65
66 const size_t numElements = headerData.sizeInElements();
67 const size_t numToInsert = values.sizeInElements();
68
69 if (numElements == 0)
70 {
71 // All newly inserted elements will be copy-constructed in the uninitialized area
72 for (size_t idx = 0; idx < numToInsert; ++idx)
73 {
74 placementNew(data[idx], src[idx]);
75 }
76 }
77 else
78 {
79 // Move construct some elements in the not initialized area
80 for (size_t idx = numElements; idx < numElements + numToInsert; ++idx)
81 {
82 // Guard against using slots that are before segment start.
83 // In the last loop at end of this scope such slots must be
84 // initialized with placement new instead of assignment operator
85 if (idx >= numToInsert)
86 {
87 placementNew(data[idx], move(data[idx - numToInsert]));
88 }
89 }
90
91 // Move assign some elements to slots in "post-move" state left from previous loop
92 for (size_t idx = numElements - 1; idx >= numToInsert; --idx)
93 {
94 if (idx >= numToInsert)
95 {
96 data[idx] = move(data[idx - numToInsert]);
97 }
98 }
99
100 // Copy assign source data to slots in "post-move" state left from previous loop
101 for (size_t idx = 0; idx < numToInsert; ++idx)
102 {
103 // See note in the first loop in this scope to understand use of assignment vs. placement new
104 if (idx < numElements)
105 {
106 data[idx] = src[idx];
107 }
108 else
109 {
110 placementNew(data[idx], src[idx]);
111 }
112 }
113 }
114 }
115
116 static void remove(Span<T> headerData, size_t numToRemove) noexcept
117 {
118 T* data = headerData.data();
119
120 const size_t numElements = headerData.sizeInElements();
121
122 for (size_t idx = 0; idx < numElements - numToRemove; ++idx)
123 {
124 data[idx] = move(data[idx + numToRemove]);
125 }
126 for (size_t idx = numElements - numToRemove; idx < numElements; ++idx)
127 {
128 data[idx].~T();
129 }
130 }
131
132 private:
133 template <typename Lambda>
134 static void forEach(Span<T> data, Lambda&& lambda) noexcept
135 {
136 const size_t numElements = data.sizeInElements();
137 T* elements = data.data();
138 for (size_t idx = 0; idx < numElements; ++idx)
139 {
140 lambda(idx, elements[idx]);
141 }
142 }
143};
144
145// Executes operations using trivial or non trivial variations of the move/copy/construct functions.
146// Trivial check cannot be a template param of this class because it would cause Vector to need
147// the entire definition of T to declare Vector<T>, making it impossible to create "recursive" structures
148// like SC::Build::WriteInternal::RenderGroup (that has a "Vector<RenderGroup> children" as member )
149template <typename T>
150struct ObjectVTable
151{
152 using Type = T;
153 static void destruct(Span<T> data) noexcept { SegmentVTable<T>::destruct(data); }
154 // clang-format off
155 template <typename U> static void copyConstructAs(Span<T> data, Span<const U> value) noexcept { SegmentVTable<T>::template copyConstructAs<U>(data, value);}
156 template <typename U> static void copyConstruct(Span<T> data, const U* src) noexcept { SegmentVTable<T>::template copyConstruct<U>(data, src);}
157 template <typename U> static void copyAssign(Span<T> data, const U* src) noexcept { SegmentVTable<T>::template copyAssign<U>(data, src);}
158 template <typename U> static void moveConstruct(Span<T> data, U* src) noexcept { SegmentVTable<T>::template moveConstruct<U>(data, src);}
159 template <typename U> static void moveAssign(Span<T> data, U* src) noexcept { SegmentVTable<T>::template moveAssign<U>(data, src);}
160 template <typename U> static void copyInsert(Span<T> data, Span<const U> values) noexcept { SegmentVTable<T>::template copyInsert<U>(data, values);}
161 // clang-format on
162 static void remove(Span<T> data, size_t numElements) noexcept { SegmentVTable<T>::remove(data, numElements); }
163};
164
165template <typename T>
166struct VectorVTable : public ObjectVTable<T>, public SegmentSelfRelativePointer<T>
167{
168 static constexpr bool IsArray = false;
169};
170} // namespace detail
171
174
177
188template <typename T>
189struct Vector : public Segment<detail::VectorVTable<T>>
190{
192
193 // Inherits all constructors from Segment
194 using Parent::Parent;
195
198 template <typename U>
199 [[nodiscard]] bool contains(const U& value, size_t* index = nullptr) const noexcept
200 {
201 return Algorithms::contains(*this, value, index);
202 }
203
206 template <typename Lambda>
207 [[nodiscard]] bool find(Lambda&& lambda, size_t* index = nullptr) const noexcept
208 {
209 return Algorithms::findIf(Parent::begin(), Parent::end(), move(lambda), index) != Parent::end();
210 }
211
214 template <typename Lambda>
215 [[nodiscard]] bool removeAll(Lambda&& criteria) noexcept
216 {
217 T* itBeg = Parent::begin();
218 T* itEnd = Parent::end();
219 T* it = Algorithms::removeIf(itBeg, itEnd, forward<Lambda>(criteria));
220
221 const size_t numElements = static_cast<size_t>(itEnd - it);
222 const size_t offset = static_cast<size_t>(it - itBeg);
223 detail::VectorVTable<T>::destruct({Parent::data() + offset, numElements});
224 Parent::header.sizeBytes -= static_cast<decltype(Parent::header.sizeBytes)>(numElements * sizeof(T));
225 return it != itEnd;
226 }
227
232 template <typename U>
233 [[nodiscard]] bool remove(const U& value) noexcept
234 {
235 return removeAll([&](auto& item) { return item == value; });
236 }
237};
238
250template <typename T, int N>
251struct SmallVector : public Vector<T>
252{
253 // clang-format off
254 SmallVector(SegmentAllocator allocator = SegmentAllocator::Global) noexcept : Vector<T>( N * sizeof(T), allocator) {}
255 ~SmallVector() noexcept {}
256 SmallVector(const SmallVector& other) noexcept : SmallVector() { Vector<T>::operator=(other); }
257 SmallVector(SmallVector&& other) noexcept : SmallVector() { Vector<T>::operator=(move(other)); }
258 SmallVector& operator=(const SmallVector& other) noexcept { Vector<T>::operator=(other); return *this; }
259 SmallVector& operator=(SmallVector&& other) noexcept { Vector<T>::operator=(move(other)); return *this; }
260
261 SmallVector(const Vector<T>& other) noexcept : SmallVector() { Vector<T>::operator=(other); }
262 SmallVector(Vector<T>&& other) noexcept : SmallVector() { Vector<T>::operator=(move(other)); }
263 SmallVector(std::initializer_list<T> list) noexcept : SmallVector() { SC_ASSERT_RELEASE(Vector<T>::assign({list.begin(), list.size()})); }
264 // clang-format on
265 protected:
266 SmallVector(int num, SegmentAllocator allocator) : Vector<T>(N, allocator) { (void)num; }
267
268 private:
269 uint64_t inlineCapacity = N * sizeof(T);
270 union
271 {
272 T inlineData[N];
273 };
274};
275
276template <typename T>
277using VectorTL = detail::SegmentCustom<Vector<T>, Vector<T>, 0, SegmentAllocator::ThreadLocal>;
278template <typename T, int N>
279using SmallVectorTL = detail::SegmentCustom<SmallVector<T, N>, Vector<T>, N, SegmentAllocator::ThreadLocal>;
280
282
283} // namespace SC
#define SC_ASSERT_RELEASE(e)
Assert expression e to be true.
Definition Assert.h:48
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:273
constexpr T && forward(typename TypeTraits::RemoveReference< T >::type &value)
Forwards an lvalue or an rvalue as an rvalue reference.
Definition Compiler.h:276
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition PrimitiveTypes.h:33
A slice of contiguous memory, prefixed by and header containing size and capacity.
Definition Segment.h:114
A Vector that can hold up to N elements inline and > N on heap.
Definition Vector.h:252
A contiguous sequence of heap allocated elements.
Definition Vector.h:190
bool remove(const U &value) noexcept
Removes all values equal to value
Definition Vector.h:233
bool find(Lambda &&lambda, size_t *index=nullptr) const noexcept
Finds the first item in array matching criteria given by the lambda.
Definition Vector.h:207
bool contains(const U &value, size_t *index=nullptr) const noexcept
Check if the current array contains a given value.
Definition Vector.h:199
bool removeAll(Lambda &&criteria) noexcept
Removes all items matching criteria given by Lambda.
Definition Vector.h:215