C/C++ API Reference
Loading...
Searching...
No Matches
vector.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <algorithm>
17#include <array>
18#include <cstddef>
19#include <initializer_list>
20#include <iterator>
21#include <limits>
22#include <memory>
23#include <new>
24#include <string_view>
25#include <type_traits>
26#include <utility>
27
28#include "pw_assert/assert.h"
29#include "pw_containers/storage.h"
30#include "pw_preprocessor/compiler.h"
31#include "pw_toolchain/constexpr_tag.h"
32
33namespace pw {
34namespace vector_impl {
35
36template <typename I>
37using IsIterator = std::negation<
38 std::is_same<typename std::iterator_traits<I>::value_type, void>>;
39
40// Used as max_size in the generic-size Vector<T> interface.
41inline constexpr size_t kGeneric = size_t(-1);
42
43} // namespace vector_impl
44
45// Storage for a vector's data that ensures entries are `clear`'d before the
46// storage is removed.
47template <typename T, size_t kMaxSize, bool kIsTriviallyDestructible>
49
50// The Vector class is similar to std::vector, except it is backed by a
51// fixed-size buffer. Vectors must be declared with an explicit maximum size
52// (e.g. Vector<int, 10>) but vectors can be used and referred to without the
53// max size template parameter (e.g. Vector<int>).
54//
55// To allow referring to a pw::Vector without an explicit maximum size, all
56// Vector classes inherit from Vector<T, vector_impl::kGeneric>, which stores
57// the maximum size in a variable. This allows Vectors to be used without having
58// to know their maximum size at compile time. It also keeps code size small
59// since function implementations are shared for all maximum sizes.
60//
61// Note that size-generic `Vector<T>` cannot be used with `std::unique_ptr`
62// or `delete`. When working with dynamic allocation, prefer use of
63// `std::vector` instead.
64template <typename T, size_t kMaxSize = vector_impl::kGeneric>
65class Vector
66 : public VectorStorage<T, kMaxSize, std::is_trivially_destructible_v<T>> {
67 private:
69
70 public:
82
83 // Construct
84 Vector() noexcept = default;
85
86 // Explicit constexpr constructor. Using this constructor will place the
87 // entire object in .data by default, which will increase ROM size. Use with
88 // caution if working with large capacity sizes.
89 constexpr Vector(ConstexprTag constexpr_tag) noexcept : Base(constexpr_tag) {}
90
91 Vector(size_type count, const T& value) { this->Append(count, value); }
92
93 explicit Vector(size_type count) { this->Append(count, T()); }
94
95 template <
96 typename Iterator,
97 typename...,
98 typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
99 Vector(Iterator first, Iterator last) {
100 this->CopyFrom(first, last);
101 }
102
103 Vector(const Vector& other) : Base() {
104 this->CopyFrom(other.begin(), other.end());
105 }
106
107 template <size_t kOtherMaxSize>
108 Vector(const Vector<T, kOtherMaxSize>& other) {
109 this->CopyFrom(other.begin(), other.end());
110 }
111
112 Vector(Vector&& other) noexcept { this->MoveFrom(other); }
113
114 template <size_t kOtherMaxSize>
115 Vector(Vector<T, kOtherMaxSize>&& other) noexcept {
116 this->MoveFrom(other);
117 }
118
119 Vector(std::initializer_list<T> list) {
120 this->CopyFrom(list.begin(), list.end());
121 }
122
123 static constexpr size_t max_size() { return kMaxSize; }
124
125 // Construct from std::string_view when T is char.
126 template <typename U = T,
127 typename = std::enable_if_t<std::is_same_v<U, char>>>
128 Vector(std::string_view source) : Vector(source.begin(), source.end()) {}
129
130 // Construct from const char* when T is char.
131 template <typename U = T,
132 typename = std::enable_if_t<std::is_same_v<U, char>>>
133 Vector(const char* source) : Vector(std::string_view(source)) {}
134
135 Vector& operator=(const Vector& other) {
136 Vector<T>::assign(other.begin(), other.end());
137 return *this;
138 }
139
140 template <size_t kOtherMaxSize>
141 Vector& operator=(const Vector<T, kOtherMaxSize>& other) noexcept {
142 Vector<T>::assign(other.begin(), other.end());
143 return *this;
144 }
145
146 Vector& operator=(Vector&& other) noexcept {
147 Vector<T>::operator=(std::move(other));
148 return *this;
149 }
150
151 template <size_t kOtherMaxSize>
152 Vector& operator=(Vector<T, kOtherMaxSize>&& other) noexcept {
153 Vector<T>::operator=(std::move(other));
154 return *this;
155 }
156
157 Vector& operator=(std::initializer_list<T> list) {
158 this->assign(list.begin(), list.end());
159 return *this;
160 }
161
162 // All other vector methods are implemented on the Vector<T> base class.
163};
164
165// Specialization of ``VectorStorage`` for trivially-destructible ``T``.
166// This specialization ensures that no destructor is generated.
167template <typename T, size_t kMaxSize>
168struct VectorStorage<T, kMaxSize, true>
169 : public Vector<T, vector_impl::kGeneric> {
170 private:
172
173 protected:
174 VectorStorage() : Base(kMaxSize) {}
175
176 constexpr VectorStorage(ConstexprTag /*constexpr_tag*/)
177 : Base(kMaxSize), array_{} {}
178
179 // NOTE: no destructor is added, as ``T`` is trivially destructible.
180 private:
181 friend class Vector<T, kMaxSize>;
182 friend class Vector<T, vector_impl::kGeneric>;
183
184 using typename Vector<T, vector_impl::kGeneric>::size_type;
185 static_assert(kMaxSize <= std::numeric_limits<size_type>::max());
186
187 using typename Vector<T, vector_impl::kGeneric>::pointer;
188 using typename Vector<T, vector_impl::kGeneric>::const_pointer;
189
190 // Provides access to the underlying array as an array of T.
191#ifdef __cpp_lib_launder
192 pointer array() { return std::launder(reinterpret_cast<T*>(&array_)); }
193 const_pointer array() const {
194 return std::launder(reinterpret_cast<const T*>(&array_));
195 }
196#else
197 pointer array() { return reinterpret_cast<T*>(&array_); }
198 const_pointer array() const { return reinterpret_cast<const T*>(&array_); }
199#endif // __cpp_lib_launder
200
201 // Vector entries are stored as uninitialized memory blocks aligned correctly
202 // for the type. Elements are initialized on demand with placement new.
203 //
204 // This uses std::array instead of a C array to support zero-length Vectors.
205 // Zero-length C arrays are non-standard, but std::array<T, 0> is valid.
206 // The alignas specifier is required ensure that a zero-length array is
207 // aligned the same as an array with elements.
208 alignas(T) std::array<pw::containers::StorageFor<T>, kMaxSize> array_;
209};
210
211// Specialization of ``VectorStorage`` for non-trivially-destructible ``T``.
212// This specialization ensures that the elements are cleared during destruction
213// prior to the invalidation of `array_`.
214template <typename T, size_t kMaxSize>
215struct VectorStorage<T, kMaxSize, false>
216 : public Vector<T, vector_impl::kGeneric> {
217 private:
218 using Base = Vector<T, vector_impl::kGeneric>;
219
220 public:
221 ~VectorStorage() {
222 static_cast<Vector<T, vector_impl::kGeneric>*>(this)->clear();
223 }
224
225 protected:
226 VectorStorage() : Base(kMaxSize) {}
227
228 constexpr VectorStorage(ConstexprTag /*constexpr_tag*/)
229 : Base(kMaxSize), array_{} {}
230
231 private:
232 friend class Vector<T, kMaxSize>;
233 friend class Vector<T, vector_impl::kGeneric>;
234
235 using typename Vector<T, vector_impl::kGeneric>::size_type;
236 static_assert(kMaxSize <= std::numeric_limits<size_type>::max());
237
238 using typename Vector<T, vector_impl::kGeneric>::pointer;
239 using typename Vector<T, vector_impl::kGeneric>::const_pointer;
240
241 // Provides access to the underlying array as an array of T.
242#ifdef __cpp_lib_launder
243 pointer array() { return std::launder(reinterpret_cast<T*>(&array_)); }
244 const_pointer array() const {
245 return std::launder(reinterpret_cast<const T*>(&array_));
246 }
247#else
248 pointer array() { return reinterpret_cast<T*>(&array_); }
249 const_pointer array() const { return reinterpret_cast<const T*>(&array_); }
250#endif // __cpp_lib_launder
251
252 // Vector entries are stored as uninitialized memory blocks aligned correctly
253 // for the type. Elements are initialized on demand with placement new.
254 //
255 // This uses std::array instead of a C array to support zero-length Vectors.
256 // Zero-length C arrays are non-standard, but std::array<T, 0> is valid.
257 // The alignas specifier is required ensure that a zero-length array is
258 // aligned the same as an array with elements.
259 alignas(T) std::array<pw::containers::StorageFor<T>, kMaxSize> array_;
260};
261
262// Defines the generic-sized Vector<T> specialization, which serves as the base
263// class for Vector<T> of any maximum size. Except for constructors and
264// destructors, all Vector methods are implemented on this class.
265//
266// Destructors must only be written for the `VectorStorage` type in order to
267// ensure that `array_` is still valid at th etime of destruction.
268//
269// NOTE: this size-polymorphic base class must not be used inside of
270// ``std::unique_ptr`` or ``delete``.
271template <typename T>
272class Vector<T, vector_impl::kGeneric> {
273 public:
274 using value_type = T;
275
276 // Use unsigned short instead of size_t. Since Vectors are statically
277 // allocated, 65535 entries is a reasonable upper limit. This reduces Vector's
278 // overhead by packing the size and capacity into 32 bits.
279 using size_type = unsigned short;
280
281 using difference_type = ptrdiff_t;
282 using reference = value_type&;
283 using const_reference = const value_type&;
284 using pointer = T*;
285 using const_pointer = const T*;
286 using iterator = T*;
287 using const_iterator = const T*;
288 using reverse_iterator = std::reverse_iterator<iterator>;
289 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
290
291 // A vector without an explicit maximum size (Vector<T>) cannot be constructed
292 // directly. Instead, construct a Vector<T, kMaxSize>. Vectors of any max size
293 // can be used through a Vector<T> pointer or reference.
294 Vector() = delete;
295
296 // Assign
297
298 Vector& operator=(const Vector& other) {
299 assign(other.begin(), other.end());
300 return *this;
301 }
302
303 Vector& operator=(Vector&& other) noexcept {
304 clear();
305 MoveFrom(other);
306 return *this;
307 }
308
309 Vector& operator=(std::initializer_list<T> list) {
310 assign(list);
311 return *this;
312 }
313
314 void assign(size_type count, const T& value) {
315 clear();
316 Append(count, value);
317 }
318
319 template <
320 typename Iterator,
321 typename...,
322 typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
323 void assign(Iterator first, Iterator last) {
324 clear();
325 CopyFrom(first, last);
326 }
327
328 void assign(std::initializer_list<T> list) {
329 assign(list.begin(), list.end());
330 }
331
332 // Access
333
334 reference at(size_t index) {
335 PW_ASSERT(index < size());
336 return data()[index];
337 }
338 const_reference at(size_t index) const {
339 PW_ASSERT(index < size());
340 return data()[index];
341 }
342
343 reference operator[](size_t index) {
344 PW_DASSERT(index < size());
345 return data()[index];
346 }
347 const_reference operator[](size_t index) const {
348 PW_DASSERT(index < size());
349 return data()[index];
350 }
351
352 reference front() { return data()[0]; }
353 const_reference front() const { return data()[0]; }
354
355 reference back() { return data()[size() - 1]; }
356 const_reference back() const { return data()[size() - 1]; }
357
358 // The underlying data is not part of the generic-length vector class. It is
359 // provided in the derived class from which this instance was constructed. To
360 // access the data, down-cast this to a Vector with a known max size, and
361 // return a pointer to the start of the array, which is the same for all
362 // vectors with explicit max size.
363 T* data() noexcept { return static_cast<Vector<T, 0>*>(this)->array(); }
364 const T* data() const noexcept {
365 return static_cast<const Vector<T, 0>*>(this)->array();
366 }
367
368 // Iterate
369
370 iterator begin() noexcept { return &data()[0]; }
371 const_iterator begin() const noexcept { return &data()[0]; }
372 const_iterator cbegin() const noexcept { return &data()[0]; }
373
374 iterator end() noexcept { return &data()[size()]; }
375 const_iterator end() const noexcept { return &data()[size()]; }
376 const_iterator cend() const noexcept { return &data()[size()]; }
377
378 reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
379 const_reverse_iterator rbegin() const { return reverse_iterator(end()); }
380 const_reverse_iterator crbegin() const noexcept {
381 return reverse_iterator(cend());
382 }
383
384 reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
385 const_reverse_iterator rend() const { return reverse_iterator(begin()); }
386 const_reverse_iterator crend() const noexcept {
387 return reverse_iterator(cbegin());
388 }
389
390 // Size
391
392 [[nodiscard]] bool empty() const noexcept { return size() == 0u; }
393
394 // True if there is no free space in the vector. Operations that add elements
395 // (push_back, insert, etc.) will fail if full() is true.
396 [[nodiscard]] bool full() const noexcept { return size() == max_size(); }
397
398 // Returns the number of elements in the Vector. Uses size_t instead of
399 // size_type for consistency with other containers.
400 size_t size() const noexcept PW_NO_SANITIZE("memory") { return size_; }
401
402 // Returns the maximum number of elements in this Vector.
403 size_t max_size() const noexcept { return max_size_; }
404
405 size_t capacity() const noexcept { return max_size(); }
406
407 // Modify
408
409 void clear() noexcept;
410
411 iterator insert(const_iterator position, size_type count, const T& value);
412
413 iterator insert(const_iterator position, const T& value) {
414 return insert(position, 1, value);
415 }
416
417 iterator insert(const_iterator position, T&& value);
418
419 template <
420 typename Iterator,
421 int&... ExplicitArgumentBarrier,
422 typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
423 iterator insert(const_iterator position, Iterator first, Iterator last) {
424 return InsertFrom(position, first, last);
425 }
426
427 iterator insert(const_iterator position, std::initializer_list<T> list) {
428 return insert(position, list.begin(), list.end());
429 }
430
431 template <typename... Args>
432 iterator emplace(const_iterator position, Args&&... args);
433
434 iterator erase(const_iterator first, const_iterator last);
435
436 iterator erase(const_iterator position) {
437 return erase(position, position + 1);
438 }
439
440 void push_back(const T& value) { emplace_back(value); }
441
442 void push_back(T&& value) { emplace_back(std::move(value)); }
443
444 template <typename... Args>
445 void emplace_back(Args&&... args);
446
447 void pop_back();
448
449 void resize(size_t new_size) { resize(new_size, T()); }
450
451 void resize(size_t new_size, const T& value);
452
453 protected:
454 // Vectors without an explicit size cannot be constructed directly. Instead,
455 // the maximum size must be provided.
456 explicit constexpr Vector(size_type max_size) noexcept
457 : max_size_(max_size) {}
458
459 // Polymorphic-sized vectors cannot be destroyed directly due to the lack of a
460 // virtual destructor.
461 ~Vector() = default;
462
463 template <typename Iterator>
464 void CopyFrom(Iterator first, Iterator last);
465
466 void MoveFrom(Vector& other) noexcept;
467
468 void Append(size_type count, const T& value);
469
470 template <typename Iterator>
471 iterator InsertFrom(const_iterator position, Iterator first, Iterator last);
472
473 // Shifts entries to make room for an insert operation. Entries shifted past
474 // the end are move constructed with placement new; entries shifted into
475 // existing positions are moved with std::move_backward.
476 void ShiftEntriesForInsert(T* first, size_type to_insert);
477
478 private:
479 const size_type max_size_;
480 size_type size_ = 0;
481};
482
483// Compare
484
485template <typename T, size_t kLhsSize, size_t kRhsSize>
486bool operator==(const Vector<T, kLhsSize>& lhs,
487 const Vector<T, kRhsSize>& rhs) {
488 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
489}
490
491template <typename T, size_t kLhsSize, size_t kRhsSize>
492bool operator!=(const Vector<T, kLhsSize>& lhs,
493 const Vector<T, kRhsSize>& rhs) {
494 return !(lhs == rhs);
495}
496
497template <typename T, size_t kLhsSize, size_t kRhsSize>
498bool operator<(const Vector<T, kLhsSize>& lhs, const Vector<T, kRhsSize>& rhs) {
499 return std::lexicographical_compare(
500 lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
501}
502
503template <typename T, size_t kLhsSize, size_t kRhsSize>
504bool operator<=(const Vector<T, kLhsSize>& lhs,
505 const Vector<T, kRhsSize>& rhs) {
506 return !(rhs < lhs);
507}
508
509template <typename T, size_t kLhsSize, size_t kRhsSize>
510bool operator>(const Vector<T, kLhsSize>& lhs, const Vector<T, kRhsSize>& rhs) {
511 return rhs < lhs;
512}
513
514template <typename T, size_t kLhsSize, size_t kRhsSize>
515bool operator>=(const Vector<T, kLhsSize>& lhs,
516 const Vector<T, kRhsSize>& rhs) {
517 return !(lhs < rhs);
518}
519
520// Function implementations
521
522template <typename T>
523void Vector<T, vector_impl::kGeneric>::clear() noexcept {
524 if constexpr (!std::is_trivially_destructible_v<value_type>) {
525 for (auto& item : *this) {
526 item.~T();
527 }
528 }
529 size_ = 0;
530}
531
532template <typename T>
533template <typename... Args>
534void Vector<T, vector_impl::kGeneric>::emplace_back(Args&&... args) {
535 if (!full()) {
536 new (&data()[size_]) T(std::forward<Args>(args)...);
537 size_ += 1;
538 }
539}
540
541template <typename T>
542void Vector<T, vector_impl::kGeneric>::pop_back() {
543 if (!empty()) {
544 if constexpr (!std::is_trivially_destructible_v<value_type>) {
545 back().~T();
546 }
547 size_ -= 1;
548 }
549}
550
551template <typename T>
552void Vector<T, vector_impl::kGeneric>::resize(size_t new_size, const T& value) {
553 PW_DASSERT(new_size <= std::numeric_limits<size_type>::max());
554 if (size() < new_size) {
555 size_type count =
556 static_cast<size_type>(std::min(max_size(), new_size) - size());
557 Append(count, value);
558 } else {
559 while (size() > new_size) {
560 pop_back();
561 }
562 }
563}
564
565template <typename T>
566typename Vector<T>::iterator Vector<T>::insert(
567 Vector<T>::const_iterator position, T&& value) {
568 PW_DASSERT(position >= cbegin());
569 PW_DASSERT(position <= cend());
570 PW_DASSERT(!full());
571
572 const iterator insertion_point = const_cast<iterator>(position);
573 if (insertion_point == end()) {
574 emplace_back(std::move(value));
575 return insertion_point;
576 }
577
578 ShiftEntriesForInsert(insertion_point, 1);
579
580 *insertion_point = std::move(value);
581 size_ += 1;
582
583 return insertion_point;
584}
585
586template <typename T>
587typename Vector<T>::iterator Vector<T>::insert(
588 Vector<T>::const_iterator position, size_type count, const T& value) {
589 PW_DASSERT(position >= cbegin());
590 PW_DASSERT(position <= cend());
591 PW_DASSERT(size() + count <= max_size());
592
593 const iterator insertion_point = const_cast<iterator>(position);
594 if (count == size_type{}) {
595 return insertion_point;
596 }
597
598 ShiftEntriesForInsert(insertion_point, count);
599
600 size_type inserted = 0;
601 while (insertion_point < end() && inserted < count) {
602 *(insertion_point + inserted) = value;
603 inserted += 1;
604 }
605 for (; inserted < count; ++inserted) {
606 new (&*(insertion_point + inserted)) T(value);
607 }
608
609 size_ += count;
610 return insertion_point;
611}
612
613template <typename T>
614typename Vector<T>::iterator Vector<T>::erase(Vector<T>::const_iterator first,
615 Vector<T>::const_iterator last) {
616 iterator source = const_cast<iterator>(last);
617 if (first == last) {
618 return source;
619 }
620
621 // Move subsequent entries over the to-be-erased range.
622 iterator destination = const_cast<iterator>(first);
623 iterator new_end = std::move(source, end(), destination);
624
625 // Destroy any leftover moved entries.
626 if constexpr (!std::is_trivially_destructible_v<T>) {
627 std::destroy(new_end, end());
628 }
629
630 size_ = static_cast<size_type>(std::distance(begin(), new_end));
631
632 // Return an iterator following the last removed element.
633 return const_cast<iterator>(first);
634}
635
636template <typename T>
637template <typename Iterator>
638void Vector<T, vector_impl::kGeneric>::CopyFrom(Iterator first, Iterator last) {
639 while (first != last) {
640 push_back(*first++);
641 }
642}
643
644template <typename T>
645void Vector<T, vector_impl::kGeneric>::MoveFrom(Vector& other) noexcept {
646 for (auto&& item : other) {
647 emplace_back(std::move(item));
648 }
649 other.clear();
650}
651
652template <typename T>
653void Vector<T, vector_impl::kGeneric>::Append(size_type count, const T& value) {
654 for (size_t i = 0; i < count; ++i) {
655 push_back(value);
656 }
657}
658
659template <typename T>
660template <typename Iterator>
661typename Vector<T>::iterator Vector<T, vector_impl::kGeneric>::InsertFrom(
662 Vector<T>::const_iterator position, Iterator first, Iterator last) {
663 PW_DASSERT(position >= cbegin());
664 PW_DASSERT(position <= cend());
665
666 const iterator insertion_point = const_cast<iterator>(position);
667 const size_t count = static_cast<size_t>(std::distance(first, last));
668 PW_DASSERT(count <= max_size() - size());
669
670 ShiftEntriesForInsert(insertion_point, static_cast<size_type>(count));
671
672 size_type i = 0;
673 iterator dest = insertion_point;
674 while (dest != end() && i < count) {
675 *dest++ = std::move(*first++);
676 i += 1;
677 }
678 for (; i < count; ++i) {
679 new (&(*dest++)) T(std::move(*first++));
680 }
681
682 size_ += static_cast<size_type>(count);
683 return insertion_point;
684}
685
686template <typename T>
687void Vector<T, vector_impl::kGeneric>::ShiftEntriesForInsert(
688 T* first, const size_type to_insert) {
689 ptrdiff_t to_shift = std::distance(first, end());
690
691 // Move construct elements after the end of the vector.
692 T* last = end();
693 T* dest = end() + to_insert;
694 while (dest != end()) {
695 if (to_shift-- == 0) {
696 // All items placed in unallocated slots; no moves needed.
697 return;
698 }
699 new (&(*--dest)) T(std::move(*--last));
700 }
701
702 // Move the remaining items to existing slots.
703 std::move_backward(first, last, end());
704}
705
706} // namespace pw
Definition: vector.h:66
The Pigweed namespace.
Definition: alignment.h:27
Definition: constexpr_tag.h:48
Definition: vector.h:48