aboutsummaryrefslogtreecommitdiff
path: root/pw_containers/public/pw_containers/vector.h
blob: 62ebc8aa121131d83cac11cfc85e8bbd59fb06af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <algorithm>
#include <array>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <new>
#include <string_view>
#include <type_traits>
#include <utility>

#include "pw_assert/assert.h"
#include "pw_polyfill/language_feature_macros.h"

namespace pw {
namespace vector_impl {

template <typename I>
using IsIterator = std::negation<
    std::is_same<typename std::iterator_traits<I>::value_type, void>>;

// Used as max_size in the generic-size Vector<T> interface.
PW_INLINE_VARIABLE constexpr size_t kGeneric = size_t(-1);

// The DestructorHelper is used to make Vector<T> trivially destructible if T
// is. This could be replaced with a C++20 constraint.
template <typename VectorClass, bool kIsTriviallyDestructible>
class DestructorHelper;

template <typename VectorClass>
class DestructorHelper<VectorClass, true> {
 public:
  ~DestructorHelper() = default;
};

template <typename VectorClass>
class DestructorHelper<VectorClass, false> {
 public:
  ~DestructorHelper() { static_cast<VectorClass*>(this)->clear(); }
};

}  // namespace vector_impl

// The Vector class is similar to std::vector, except it is backed by a
// fixed-size buffer. Vectors must be declared with an explicit maximum size
// (e.g. Vector<int, 10>) but vectors can be used and referred to without the
// max size template parameter (e.g. Vector<int>).
//
// To allow referring to a pw::Vector without an explicit maximum size, all
// Vector classes inherit from Vector<T, vector_impl::kGeneric>, which stores
// the maximum size in a variable. This allows Vectors to be used without having
// to know their maximum size at compile time. It also keeps code size small
// since function implementations are shared for all maximum sizes.
template <typename T, size_t kMaxSize = vector_impl::kGeneric>
class Vector : public Vector<T, vector_impl::kGeneric> {
 public:
  using typename Vector<T, vector_impl::kGeneric>::value_type;
  using typename Vector<T, vector_impl::kGeneric>::size_type;
  using typename Vector<T, vector_impl::kGeneric>::difference_type;
  using typename Vector<T, vector_impl::kGeneric>::reference;
  using typename Vector<T, vector_impl::kGeneric>::const_reference;
  using typename Vector<T, vector_impl::kGeneric>::pointer;
  using typename Vector<T, vector_impl::kGeneric>::const_pointer;
  using typename Vector<T, vector_impl::kGeneric>::iterator;
  using typename Vector<T, vector_impl::kGeneric>::const_iterator;
  using typename Vector<T, vector_impl::kGeneric>::reverse_iterator;
  using typename Vector<T, vector_impl::kGeneric>::const_reverse_iterator;

  // Construct
  Vector() noexcept : Vector<T, vector_impl::kGeneric>(kMaxSize) {}

  Vector(size_type count, const T& value)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, count, value) {}

  explicit Vector(size_type count)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, count, T()) {}

  template <
      typename Iterator,
      typename...,
      typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
  Vector(Iterator first, Iterator last)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, first, last) {}

  Vector(const Vector& other)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, other) {}

  template <size_t kOtherMaxSize>
  Vector(const Vector<T, kOtherMaxSize>& other)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, other) {}

  Vector(Vector&& other) noexcept
      : Vector<T, vector_impl::kGeneric>(kMaxSize, std::move(other)) {}

  template <size_t kOtherMaxSize>
  Vector(Vector<T, kOtherMaxSize>&& other) noexcept
      : Vector<T, vector_impl::kGeneric>(kMaxSize, std::move(other)) {}

  Vector(std::initializer_list<T> list)
      : Vector<T, vector_impl::kGeneric>(kMaxSize, list) {}

  static constexpr size_t max_size() { return kMaxSize; }

  // Construct from std::string_view when T is char.
  template <typename U = T,
            typename = std::enable_if_t<std::is_same_v<U, char>>>
  Vector(std::string_view source) : Vector(source.begin(), source.end()) {}

  // Construct from const char* when T is char.
  template <typename U = T,
            typename = std::enable_if_t<std::is_same_v<U, char>>>
  Vector(const char* source) : Vector(std::string_view(source)) {}

  Vector& operator=(const Vector& other) {
    Vector<T>::assign(other.begin(), other.end());
    return *this;
  }

  template <size_t kOtherMaxSize>
  Vector& operator=(const Vector<T, kOtherMaxSize>& other) noexcept {
    Vector<T>::assign(other.begin(), other.end());
    return *this;
  }

  Vector& operator=(Vector&& other) noexcept {
    Vector<T>::operator=(std::move(other));
    return *this;
  }

  template <size_t kOtherMaxSize>
  Vector& operator=(Vector<T, kOtherMaxSize>&& other) noexcept {
    Vector<T>::operator=(std::move(other));
    return *this;
  }

  Vector& operator=(std::initializer_list<T> list) {
    this->assign(list.begin(), list.end());
    return *this;
  }

  // All other vector methods are implemented on the Vector<T> base class.

 private:
  friend class Vector<T, vector_impl::kGeneric>;

  static_assert(kMaxSize <= std::numeric_limits<size_type>::max());

  // Provides access to the underlying array as an array of T.
