KTL
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"
5 #include "trivial_vector_fwd.h"
6 
7 #include <cstring>
8 #include <iterator>
9 #include <memory>
10 #include <utility>
11 
12 namespace 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 
166  ~trivial_vector() noexcept
167  {
168  if (m_Begin != nullptr)
169  Traits::deallocate(m_Alloc, m_Begin, capacity());
170  }
171 
172  trivial_vector& operator=(const trivial_vector& other) noexcept
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
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
T & at(size_t index) const noexcept
Returns a reference to the element at index.
Definition: trivial_vector.h:278
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
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
T & operator[](size_t index) noexcept
Returns a reference to the element at index.
Definition: trivial_vector.h:213
const T & operator[](size_t index) const noexcept
Returns a reference to the element at index.
Definition: trivial_vector.h:221
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
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
~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
trivial_vector & operator=(trivial_vector &&other) noexcept
Definition: trivial_vector.h:191
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:15