KTL
Loading...
Searching...
No Matches
trivial_vector.h
Go to the documentation of this file.
1#pragma once
2
3#include "../utility/assert.h"
4#include "../utility/empty_base.h"
6
7#include <cstring>
8#include <iterator>
9#include <memory>
10#include <utility>
11
12namespace ktl
13{
19 template<typename T, typename Alloc>
21 {
22 private:
23 static_assert(std::is_default_constructible<T>::value, "Template class needs to be default constructible");
24 static_assert(std::is_trivially_copyable<T>::value, "Template class needs to be trivially copyable");
25
26 typedef std::allocator_traits<Alloc> Traits;
27
28 public:
29 typedef T* iterator;
30 typedef const T* const_iterator;
31
32 typedef std::reverse_iterator<T*> reverse_iterator;
33 typedef std::reverse_iterator<const T*> const_reverse_iterator;
34
35 public:
39 trivial_vector() noexcept :
40 m_Alloc(),
41 m_Begin(nullptr),
42 m_End(nullptr),
43 m_EndMax(nullptr) {}
44
49 explicit trivial_vector(const Alloc& allocator) noexcept :
50 m_Alloc(allocator),
51 m_Begin(nullptr),
52 m_End(nullptr),
53 m_EndMax(nullptr) {}
54
60 explicit trivial_vector(size_t n, const Alloc& allocator = Alloc()) :
61 m_Alloc(allocator),
62 m_Begin(Traits::allocate(m_Alloc, n)),
63 m_End(m_Begin + n),
64 m_EndMax(m_End) {}
65
72 explicit trivial_vector(size_t n, const T& value, const Alloc& allocator = Alloc()) :
73 m_Alloc(allocator),
74 m_Begin(Traits::allocate(m_Alloc, n)),
75 m_End(m_Begin + n),
76 m_EndMax(m_End)
77 {
78 std::uninitialized_fill_n<T*, size_t>(m_Begin, n, value);
79 }
80
86 trivial_vector(std::initializer_list<T> initializer, const Alloc& allocator = Alloc()) :
87 m_Alloc(allocator),
88 m_Begin(Traits::allocate(m_Alloc, initializer.size())),
89 m_End(m_Begin + initializer.size()),
90 m_EndMax(m_End)
91 {
92 T* dst = m_Begin;
93 for (auto value : initializer)
94 {
95 *dst = value;
96 dst++;
97 }
98 }
99
106 explicit trivial_vector(const T* first, const T* last, const Alloc& allocator = Alloc()) :
107 m_Alloc(allocator),
108 m_Begin(Traits::allocate(m_Alloc, size_t(last - first))),
109 m_End(m_Begin + size_t(last - first)),
110 m_EndMax(m_End)
111 {
112 size_t n = last - first;
113 std::memcpy(m_Begin, first, n * sizeof(T));
114 }
115
116 trivial_vector(const trivial_vector& other) noexcept :
117 m_Alloc(Traits::select_on_container_copy_construction(static_cast<Alloc>(other.m_Alloc))),
118 m_Begin(Traits::allocate(m_Alloc, other.size())),
119 m_End(m_Begin + other.size()),
120 m_EndMax(m_End)
121 {
122 if (other.m_Begin != nullptr)
123 std::memcpy(m_Begin, other.m_Begin, other.size() * sizeof(T));
124 }
125
126 trivial_vector(trivial_vector&& other) noexcept :
127 m_Alloc(std::move(other.m_Alloc)),
128 m_Begin(other.m_Begin),
129 m_End(other.m_End),
130 m_EndMax(other.m_EndMax)
131 {
132 other.m_Begin = nullptr;
133 other.m_End = nullptr;
134 other.m_EndMax = nullptr;
135 }
136
137 trivial_vector(const trivial_vector& other, const Alloc& allocator) noexcept :
138 m_Alloc(allocator),
139 m_Begin(Traits::allocate(m_Alloc, other.size())),
140 m_End(m_Begin + other.size()),
141 m_EndMax(m_End)
142 {
143 if (other.m_Begin != nullptr)
144 std::memcpy(m_Begin, other.m_Begin, other.size() * sizeof(T));
145 }
146
147 trivial_vector(trivial_vector&& other, const Alloc& allocator) noexcept :
148 m_Alloc(allocator),
149 m_Begin(Traits::allocate(m_Alloc, other.size())),
150 m_End(m_Begin + other.size()),
151 m_EndMax(m_End)
152 {
153 // Moving using a different allocator means we can't just move, we have to reallocate
154 if (other.m_Begin != nullptr)
155 {
156 std::memcpy(m_Begin, other.m_Begin, other.size() * sizeof(T));
157
158 Traits::deallocate(other.m_Alloc, other.m_Begin, other.capacity() * sizeof(T));
159 }
160
161 other.m_Begin = nullptr;
162 other.m_End = nullptr;
163 other.m_EndMax = nullptr;
164 }
165
167 {
168 if (m_Begin != nullptr)
169 Traits::deallocate(m_Alloc, m_Begin, capacity());
170 }
171
173 {
174 if (m_Begin != nullptr)
175 Traits::deallocate(m_Alloc, m_Begin, capacity());
176
177 m_Alloc = other.m_Alloc;
178
179 size_t n = other.size();
180
181 T* alBlock = Traits::allocate(m_Alloc, n);
182
183 m_Begin = alBlock;
184 m_End = m_Begin + n;
185 m_EndMax = m_Begin + n;
186
187 std::memcpy(m_Begin, other.m_Begin, n * sizeof(T));
188 return *this;
189 }
190
192 {
193 if (m_Begin != nullptr)
194 Traits::deallocate(m_Alloc, m_Begin, capacity());
195
196 m_Alloc = std::move(other.m_Alloc);
197 m_Begin = other.m_Begin;
198 m_End = other.m_End;
199 m_EndMax = other.m_EndMax;
200
201 other.m_Begin = nullptr;
202 other.m_End = nullptr;
203 other.m_EndMax = nullptr;
204 return *this;
205 }
206
213 T& operator[](size_t index) noexcept { KTL_ASSERT(index < size()); return m_Begin[index]; }
214
221 const T& operator[](size_t index) const noexcept { KTL_ASSERT(index < size()); return m_Begin[index]; }
222
223
224 iterator begin() noexcept { return m_Begin; }
225
226 const_iterator begin() const noexcept { return m_Begin; }
227
228 iterator end() noexcept { return m_End; }
229
230 const_iterator end() const noexcept { return m_End; }
231
232 reverse_iterator rbegin() noexcept { return std::reverse_iterator(m_End); }
233
234 const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(m_End); }
235
236 reverse_iterator rend() noexcept { return std::reverse_iterator(m_Begin); }
237
238 const_reverse_iterator rend() const noexcept { return std::reverse_iterator(m_Begin); }
239
240
245 size_t size() const noexcept { return m_End - m_Begin; }
246
251 size_t capacity() const noexcept { return m_EndMax - m_Begin; }
252
257 bool empty() const noexcept { return m_Begin == m_End; }
258
259
264 iterator data() noexcept { return m_Begin; }
265
270 const_iterator data() const noexcept { return m_Begin; }
271
278 T& at(size_t index) const noexcept { KTL_ASSERT(index < size()); return m_Begin[index]; }
279
280
285 void resize(size_t n) noexcept
286 {
287 if (capacity() < n)
288 expand(n - capacity());
289
290 m_End = m_Begin + n;
291 }
292
297 void reserve(size_t n) noexcept
298 {
299 if (capacity() < n)
300 set_size(n);
301 }
302
308 iterator push_back(const T& element) noexcept
309 {
310 if (m_End == m_EndMax)
311 expand(1);
312 *m_End = element;
313
314 return m_End++;
315 }
316
322 iterator push_back(T&& element) noexcept
323 {
324 if (m_End == m_EndMax)
325 expand(1);
326 *m_End = std::move(element);
327
328 return m_End++;
329 }
330
337 iterator push_back(const T* first, const T* last) noexcept
338 {
339 const size_t n = (last - first);
340
341 if (size_t(m_EndMax - m_End) < n)
342 expand(n);
343
344 T* lastElement = m_End;
345 m_End += n;
346
347 std::memcpy(lastElement, first, n * sizeof(T));
348
349 return lastElement;
350 }
351
358 template<typename... Args>
359 iterator emplace_back(Args&&... args) noexcept
360 {
361 if (m_End == m_EndMax)
362 expand(1);
363 *m_End = T(std::forward<Args>(args)...);
364
365 return m_End++;
366 }
367
375 template<typename... Args>
376 void emplace(const_iterator iter, Args&&... args) noexcept
377 {
378 KTL_ASSERT(iter >= m_Begin && iter <= m_End);
379
380 if (m_End == m_EndMax)
381 expand(1);
382
383 std::memmove(const_cast<iterator>(iter + 1), iter, (m_End - iter) * sizeof(T));
384
385 *iter = T(std::forward<Args>(args)...);
386 m_End++;
387 }
388
395 {
396 KTL_ASSERT(iter >= m_Begin && iter < m_End);
397
398 std::memmove(const_cast<iterator>(iter), iter + 1, ((m_End - iter) - 1) * sizeof(T));
399
400 m_End--;
401
402 return const_cast<iterator>(iter);
403 }
404
412 {
413 KTL_ASSERT(first <= last);
414 KTL_ASSERT(first >= m_Begin && last <= m_End);
415
416 std::memmove(const_cast<iterator>(first), last, (m_End - last) * sizeof(T));
417
418 m_End -= (last - first);
419
420 return const_cast<iterator>(first);
421 }
422
427 T pop_back() noexcept { return m_Begin[--m_End]; }
428
432 void clear() noexcept { m_End = m_Begin; }
433
434 private:
435 void expand(size_t n) noexcept
436 {
437 size_t curCap = capacity();
438 size_t alSize = curCap + (std::max)(curCap / 2, n);
439
440 set_size(alSize);
441 }
442
443 void set_size(size_t n) noexcept
444 {
445 size_t curSize = (std::min)(size(), n);
446
447 T* alBlock = Traits::allocate(m_Alloc, n);
448
449 if (m_Begin != nullptr)
450 {
451 std::memcpy(alBlock, m_Begin, curSize * sizeof(T));
452
453 Traits::deallocate(m_Alloc, m_Begin, capacity());
454 }
455
456 m_Begin = alBlock;
457 m_End = m_Begin + curSize;
458 m_EndMax = m_Begin + n;
459 }
460
461 private:
462 KTL_EMPTY_BASE Alloc m_Alloc;
463 T* m_Begin;
464 T* m_End;
465 T* m_EndMax;
466
467 //std::conditional_t<sizeof(T) < 32, uint8_t[32], uint8_t*> m_Data;
468 };
469}
#define KTL_ASSERT(x)
Definition assert.h:17
A dynamically allocated vector or trivial types.
Definition trivial_vector.h:21
T * iterator
Definition trivial_vector.h:29
T & operator[](size_t index) noexcept
Returns a reference to the element at index.
Definition trivial_vector.h:213
iterator begin() noexcept
Definition trivial_vector.h:224
iterator data() noexcept
Returns an iterator to the start of the vector.
Definition trivial_vector.h:264
trivial_vector(const T *first, const T *last, const Alloc &allocator=Alloc())
Construct the vector with the allocator and range of values.
Definition trivial_vector.h:106
const T * const_iterator
Definition trivial_vector.h:30
const_reverse_iterator rbegin() const noexcept
Definition trivial_vector.h:234
iterator end() noexcept
Definition trivial_vector.h:228
const_reverse_iterator rend() const noexcept
Definition trivial_vector.h:238
trivial_vector(size_t n, const T &value, const Alloc &allocator=Alloc())
Construct the vector with the given allocator, initial size and default value.
Definition trivial_vector.h:72
T & at(size_t index) const noexcept
Returns a reference to the element at index.
Definition trivial_vector.h:278
trivial_vector(std::initializer_list< T > initializer, const Alloc &allocator=Alloc())
Construct the vector with the allocator and range of values.
Definition trivial_vector.h:86
trivial_vector(const Alloc &allocator) noexcept
Construct the vector with the given allocator.
Definition trivial_vector.h:49
trivial_vector(trivial_vector &&other) noexcept
Definition trivial_vector.h:126
size_t capacity() const noexcept
Returns the current capacity of the vector.
Definition trivial_vector.h:251
T pop_back() noexcept
Removes the last element from the vector and returns it.
Definition trivial_vector.h:427
const_iterator begin() const noexcept
Definition trivial_vector.h:226
trivial_vector(trivial_vector &&other, const Alloc &allocator) noexcept
Definition trivial_vector.h:147
iterator push_back(const T *first, const T *last) noexcept
Pushes a range of values into the vector.
Definition trivial_vector.h:337
const_iterator end() const noexcept
Definition trivial_vector.h:230
bool empty() const noexcept
Returns true if the vector has no elements.
Definition trivial_vector.h:257
trivial_vector & operator=(const trivial_vector &other) noexcept
Definition trivial_vector.h:172
trivial_vector(size_t n, const Alloc &allocator=Alloc())
Construct the vector with the given allocator and initial size.
Definition trivial_vector.h:60
trivial_vector(const trivial_vector &other, const Alloc &allocator) noexcept
Definition trivial_vector.h:137
const_iterator data() const noexcept
Returns a const iterator to the start of the vector.
Definition trivial_vector.h:270
iterator push_back(const T &element) noexcept
Pushes a new element into the vector by copying it.
Definition trivial_vector.h:308
iterator emplace_back(Args &&... args) noexcept
Pushes a new element into the vector by constructing it.
Definition trivial_vector.h:359
trivial_vector & operator=(trivial_vector &&other) noexcept
Definition trivial_vector.h:191
iterator push_back(T &&element) noexcept
Pushes a new element into the vector by moving it.
Definition trivial_vector.h:322
size_t size() const noexcept
Returns the current size of the vector.
Definition trivial_vector.h:245
void reserve(size_t n) noexcept
Reserves the capacity of the vector to n, without initializing any elements.
Definition trivial_vector.h:297
std::reverse_iterator< const T * > const_reverse_iterator
Definition trivial_vector.h:33
const T & operator[](size_t index) const noexcept
Returns a reference to the element at index.
Definition trivial_vector.h:221
~trivial_vector() noexcept
Definition trivial_vector.h:166
std::reverse_iterator< T * > reverse_iterator
Definition trivial_vector.h:32
void clear() noexcept
Clears all elements in the vector.
Definition trivial_vector.h:432
trivial_vector() noexcept
Construct the vector with a default constructed allocator.
Definition trivial_vector.h:39
void resize(size_t n) noexcept
Resizes the vector to the given size.
Definition trivial_vector.h:285
reverse_iterator rend() noexcept
Definition trivial_vector.h:236
iterator erase(const_iterator first, const_iterator last) noexcept
Erases all elements in a range.
Definition trivial_vector.h:411
reverse_iterator rbegin() noexcept
Definition trivial_vector.h:232
void emplace(const_iterator iter, Args &&... args) noexcept
Inserts a new element into the vector by constructing it.
Definition trivial_vector.h:376
trivial_vector(const trivial_vector &other) noexcept
Definition trivial_vector.h:116
iterator erase(const_iterator iter) noexcept
Erases the element pointed to by the iterator.
Definition trivial_vector.h:394
#define KTL_EMPTY_BASE
Definition empty_base.h:6
Definition cascading.h:16