#ifdef __cpp_lib_launder
  pointer array() { return std::launder(reinterpret_cast<T*>(&array_)); }
  const_pointer array() const {
    return std::launder(reinterpret_cast<const T*>(&array_));
  }
#else
  pointer array() { return reinterpret_cast<T*>(&array_); }
  const_pointer array() const { return reinterpret_cast<const T*>(&array_); }
#endif  // __cpp_lib_launder

  // Vector entries are stored as uninitialized memory blocks aligned correctly
  // for the type. Elements are initialized on demand with placement new.
  //
  // This uses std::array instead of a C array to support zero-length Vectors.
  // Zero-length C arrays are non-standard, but std::array<T, 0> is valid.
  // The alignas specifier is required ensure that a zero-length array is
  // aligned the same as an array with elements.
  alignas(T) std::array<std::aligned_storage_t<sizeof(T), alignof(T)>,
                        kMaxSize> array_;
};

// Defines the generic-sized Vector<T> specialization, which serves as the base
// class for Vector<T> of any maximum size. Except for constructors, all Vector
// methods are implemented on this class.
template <typename T>
class Vector<T, vector_impl::kGeneric>
    : public vector_impl::DestructorHelper<
          Vector<T, vector_impl::kGeneric>,
          std::is_trivially_destructible<T>::value> {
 public:
  using value_type = T;

  // Use unsigned short instead of size_t. Since Vectors are statically
  // allocated, 65535 entries is a reasonable upper limit. This reduces Vector's
  // overhead by packing the size and capacity into 32 bits.
  using size_type = unsigned short;

  using difference_type = ptrdiff_t;
  using reference = value_type&;
  using const_reference = const value_type&;
  using pointer = T*;
  using const_pointer = const T*;
  using iterator = T*;
  using const_iterator = const T*;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;

  // A vector without an explicit maximum size (Vector<T>) cannot be constructed
  // directly. Instead, construct a Vector<T, kMaxSize>. Vectors of any max size
  // can be used through a Vector<T> pointer or reference.

  // Assign

  Vector& operator=(const Vector& other) {
    assign(other.begin(), other.end());
    return *this;
  }

  Vector& operator=(Vector&& other) noexcept {
    clear();
    MoveFrom(other);
    return *this;
  }

  Vector& operator=(std::initializer_list<T> list) {
    assign(list);
    return *this;
  }

  void assign(size_type count, const T& value) {
    clear();
    Append(count, value);
  }

  template <
      typename Iterator,
      typename...,
      typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
  void assign(Iterator first, Iterator last) {
    clear();
    CopyFrom(first, last);
  }

  void assign(std::initializer_list<T> list) {
    assign(list.begin(), list.end());
  }

  // Access

  reference at(size_type index) {
    PW_ASSERT(index < size());
    return data()[index];
  }
  const_reference at(size_type index) const {
    PW_ASSERT(index < size());
    return data()[index];
  }

  reference operator[](size_type index) {
    PW_DASSERT(index < size());
    return data()[index];
  }
  const_reference operator[](size_type index) const {
    PW_DASSERT(index < size());
    return data()[index];
  }

  reference front() { return data()[0]; }
  const_reference front() const { return data()[0]; }

  reference back() { return data()[size() - 1]; }
  const_reference back() const { return data()[size() - 1]; }

  // The underlying data is not part of the generic-length vector class. It is
  // provided in the derived class from which this instance was constructed. To
  // access the data, down-cast this to a Vector with a known max size, and
  // return a pointer to the start of the array, which is the same for all
  // vectors with explicit max size.
  T* data() noexcept { return static_cast<Vector<T, 0>*>(this)->array(); }
  const T* data() const noexcept {
    return static_cast<const Vector<T, 0>*>(this)->array();
  }

  // Iterate

  iterator begin() noexcept { return &data()[0]; }
  const_iterator begin() const noexcept { return &data()[0]; }
  const_iterator cbegin() const noexcept { return &data()[0]; }

  iterator end() noexcept { return &data()[size()]; }
  const_iterator end() const noexcept { return &data()[size()]; }
  const_iterator cend() const noexcept { return &data()[size()]; }

  reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
  const_reverse_iterator rbegin() const { return reverse_iterator(end()); }
  const_reverse_iterator crbegin() const noexcept {
    return reverse_iterator(cend());
  }

  reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
  const_reverse_iterator rend() const { return reverse_iterator(begin()); }
  const_reverse_iterator crend() const noexcept {
    return reverse_iterator(cbegin());
  }

  // Size

  [[nodiscard]] bool empty() const noexcept { return size() == 0u; }

  // True if there is no free space in the vector. Operations that add elements
  // (push_back, insert, etc.) will fail if full() is true.
  [[nodiscard]] bool full() const noexcept { return size() == max_size(); }

  // Returns the number of elements in the Vector. Uses size_t instead of
  // size_type for consistency with other containers.
  size_t size() const noexcept { return size_; }

  // Returns the maximum number of elements in this Vector.
  size_t max_size() const noexcept { return max_size_; }

  size_t capacity() const noexcept { return max_size(); }

  // Modify

  void clear() noexcept;

  iterator insert(const_iterator index, size_type count, const T& value);

  iterator insert(const_iterator index, const T& value) {
    return insert(index, 1, value);
  }

  iterator insert(const_iterator index, T&& value);

  template <typename Iterator>
  iterator insert(const_iterator index, Iterator first, Iterator last);

  iterator insert(const_iterator index, std::initializer_list<T> list) {
    return insert(index, list.begin(), list.end());
  }

  template <typename... Args>
  iterator emplace(const_iterator index, Args&&... args);

  iterator erase(const_iterator first, const_iterator last);

  iterator erase(const_iterator index) { return erase(index, index + 1); }

  void push_back(const T& value) { emplace_back(value); }

  void push_back(T&& value) { emplace_back(std::move(value)); }

  template <typename... Args>
  void emplace_back(Args&&... args);

  void pop_back();

  void resize(size_type new_size) { resize(new_size, T()); }

  void resize(size_type new_size, const T& value);

 protected:
  // Vectors without an explicit size cannot be constructed directly. Instead,
  // the maximum size must be provided.
  explicit constexpr Vector(size_type max_size) noexcept
      : max_size_(max_size) {}

  Vector(size_type max_size, size_type count, const T& value)
      : Vector(max_size) {
    Append(count, value);
  }

  Vector(size_type max_size, size_type count) : Vector(max_size, count, T()) {}

  template <
      typename Iterator,
      typename...,
      typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
  Vector(size_type max_size, Iterator first, Iterator last) : Vector(max_size) {
    CopyFrom(first, last);
  }

  Vector(size_type max_size, const Vector& other) : Vector(max_size) {
    CopyFrom(other.begin(), other.end());
  }

  Vector(size_type max_size, Vector&& other) noexcept : Vector(max_size) {
    MoveFrom(other);
  }

  Vector(size_type max_size, std::initializer_list<T> list) : Vector(max_size) {
    CopyFrom(list.begin(), list.end());
  }

 private:
  template <typename Iterator>
  void CopyFrom(Iterator first, Iterator last);

  void MoveFrom(Vector& other) noexcept;

  void Append(size_type count, const T& value);

  const size_type max_size_;
  size_type size_ = 0;
};

