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