// Compare

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator==(const Vector<T, kLhsSize>& lhs,
                const Vector<T, kRhsSize>& rhs) {
  return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator!=(const Vector<T, kLhsSize>& lhs,
                const Vector<T, kRhsSize>& rhs) {
  return !(lhs == rhs);
}

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator<(const Vector<T, kLhsSize>& lhs, const Vector<T, kRhsSize>& rhs) {
  return std::lexicographical_compare(
      lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator<=(const Vector<T, kLhsSize>& lhs,
                const Vector<T, kRhsSize>& rhs) {
  return !(rhs < lhs);
}

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator>(const Vector<T, kLhsSize>& lhs, const Vector<T, kRhsSize>& rhs) {
  return rhs < lhs;
}

template <typename T, size_t kLhsSize, size_t kRhsSize>
bool operator>=(const Vector<T, kLhsSize>& lhs,
                const Vector<T, kRhsSize>& rhs) {
  return !(lhs < rhs);
}

// Function implementations

template <typename T>
void Vector<T, vector_impl::kGeneric>::clear() noexcept {
  if constexpr (!std::is_trivially_destructible_v<value_type>) {
    for (auto& item : *this) {
      item.~T();
    }
  }
  size_ = 0;
}

template <typename T>
template <typename... Args>
void Vector<T, vector_impl::kGeneric>::emplace_back(Args&&... args) {
  if (!full()) {
    new (&data()[size_]) T(std::forward<Args>(args)...);
    size_ += 1;
  }
}

template <typename T>
void Vector<T, vector_impl::kGeneric>::pop_back() {
  if (!empty()) {
    if constexpr (!std::is_trivially_destructible_v<value_type>) {
      back().~T();
    }
    size_ -= 1;
  }
}

template <typename T>
void Vector<T, vector_impl::kGeneric>::resize(size_type new_size,
                                              const T& value) {
  if (size() < new_size) {
    Append(std::min(max_size(), size_t(new_size)) - size(), value);
  } else {
    while (size() > new_size) {
      pop_back();
    }
  }
}

template <typename T>
typename Vector<T>::iterator Vector<T>::insert(Vector<T>::const_iterator index,
                                               T&& value) {
  PW_DASSERT(index >= cbegin());
  PW_DASSERT(index <= cend());
  PW_DASSERT(!full());

  iterator insertion_point = begin() + std::distance(cbegin(), index);
  if (insertion_point == end()) {
    emplace_back(std::move(value));
    return insertion_point;
  }

  std::move_backward(insertion_point, end(), end() + 1);
  *insertion_point = std::move(value);
  ++size_;

  // Return an iterator pointing to the inserted value.
  return insertion_point;
}

template <typename T>
template <typename Iterator>
typename Vector<T>::iterator Vector<T>::insert(Vector<T>::const_iterator index,
                                               Iterator first,
                                               Iterator last) {
  PW_DASSERT(index >= cbegin());
  PW_DASSERT(index <= cend());
  PW_DASSERT(!full());

  iterator insertion_point = begin() + std::distance(cbegin(), index);

  const size_t insertion_count = std::distance(first, last);
  if (insertion_count == 0) {
    return insertion_point;
  }
  PW_DASSERT(size() + insertion_count <= max_size());

  iterator return_value = insertion_point;

  if (insertion_point != end()) {
    std::move_backward(insertion_point, end(), end() + insertion_count);
  }

  while (first != last) {
    *insertion_point = *first;
    ++first;
    ++insertion_point;
  }
  size_ += insertion_count;

  // Return an iterator pointing to the first element inserted.
  return return_value;
}

template <typename T>
typename Vector<T>::iterator Vector<T>::insert(Vector<T>::const_iterator index,
                                               size_type count,
                                               const T& value) {
  PW_DASSERT(index >= cbegin());
  PW_DASSERT(index <= cend());
  PW_DASSERT(size() + count <= max_size());

  iterator insertion_point = begin() + std::distance(cbegin(), index);
  if (count == size_type{}) {
    return insertion_point;
  }

  if (insertion_point != end()) {
    std::move_backward(insertion_point, end(), end() + count);
  }

  iterator return_value = insertion_point;

  for (size_type final_count = size_ + count; size_ != final_count; ++size_) {
    *insertion_point = value;
    ++insertion_point;
  }

  // Return an iterator pointing to the first element inserted.
  return return_value;
}

template <typename T>
typename Vector<T>::iterator Vector<T>::erase(Vector<T>::const_iterator first,
                                              Vector<T>::const_iterator last) {
  iterator source = begin() + std::distance(cbegin(), last);
  if (first == last) {
    return source;
  }

  if constexpr (!std::is_trivially_destructible_v<T>) {
    std::destroy(first, last);
  }

  iterator destination = begin() + std::distance(cbegin(), first);
  iterator new_end = std::move(source, end(), destination);

  size_ = std::distance(begin(), new_end);

  // Return an iterator following the last removed element.
  return new_end;
}

template <typename T>
template <typename Iterator>
void Vector<T, vector_impl::kGeneric>::CopyFrom(Iterator first, Iterator last) {
  while (first != last) {
    push_back(*first++);
  }
}

template <typename T>
void Vector<T, vector_impl::kGeneric>::MoveFrom(Vector& other) noexcept {
  for (auto&& item : other) {
    emplace_back(std::move(item));
  }
  other.clear();
}

template <typename T>
void Vector<T, vector_impl::kGeneric>::Append(size_type count, const T& value) {
  for (size_t i = 0; i < count; ++i) {
    push_back(value);
  }
}

}  // namespace pw