plugify 1.2.8
Loading...
Searching...
No Matches
vector.hpp
1#pragma once
2
3#include <algorithm>
4#include <compare>
5#include <cstddef>
6#include <cstdint>
7#include <cstring>
8#include <initializer_list>
9#include <iterator>
10#include <limits>
11#include <memory>
12#include <memory_resource>
13#include <optional>
14#include <span>
15#include <type_traits>
16#include <utility>
17
18#include "plg/allocator.hpp"
19#include "plg/guards.hpp"
20#include "plg/split_buffer.hpp"
21#include "plg/uninitialized.hpp"
22
23// Just in case, because we can't ignore some warnings from `-Wpedantic` (about zero size arrays and anonymous structs when gnu extensions are disabled) on gcc
24#if PLUGIFY_COMPILER_CLANG
25# pragma clang system_header
26#elif PLUGIFY_COMPILER_GCC
27# pragma GCC system_header
28#endif
29
30// from https://github.com/llvm/llvm-project/blob/main/libcxx/include/vector
31namespace plg {
32 namespace detail {
33 template <class T, class Alloc>
34 struct temp_value {
35 using allocator_traits = std::allocator_traits<Alloc>;
36
37 union {
38 T v;
39 };
40 PLUGIFY_NO_UNIQUE_ADDRESS Alloc& a;
41
42 constexpr T* addr() {
43 return std::addressof(v);
44 }
45
46 constexpr T& get() {
47 return *addr();
48 }
49
50 template <class... Args>
51 PLUGIFY_NO_CFI constexpr
52 explicit temp_value(Alloc& alloc, Args&&... args)
53 : a(alloc) {
54 allocator_traits::construct(a, addr(), std::forward<Args>(args)...);
55 }
56
57 constexpr ~temp_value() {
58 allocator_traits::destroy(a, addr());
59 }
60 };
61 }
62
63 template <class T, class Allocator = allocator<T>>
64 class vector {
65 template <class U, class Alloc>
67 public:
68 //
69 // Types
70 //
71 using value_type = T;
72 using allocator_type = Allocator;
73 using alloc_traits = std::allocator_traits<allocator_type>;
74 using reference = value_type&;
75 using const_reference = const value_type&;
76 using size_type = typename alloc_traits::size_type;
77 using difference_type = typename alloc_traits::difference_type;
78 using pointer = typename alloc_traits::pointer;
79 using const_pointer = typename alloc_traits::const_pointer;
80 using iterator = pointer;
81 using const_iterator = const_pointer;
82 using reverse_iterator = std::reverse_iterator<iterator>;
83 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
84
85 //static_assert(std::check_valid_allocator<allocator_type>::value, "");
86 static_assert(
87 std::is_same_v<typename allocator_type::value_type, value_type>,
88 "Allocator::value_type must be same type as value_type"
89 );
90
91 //
92 // [vector.cons], construct/copy/destroy
93 //
94 constexpr vector() noexcept(std::is_nothrow_default_constructible_v<allocator_type>) = default;
95
96 constexpr explicit vector(const allocator_type& a) noexcept
97 : alloc_(a) {
98 }
99
100 constexpr explicit vector(size_type n) {
101 auto guard = make_exception_guard(destroy_vector(*this));
102 if (n > 0) {
103 vallocate(n);
104 construct_at_end(n);
105 }
106 guard.complete();
107 }
108
109 constexpr
110 explicit vector(size_type n, const allocator_type& a)
111 : alloc_(a) {
112 auto guard = make_exception_guard(destroy_vector(*this));
113 if (n > 0) {
114 vallocate(n);
115 construct_at_end(n);
116 }
117 guard.complete();
118 }
119
120 constexpr vector(size_type n, const value_type& x) {
121 auto guard = make_exception_guard(destroy_vector(*this));
122 if (n > 0) {
123 vallocate(n);
124 construct_at_end(n, x);
125 }
126 guard.complete();
127 }
128
129 constexpr
130 vector(size_type n, const value_type& x, const allocator_type& a)
131 : alloc_(a) {
132 auto guard = make_exception_guard(destroy_vector(*this));
133 if (n > 0) {
134 vallocate(n);
135 construct_at_end(n, x);
136 }
137 guard.complete();
138 }
139
140 template<std::input_iterator InputIterator>
141 constexpr
143 init_with_sentinel(first, last);
144 }
145
146 template<std::input_iterator InputIterator>
147 constexpr
148 vector(InputIterator first, InputIterator last, const allocator_type& a)
149 : alloc_(a) {
150 init_with_sentinel(first, last);
151 }
152
153 template <std::forward_iterator ForwardIterator>
154 constexpr
156 size_type n = static_cast<size_type>(std::distance(first, last));
157 init_with_size(first, last, n);
158 }
159
160 template <std::forward_iterator ForwardIterator>
161 constexpr
162 vector(ForwardIterator first, ForwardIterator last, const allocator_type& a)
163 : alloc_(a) {
164 size_type n = static_cast<size_type>(std::distance(first, last));
165 init_with_size(first, last, n);
166 }
167
168#if PLUGIFY_HAS_CXX23
169 template <container_compatible_range<T> Range>
170 constexpr vector(
171 std::from_range_t,
172 Range&& range,
173 const allocator_type& alloc = allocator_type()
174 ) : alloc_(alloc) {
175 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
176 auto n = static_cast<size_type>(std::ranges::distance(range));
177 init_with_size(std::ranges::begin(range), std::ranges::end(range), n);
178
179 } else {
180 init_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
181 }
182 }
183#endif
184
185 private:
186 class destroy_vector {
187 public:
188 constexpr explicit destroy_vector(vector& vec)
189 : vec_(vec) {
190 }
191
192 constexpr void operator()() {
193 if (vec_.begin_ != nullptr) {
194 vec_.clear();
195 vec_.annotate_delete();
196 alloc_traits::deallocate(vec_.alloc_, vec_.begin_, vec_.capacity());
197 }
198 }
199
200 private:
201 vector& vec_;
202 };
203
204 public:
205 constexpr ~vector() {
206 destroy_vector (*this)();
207 }
208
209 constexpr vector(const vector& x)
210 : alloc_(alloc_traits::select_on_container_copy_construction(x.alloc_)) {
211 init_with_size(x.begin_, x.end_, x.size());
212 }
213
214 constexpr
215 vector(const vector& x, const std::type_identity_t<allocator_type>& a)
216 : alloc_(a) {
217 init_with_size(x.begin_, x.end_, x.size());
218 }
219
220 constexpr vector& operator=(const vector& x);
221
222 constexpr vector(std::initializer_list<value_type> il) {
223 init_with_size(il.begin(), il.end(), il.size());
224 }
225
226 constexpr
227 vector(std::initializer_list<value_type> il, const allocator_type& a)
228 : alloc_(a) {
229 init_with_size(il.begin(), il.end(), il.size());
230 }
231
232 constexpr vector&
233 operator=(std::initializer_list<value_type> il) {
234 assign(il.begin(), il.end());
235 return *this;
236 }
237
238 constexpr vector(vector&& x) noexcept;
239
240 constexpr
241 vector(vector&& x, const std::type_identity_t<allocator_type>& a);
242
243 constexpr vector& operator=(vector&& x) noexcept(
244 std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
245 std::allocator_traits<Allocator>::is_always_equal::value)
246 {
247 move_assign(
248 x,
249 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
250 );
251 return *this;
252 }
253
254 template<std::input_iterator InputIterator>
255 constexpr void
257 assign_with_sentinel(first, last);
258 }
259
260 template <std::forward_iterator ForwardIterator>
261 constexpr void
263 assign_with_size(first, last, std::distance(first, last));
264 }
265
266#if PLUGIFY_HAS_CXX23
267 template <container_compatible_range<T> Range>
268 constexpr void assign_range(Range&& range) {
269 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
270 auto n = static_cast<size_type>(std::ranges::distance(range));
271 assign_with_size(std::ranges::begin(range), std::ranges::end(range), n);
272
273 } else {
274 assign_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
275 }
276 }
277#endif
278
279 constexpr void
280 assign(size_type n, const_reference u);
281
282 constexpr void
283 assign(std::initializer_list<value_type> il) {
284 assign(il.begin(), il.end());
285 }
286
287 [[nodiscard]] constexpr allocator_type
288 get_allocator() const noexcept {
289 return this->alloc_;
290 }
291
292 //
293 // Iterators
294 //
295 [[nodiscard]] constexpr iterator begin() noexcept {
296 return make_iter(add_alignment_assumption(this->begin_));
297 }
298
299 [[nodiscard]] constexpr const_iterator
300 begin() const noexcept {
301 return make_iter(add_alignment_assumption(this->begin_));
302 }
303
304 [[nodiscard]] constexpr iterator end() noexcept {
305 return make_iter(add_alignment_assumption(this->end_));
306 }
307
308 [[nodiscard]] constexpr const_iterator
309 end() const noexcept {
310 return make_iter(add_alignment_assumption(this->end_));
311 }
312
313 [[nodiscard]] constexpr reverse_iterator
314 rbegin() noexcept {
315 return reverse_iterator(end());
316 }
317
318 [[nodiscard]] constexpr const_reverse_iterator
319 rbegin() const noexcept {
320 return const_reverse_iterator(end());
321 }
322
323 [[nodiscard]] constexpr reverse_iterator
324 rend() noexcept {
325 return reverse_iterator(begin());
326 }
327
328 [[nodiscard]] constexpr const_reverse_iterator
329 rend() const noexcept {
330 return const_reverse_iterator(begin());
331 }
332
333 [[nodiscard]] constexpr const_iterator
334 cbegin() const noexcept {
335 return begin();
336 }
337
338 [[nodiscard]] constexpr const_iterator
339 cend() const noexcept {
340 return end();
341 }
342
343 [[nodiscard]] constexpr const_reverse_iterator
344 crbegin() const noexcept {
345 return rbegin();
346 }
347
348 [[nodiscard]] constexpr const_reverse_iterator
349 crend() const noexcept {
350 return rend();
351 }
352
353 //
354 // [vector.capacity], capacity
355 //
356 [[nodiscard]] constexpr size_type size() const noexcept {
357 return static_cast<size_type>(this->end_ - this->begin_);
358 }
359
360 [[nodiscard]] constexpr size_type
361 capacity() const noexcept {
362 return static_cast<size_type>(this->cap_ - this->begin_);
363 }
364
365 [[nodiscard]] constexpr bool
366 empty() const noexcept {
367 return this->begin_ == this->end_;
368 }
369
370 [[nodiscard]] constexpr size_type
371 max_size() const noexcept {
372 return std::min<size_type>(
373 alloc_traits::max_size(this->alloc_),
374 std::numeric_limits<difference_type>::max()
375 );
376 }
377
378 constexpr void reserve(size_type n);
379 constexpr void shrink_to_fit() noexcept;
380
381 //
382 // element access
383 //
384 [[nodiscard]] constexpr reference
385 operator[](size_type n) noexcept {
386 PLUGIFY_ASSERT(n < size(), "vector[] index out of bounds");
387 return this->begin_[n];
388 }
389
390 [[nodiscard]] constexpr const_reference
391 operator[](size_type n) const noexcept {
392 PLUGIFY_ASSERT(n < size(), "vector[] index out of bounds");
393 return this->begin_[n];
394 }
395
396 [[nodiscard]] constexpr reference at(size_type n) {
397 if (n >= size()) {
398 this->throw_out_of_range();
399 }
400 return this->begin_[n];
401 }
402
403 [[nodiscard]] constexpr const_reference
404 at(size_type n) const {
405 if (n >= size()) {
406 this->throw_out_of_range();
407 }
408 return this->begin_[n];
409 }
410
411 [[nodiscard]] constexpr reference front() noexcept {
412 PLUGIFY_ASSERT(!empty(), "front() called on an empty vector");
413 return *this->begin_;
414 }
415
416 [[nodiscard]] constexpr const_reference
417 front() const noexcept {
418 PLUGIFY_ASSERT(!empty(), "front() called on an empty vector");
419 return *this->begin_;
420 }
421
422 [[nodiscard]] constexpr reference back() noexcept {
423 PLUGIFY_ASSERT(!empty(), "back() called on an empty vector");
424 return *(this->end_ - 1);
425 }
426
427 [[nodiscard]] constexpr const_reference
428 back() const noexcept {
429 PLUGIFY_ASSERT(!empty(), "back() called on an empty vector");
430 return *(this->end_ - 1);
431 }
432
433 //
434 // [vector.data], data access
435 //
436 [[nodiscard]] constexpr value_type*
437 data() noexcept {
438 return std::to_address(this->begin_);
439 }
440
441 [[nodiscard]] constexpr const value_type*
442 data() const noexcept {
443 return std::to_address(this->begin_);
444 }
445
446 //
447 // [vector.modifiers], modifiers
448 //
449 constexpr void push_back(const_reference x) {
450 emplace_back(x);
451 }
452
453 constexpr void push_back(value_type&& x) {
454 emplace_back(std::move(x));
455 }
456
457 template <class... Args>
458 constexpr
459 reference
460 emplace_back(Args&&... args);
461
462 template <class... Args>
463 constexpr void
464 emplace_back_assume_capacity(Args&&... args) {
466 size() < capacity(),
467 "We assume that we have enough space to insert an element at the end of the vector"
468 );
469 ConstructTransaction tx(*this, 1);
470 alloc_traits::construct(this->alloc_, std::to_address(tx.pos_), std::forward<Args>(args)...);
471 ++tx.pos_;
472 }
473
474#if PLUGIFY_HAS_CXX23
475 template <container_compatible_range<T> Range>
476 constexpr void append_range(Range&& range) {
477 insert_range(end(), std::forward<Range>(range));
478 }
479#endif
480
481 constexpr void pop_back() {
482 PLUGIFY_ASSERT(!empty(), "vector::pop_back called on an empty vector");
483 this->destruct_at_end(this->end_ - 1);
484 }
485
486 constexpr iterator
487 insert(const_iterator position, const_reference x);
488
489 constexpr iterator
490 insert(const_iterator position, value_type&& x);
491 template <class... Args>
492 constexpr iterator
493 emplace(const_iterator position, Args&&... args);
494
495 constexpr iterator
496 insert(const_iterator position, size_type n, const_reference x);
497
498 template<std::input_iterator InputIterator>
499 constexpr iterator
500 insert(const_iterator position, InputIterator first, InputIterator last) {
501 return insert_with_sentinel(position, first, last);
502 }
503
504 template <std::forward_iterator ForwardIterator>
505 constexpr iterator
506 insert(const_iterator position, ForwardIterator first, ForwardIterator last) {
507 return insert_with_size(position, first, last, std::distance(first, last));
508 }
509
510#if PLUGIFY_HAS_CXX23
511 template <container_compatible_range<T> Range>
512 constexpr iterator
513 insert_range(const_iterator position, Range&& range) {
514 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
515 auto n = static_cast<size_type>(std::ranges::distance(range));
516 return insert_with_size(position, std::ranges::begin(range), std::ranges::end(range), n);
517
518 } else {
519 return insert_with_sentinel(position, std::ranges::begin(range), std::ranges::end(range));
520 }
521 }
522#endif
523
524 constexpr iterator
525 insert(const_iterator position, std::initializer_list<value_type> il) {
526 return insert(position, il.begin(), il.end());
527 }
528
529 constexpr iterator erase(const_iterator position);
530 constexpr iterator
531 erase(const_iterator first, const_iterator last);
532
533 constexpr void clear() noexcept {
534 size_type old_size = size();
535 base_destruct_at_end(this->begin_);
536 annotate_shrink(old_size);
537 }
538
539 constexpr void resize(size_type sz);
540 constexpr void
541 resize(size_type sz, const_reference x);
542
543 constexpr void swap(vector&) noexcept;
544
545 constexpr bool invariants() const;
546
547 private:
548 pointer begin_ = nullptr;
549 pointer end_ = nullptr;
550 pointer cap_ = nullptr;
551 PLUGIFY_NO_UNIQUE_ADDRESS
552 allocator_type alloc_;
553
554 // Allocate space for n objects
555 // throws length_error if n > max_size()
556 // throws (probably bad_alloc) if memory run out
557 // Precondition: begin_ == end_ == cap_ == nullptr
558 // Precondition: n > 0
559 // Postcondition: capacity() >= n
560 // Postcondition: size() == 0
561 constexpr void vallocate(size_type n) {
562 if (n > max_size()) {
563 this->throw_length_error();
564 }
565 auto allocation = allocate_at_least(this->alloc_, n);
566 begin_ = allocation.ptr;
567 end_ = allocation.ptr;
568 cap_ = begin_ + allocation.count;
569 annotate_new(0);
570 }
571
572 constexpr void vdeallocate() noexcept;
573 constexpr size_type recommend(size_type new_size) const;
574 constexpr void construct_at_end(size_type n);
575 constexpr void
576 construct_at_end(size_type n, const_reference x);
577
578 template <class InputIterator, class Sentinel>
579 constexpr void
580 init_with_size(InputIterator first, Sentinel last, size_type n) {
581 auto guard = make_exception_guard(destroy_vector(*this));
582
583 if (n > 0) {
584 vallocate(n);
585 construct_at_end(std::move(first), std::move(last), n);
586 }
587
588 guard.complete();
589 }
590
591 template <class InputIterator, class Sentinel>
592 constexpr void
593 init_with_sentinel(InputIterator first, Sentinel last) {
594 auto guard = make_exception_guard(destroy_vector(*this));
595
596 for (; first != last; ++first) {
597 emplace_back(*first);
598 }
599
600 guard.complete();
601 }
602
603 template <class Iterator, class Sentinel>
604 constexpr void
605 assign_with_sentinel(Iterator first, Sentinel last);
606
607 // The `Iterator` in `*_with_size` functions can be input-only only if called from
608 // `*_range` (since C++23). Otherwise, `Iterator` is a forward iterator.
609
610 template <class Iterator, class Sentinel>
611 constexpr void
612 assign_with_size(Iterator first, Sentinel last, difference_type n);
613
614 template <class Iterator>
615 requires (!std::same_as<decltype(*std::declval<Iterator&>())&&, value_type&&>)
616 constexpr void
617 insert_assign_n_unchecked(Iterator first, difference_type n, pointer position) {
618 for (pointer end_position = position + n; position != end_position;
619 ++position, (void) ++first) {
621 *position = std::move(tmp.get());
622 }
623 }
624
625 template <class Iterator>
626 requires (std::same_as<decltype(*std::declval<Iterator&>())&&, value_type&&>)
627 constexpr void
628 insert_assign_n_unchecked(Iterator first, difference_type n, pointer position) {
629#if PLUGIFY_HAS_CXX23
630 if constexpr (!std::forward_iterator<Iterator>) {
631 // Handles input-only sized ranges for insert_range
632 std::ranges::copy_n(std::move(first), n, position);
633 } else
634#endif
635 {
636 std::copy_n(first, n, position);
637 }
638 }
639
640 template <class InputIterator, class Sentinel>
641 constexpr iterator
642 insert_with_sentinel(const_iterator position, InputIterator first, Sentinel last);
643
644 template <class Iterator, class Sentinel>
645 constexpr iterator
646 insert_with_size(const_iterator position, Iterator first, Sentinel last, difference_type n);
647
648 template <class InputIterator, class Sentinel>
649 constexpr void
650 construct_at_end(InputIterator first, Sentinel last, size_type n);
651
652 constexpr void append(size_type n);
653 constexpr void
654 append(size_type n, const_reference x);
655
656 constexpr iterator make_iter(pointer p) noexcept {
657 return iterator(p);
658 }
659
660 constexpr const_iterator make_iter(const_pointer p) const noexcept {
661 return const_iterator(p);
662 }
663
664 constexpr void
665 swap_out_circular_buffer(split_buffer<value_type, allocator_type&>& v);
666 constexpr pointer
667 swap_out_circular_buffer(split_buffer<value_type, allocator_type&>& v, pointer p);
668 constexpr void
669 move_range(pointer from_s, pointer from_e, pointer to);
670 constexpr void
671 move_assign(vector& c, std::true_type) noexcept(std::is_nothrow_move_assignable<allocator_type>::value);
672 constexpr void
673 move_assign(vector& c, std::false_type) noexcept(alloc_traits::is_always_equal::value);
674
675 constexpr void
676 destruct_at_end(pointer new_last) noexcept {
677 size_type old_size = size();
678 base_destruct_at_end(new_last);
679 annotate_shrink(old_size);
680 }
681
682 template <class... Args>
683 constexpr inline pointer
684 emplace_back_slow_path(Args&&... args);
685
686 // The following functions are no-ops outside of AddressSanitizer mode.
687 // We call annotations for every allocator, unless explicitly disabled.
688 //
689 // To disable annotations for a particular allocator, change value of
690 // asan_annotate_container_with_allocator to false.
691 // For more details, see the "Using libc++" documentation page or
692 // the documentation for sanitizer_annotate_contiguous_container.
693
694 constexpr void
695 annotate_contiguous_container(
696 [[maybe_unused]] const void* old_mid,
697 [[maybe_unused]] const void* new_mid
698 ) const {
700 }
701
702 constexpr void
703 annotate_new(size_type current_size) const noexcept {
704 annotate_contiguous_container(data() + capacity(), data() + current_size);
705 }
706
707 constexpr void annotate_delete() const noexcept {
708 annotate_contiguous_container(data() + size(), data() + capacity());
709 }
710
711 constexpr void
712 annotate_increase(size_type n) const noexcept {
713 annotate_contiguous_container(data() + size(), data() + size() + n);
714 }
715
716 constexpr void
717 annotate_shrink(size_type old_size) const noexcept {
718 annotate_contiguous_container(data() + old_size, data() + size());
719 }
720
721 struct ConstructTransaction {
722 constexpr
723 explicit ConstructTransaction(vector& v, size_type n)
724 : v_(v)
725 , pos_(v.end_)
726 , new_end_(v.end_ + n) {
727 v.annotate_increase(n);
728 }
729
730 constexpr ~ConstructTransaction() {
731 v_.end_ = pos_;
732 if (pos_ != new_end_) {
733 v_.annotate_shrink(new_end_ - v_.begin_);
734 }
735 }
736
737 vector& v_;
738 pointer pos_;
739 const const_pointer new_end_;
740
741 ConstructTransaction(const ConstructTransaction&) = delete;
742 ConstructTransaction& operator=(const ConstructTransaction&) = delete;
743 };
744
745 constexpr void
746 base_destruct_at_end(pointer new_last) noexcept {
747 pointer soon_to_be_end = this->end_;
748 while (new_last != soon_to_be_end) {
749 alloc_traits::destroy(this->alloc_, std::to_address(--soon_to_be_end));
750 }
751 this->end_ = new_last;
752 }
753
754 constexpr void copy_assign_alloc(const vector& c) {
755 copy_assign_alloc(
756 c,
757 std::integral_constant<bool, alloc_traits::propagate_on_container_copy_assignment::value>()
758 );
759 }
760
761 constexpr void
762 move_assign_alloc(vector& c) noexcept(
763 !alloc_traits::propagate_on_container_move_assignment::value
764 || std::is_nothrow_move_assignable<allocator_type>::value
765 ) {
766 move_assign_alloc(
767 c,
768 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
769 );
770 }
771
772 [[noreturn]] static void throw_length_error() {
773 PLUGIFY_THROW("allocated memory size would exceed max_size()", std::length_error);
774 }
775
776 [[noreturn]] static void throw_out_of_range() {
777 PLUGIFY_THROW("input index is out of bounds", std::out_of_range);
778 }
779
780 constexpr void
781 copy_assign_alloc(const vector& c, std::true_type) {
782 if (this->alloc_ != c.alloc_) {
783 clear();
784 annotate_delete();
785 alloc_traits::deallocate(this->alloc_, this->begin_, capacity());
786 this->begin_ = this->end_ = this->cap_ = nullptr;
787 }
788 this->alloc_ = c.alloc_;
789 }
790
791 constexpr void
792 copy_assign_alloc(const vector&, std::false_type) {
793 }
794
795 constexpr void
796 move_assign_alloc(vector& c, std::true_type) noexcept(
797 std::is_nothrow_move_assignable_v<allocator_type>
798 ) {
799 this->alloc_ = std::move(c.alloc_);
800 }
801
802 constexpr void
803 move_assign_alloc(vector&, std::false_type) noexcept {
804 }
805
806 template <class Ptr = pointer>
807 requires(std::is_pointer_v<Ptr>)
808 static constexpr PLUGIFY_NO_CFI pointer add_alignment_assumption(Ptr p) noexcept {
809 if (!std::is_constant_evaluated()) {
810 return static_cast<pointer>(std::assume_aligned<alignof(decltype(*p))>(p));
811 }
812 return p;
813 }
814
815 template <class Ptr = pointer>
816 requires(!std::is_pointer_v<Ptr>)
817 static constexpr PLUGIFY_NO_CFI pointer add_alignment_assumption(Ptr p) noexcept {
818 return p;
819 }
820
821 constexpr void
822 swap_layouts(split_buffer<T, allocator_type&>& sb) {
823 auto vector_begin = begin_;
824 auto vector_sentinel = end_;
825 auto vector_cap = cap_;
826
827 auto sb_begin = sb.begin();
828 auto sb_sentinel = sb.raw_sentinel();
829 auto sb_cap = sb.raw_capacity();
830
831 // TODO: replace with set_valid_range and set_capacity when vector supports it.
832 begin_ = sb_begin;
833 end_ = sb_sentinel;
834 cap_ = sb_cap;
835
836 sb.set_valid_range(vector_begin, vector_sentinel);
837 sb.set_capacity(vector_cap);
838 }
839 };
840
841 template <
842 std::input_iterator InputIterator,
846
847#if PLUGIFY_HAS_CXX23
848 template <
849 std::ranges::input_range Range,
851 vector(std::from_range_t, Range&&, Alloc = Alloc())
853#endif
854
855 // swap_out_circular_buffer relocates the objects in [begin_, end_) into the front of v and
856 // swaps the buffers of *this and v. It is assumed that v provides space for exactly (end_ -
857 // begin_) objects in the front. This function has a strong exception guarantee.
858 template <class T, class Allocator>
859 constexpr void
861 annotate_delete();
862 auto new_begin = v.begin() - size();
863 uninitialized_allocator_relocate(
864 this->alloc_,
865 std::to_address(begin_),
866 std::to_address(end_),
867 std::to_address(new_begin)
868 );
869 v.set_valid_range(new_begin, v.end());
870 end_ = begin_; // All the objects have been destroyed by relocating them.
871
872 swap_layouts(v);
873 v.set_data(v.begin());
874 annotate_new(size());
875 }
876
877 // swap_out_circular_buffer relocates the objects in [begin_, p) into the front of v, the
878 // objects in [p, end_) into the back of v and swaps the buffers of *this and v. It is assumed
879 // that v provides space for exactly (p - begin_) objects in the front and space for at least
880 // (end_ - p) objects in the back. This function has a strong exception guarantee if begin_ == p
881 // || end_ == p.
882 template <class T, class Allocator>
883 constexpr typename vector<T, Allocator>::pointer
884 vector<T, Allocator>::swap_out_circular_buffer(
885 split_buffer<value_type, allocator_type&>& v,
886 pointer p
887 ) {
888 annotate_delete();
889 pointer ret = v.begin();
890
891 // Relocate [p, end_) first to avoid having a hole in [begin_, end_)
892 // in case something in [begin_, p) throws.
893 uninitialized_allocator_relocate(
894 this->alloc_,
895 std::to_address(p),
896 std::to_address(end_),
897 std::to_address(v.end())
898 );
899 auto relocated_so_far = end_ - p;
900 v.set_sentinel(v.end() + relocated_so_far);
901 end_ = p; // The objects in [p, end_) have been destroyed by relocating them.
902 auto new_begin = v.begin() - (p - begin_);
903
904 uninitialized_allocator_relocate(
905 this->alloc_,
906 std::to_address(begin_),
907 std::to_address(p),
908 std::to_address(new_begin)
909 );
910 v.set_valid_range(new_begin, v.end());
911 end_ = begin_; // All the objects have been destroyed by relocating them.
912 swap_layouts(v);
913 v.set_data(v.begin());
914 annotate_new(size());
915 return ret;
916 }
917
918 template <class T, class Allocator>
919 constexpr void vector<T, Allocator>::vdeallocate() noexcept {
920 if (this->begin_ != nullptr) {
921 clear();
922 annotate_delete();
923 alloc_traits::deallocate(this->alloc_, this->begin_, capacity());
924 this->begin_ = this->end_ = this->cap_ = nullptr;
925 }
926 }
927
928 // Precondition: new_size > capacity()
929 template <class T, class Allocator>
930 constexpr inline
931 typename vector<T, Allocator>::size_type
932 vector<T, Allocator>::recommend(size_type new_size) const {
933 const size_type ms = max_size();
934 if (new_size > ms) {
935 this->throw_length_error();
936 }
937 const size_type cap = capacity();
938 if (cap >= ms / 2) {
939 return ms;
940 }
941 return std::max<size_type>(2 * cap, new_size);
942 }
943
944 // Default constructs n objects starting at end_
945 // throws if construction throws
946 // Precondition: n > 0
947 // Precondition: size() + n <= capacity()
948 // Postcondition: size() == size() + n
949 template <class T, class Allocator>
950 constexpr void vector<T, Allocator>::construct_at_end(size_type n) {
951 ConstructTransaction tx(*this, n);
952 const_pointer new_end = tx.new_end_;
953 for (pointer pos = tx.pos_; pos != new_end; tx.pos_ = ++pos) {
954 alloc_traits::construct(this->alloc_, std::to_address(pos));
955 }
956 }
957
958 // Copy constructs n objects starting at end_ from x
959 // throws if construction throws
960 // Precondition: n > 0
961 // Precondition: size() + n <= capacity()
962 // Postcondition: size() == old size() + n
963 // Postcondition: [i] == x for all i in [size() - n, n)
964 template <class T, class Allocator>
965 constexpr inline void
966 vector<T, Allocator>::construct_at_end(size_type n, const_reference x) {
967 ConstructTransaction tx(*this, n);
968 const_pointer new_end = tx.new_end_;
969 for (pointer pos = tx.pos_; pos != new_end; tx.pos_ = ++pos) {
970 alloc_traits::construct(this->alloc_, std::to_address(pos), x);
971 }
972 }
973
974 template <class T, class Allocator>
975 template <class InputIterator, class Sentinel>
976 constexpr void
977 vector<T, Allocator>::construct_at_end(InputIterator first, Sentinel last, size_type n) {
978 ConstructTransaction tx(*this, n);
979 tx.pos_ = uninitialized_allocator_copy(
980 this->alloc_,
981 std::move(first),
982 std::move(last),
983 tx.pos_
984 );
985 }
986
987 // Default constructs n objects starting at end_
988 // throws if construction throws
989 // Postcondition: size() == size() + n
990 // Exception safety: strong.
991 template <class T, class Allocator>
992 constexpr void vector<T, Allocator>::append(size_type n) {
993 if (static_cast<size_type>(this->cap_ - this->end_) >= n) {
994 this->construct_at_end(n);
995 } else {
996 split_buffer<value_type, allocator_type&> v(recommend(size() + n), size(), this->alloc_);
997 v.construct_at_end(n);
998 swap_out_circular_buffer(v);
999 }
1000 }
1001
1002 // Default constructs n objects starting at end_
1003 // throws if construction throws
1004 // Postcondition: size() == size() + n
1005 // Exception safety: strong.
1006 template <class T, class Allocator>
1007 constexpr void
1008 vector<T, Allocator>::append(size_type n, const_reference x) {
1009 if (static_cast<size_type>(this->cap_ - this->end_) >= n) {
1010 this->construct_at_end(n, x);
1011 } else {
1012 split_buffer<value_type, allocator_type&> v(recommend(size() + n), size(), this->alloc_);
1013 v.construct_at_end(n, x);
1014 swap_out_circular_buffer(v);
1015 }
1016 }
1017
1018 template <class T, class Allocator>
1019 constexpr inline
1020 vector<T, Allocator>::vector(vector&& x) noexcept
1021 : alloc_(std::move(x.alloc_)) {
1022 this->begin_ = x.begin_;
1023 this->end_ = x.end_;
1024 this->cap_ = x.cap_;
1025 x.begin_ = x.end_ = x.cap_ = nullptr;
1026 }
1027
1028 template <class T, class Allocator>
1029 constexpr inline
1030 vector<T, Allocator>::vector(vector&& x, const std::type_identity_t<allocator_type>& a)
1031 : alloc_(a) {
1032 if (a == x.alloc_) {
1033 this->begin_ = x.begin_;
1034 this->end_ = x.end_;
1035 this->cap_ = x.cap_;
1036 x.begin_ = x.end_ = x.cap_ = nullptr;
1037 } else {
1038 using Ip = std::move_iterator<iterator>;
1039 init_with_size(Ip(x.begin()), Ip(x.end()), x.size());
1040 }
1041 }
1042
1043 template <class T, class Allocator>
1044 constexpr void
1045 vector<T, Allocator>::move_assign(vector& c, std::false_type) noexcept(
1046 alloc_traits::is_always_equal::value
1047 ) {
1048 if (this->alloc_ != c.alloc_) {
1049 using Ip = std::move_iterator<iterator>;
1050 assign(Ip(c.begin()), Ip(c.end()));
1051 } else {
1052 move_assign(c, std::true_type());
1053 }
1054 }
1055
1056 template <class T, class Allocator>
1057 constexpr void
1058 vector<T, Allocator>::move_assign(vector& c, std::true_type) noexcept(
1059 std::is_nothrow_move_assignable<allocator_type>::value
1060 ) {
1061 vdeallocate();
1062 move_assign_alloc(c); // this can throw
1063 this->begin_ = c.begin_;
1064 this->end_ = c.end_;
1065 this->cap_ = c.cap_;
1066 c.begin_ = c.end_ = c.cap_ = nullptr;
1067 }
1068
1069 template <class T, class Allocator>
1070 constexpr inline vector<T, Allocator>&
1071 vector<T, Allocator>::operator=(const vector& x) {
1072 if (this != std::addressof(x)) {
1073 copy_assign_alloc(x);
1074 assign(x.begin_, x.end_);
1075 }
1076 return *this;
1077 }
1078
1079 template <class T, class Allocator>
1080 template <class Iterator, class Sentinel>
1081 constexpr void
1082 vector<T, Allocator>::assign_with_sentinel(Iterator first, Sentinel last) {
1083 pointer cur = begin_;
1084 for (; first != last && cur != end_; ++first, (void) ++cur) {
1085 *cur = *first;
1086 }
1087 if (cur != end_) {
1088 destruct_at_end(cur);
1089 } else {
1090 for (; first != last; ++first) {
1091 emplace_back(*first);
1092 }
1093 }
1094 }
1095
1096 template <class T, class Allocator>
1097 template <class Iterator, class Sentinel>
1098 constexpr void
1099 vector<T, Allocator>::assign_with_size(Iterator first, Sentinel last, difference_type n) {
1100 size_type new_size = static_cast<size_type>(n);
1101 if (new_size <= capacity()) {
1102 if (new_size > size()) {
1103#if PLUGIFY_HAS_CXX23
1104 auto mid = std::ranges::copy_n(std::move(first), size(), this->begin_).in;
1105 construct_at_end(std::move(mid), std::move(last), new_size - size());
1106#else
1107 Iterator mid = std::next(first, size());
1108 std::copy(first, mid, this->begin_);
1109 construct_at_end(mid, last, new_size - size());
1110#endif
1111 } else {
1112 pointer m = std::copy(std::move(first), last, this->begin_);
1113 this->destruct_at_end(m);
1114 }
1115 } else {
1116 vdeallocate();
1117 vallocate(recommend(new_size));
1118 construct_at_end(std::move(first), std::move(last), new_size);
1119 }
1120 }
1121
1122 template <class T, class Allocator>
1123 constexpr void
1124 vector<T, Allocator>::assign(size_type n, const_reference u) {
1125 if (n <= capacity()) {
1126 size_type s = size();
1127 std::fill_n(this->begin_, std::min(n, s), u);
1128 if (n > s) {
1129 construct_at_end(n - s, u);
1130 } else {
1131 this->destruct_at_end(this->begin_ + n);
1132 }
1133 } else {
1134 vdeallocate();
1135 vallocate(recommend(static_cast<size_type>(n)));
1136 construct_at_end(n, u);
1137 }
1138 }
1139
1140 template <class T, class Allocator>
1141 constexpr void vector<T, Allocator>::reserve(size_type n) {
1142 if (n > capacity()) {
1143 if (n > max_size()) {
1144 this->throw_length_error();
1145 }
1146 split_buffer<value_type, allocator_type&> v(n, size(), this->alloc_);
1147 swap_out_circular_buffer(v);
1148 }
1149 }
1150
1151 template <class T, class Allocator>
1152 constexpr void vector<T, Allocator>::shrink_to_fit() noexcept {
1153 if (capacity() > size()) {
1154#if PLUGIFY_HAS_EXCEPTIONS
1155 try {
1156#endif // PLUGIFY_HAS_EXCEPTIONS
1157 split_buffer<value_type, allocator_type&> v(size(), size(), this->alloc_);
1158 // The Standard mandates shrink_to_fit() does not increase the capacity.
1159 // With equal capacity keep the existing buffer. This avoids extra work
1160 // due to swapping the elements.
1161 if (v.capacity() < capacity()) {
1162 swap_out_circular_buffer(v);
1163 }
1164#if PLUGIFY_HAS_EXCEPTIONS
1165 } catch (...) {
1166 }
1167#endif // PLUGIFY_HAS_EXCEPTIONS
1168 }
1169 }
1170
1171 template <class T, class Allocator>
1172 template <class... Args>
1173 constexpr typename vector<T, Allocator>::pointer
1174 vector<T, Allocator>::emplace_back_slow_path(Args&&... args) {
1175 split_buffer<value_type, allocator_type&> v(recommend(size() + 1), size(), this->alloc_);
1176 // v.emplace_back(std::forward<Args>(args)...);
1177 pointer end = v.end();
1178 alloc_traits::construct(this->alloc_, std::to_address(end), std::forward<Args>(args)...);
1179 v.set_sentinel(++end);
1180 swap_out_circular_buffer(v);
1181 return this->end_;
1182 }
1183
1184 // This makes the compiler inline `else()` if `cond` is known to be false. Currently LLVM
1185 // doesn't do that without the `builtin_constant_p`, since it considers `else` unlikely even
1186 // through it's known to be run. See https://llvm.org/PR154292
1187 template <class If, class Else>
1188 constexpr void
1189 if_likely_else(bool cond, If _if, Else _else) {
1190 if (__builtin_constant_p(cond)) {
1191 if (cond) {
1192 _if();
1193 } else {
1194 _else();
1195 }
1196 } else {
1197 if (cond) [[likely]] {
1198 _if();
1199 } else {
1200 _else();
1201 }
1202 }
1203 }
1204
1205 template <class T, class Allocator>
1206 template <class... Args>
1207 constexpr inline
1208 typename vector<T, Allocator>::reference
1209 vector<T, Allocator>::emplace_back(Args&&... args) {
1210 pointer end = this->end_;
1211 if_likely_else(
1212 end < this->cap_,
1213 [&] {
1214 emplace_back_assume_capacity(std::forward<Args>(args)...);
1215 ++end;
1216 },
1217 [&] { end = emplace_back_slow_path(std::forward<Args>(args)...); }
1218 );
1219
1220 this->end_ = end;
1221 return *(end - 1);
1222 }
1223
1224 template <class T, class Allocator>
1225 constexpr inline
1226 typename vector<T, Allocator>::iterator
1227 vector<T, Allocator>::erase(const_iterator position) {
1228 PLUGIFY_ASSERT(
1229 position != end(),
1230 "vector::erase(iterator) called with a non-dereferenceable iterator"
1231 );
1232 difference_type ps = position - cbegin();
1233 pointer p = this->begin_ + ps;
1234 this->destruct_at_end(std::move(p + 1, this->end_, p));
1235 return make_iter(p);
1236 }
1237
1238 template <class T, class Allocator>
1239 constexpr typename vector<T, Allocator>::iterator
1240 vector<T, Allocator>::erase(const_iterator first, const_iterator last) {
1241 PLUGIFY_ASSERT(
1242 first <= last,
1243 "vector::erase(first, last) called with invalid range"
1244 );
1245 pointer p = this->begin_ + (first - begin());
1246 if (first != last) {
1247 this->destruct_at_end(std::move(p + (last - first), this->end_, p));
1248 }
1249 return make_iter(p);
1250 }
1251
1252 template <class T, class Allocator>
1253 constexpr void
1254 vector<T, Allocator>::move_range(pointer from_s, pointer from_e, pointer to) {
1255 pointer old_last = this->end_;
1256 difference_type n = old_last - to;
1257 {
1258 pointer i = from_s + n;
1259 ConstructTransaction tx(*this, from_e - i);
1260 for (pointer pos = tx.pos_; i < from_e; ++i, (void) ++pos, tx.pos_ = pos) {
1261 alloc_traits::construct(this->alloc_, std::to_address(pos), std::move(*i));
1262 }
1263 }
1264 std::move_backward(from_s, from_s + n, old_last);
1265 }
1266
1267 template <class T, class Allocator>
1268 constexpr typename vector<T, Allocator>::iterator
1269 vector<T, Allocator>::insert(const_iterator position, const_reference x) {
1270 pointer p = this->begin_ + (position - begin());
1271 if (this->end_ < this->cap_) {
1272 if (p == this->end_) {
1273 emplace_back_assume_capacity(x);
1274 } else {
1275 move_range(p, this->end_, p + 1);
1276 const_pointer xr = std::pointer_traits<const_pointer>::pointer_to(x);
1277 if (is_pointer_in_range(
1278 std::to_address(p),
1279 std::to_address(end_),
1280 std::addressof(x)
1281 )) {
1282 ++xr;
1283 }
1284 *p = *xr;
1285 }
1286 } else {
1287 split_buffer<value_type, allocator_type&> v(
1288 recommend(size() + 1),
1289 p - this->begin_,
1290 this->alloc_
1291 );
1292 v.emplace_back(x);
1293 p = swap_out_circular_buffer(v, p);
1294 }
1295 return make_iter(p);
1296 }
1297
1298 template <class T, class Allocator>
1299 constexpr typename vector<T, Allocator>::iterator
1300 vector<T, Allocator>::insert(const_iterator position, value_type&& x) {
1301 pointer p = this->begin_ + (position - begin());
1302 if (this->end_ < this->cap_) {
1303 if (p == this->end_) {
1304 emplace_back_assume_capacity(std::move(x));
1305 } else {
1306 move_range(p, this->end_, p + 1);
1307 *p = std::move(x);
1308 }
1309 } else {
1310 split_buffer<value_type, allocator_type&> v(
1311 recommend(size() + 1),
1312 p - this->begin_,
1313 this->alloc_
1314 );
1315 v.emplace_back(std::move(x));
1316 p = swap_out_circular_buffer(v, p);
1317 }
1318 return make_iter(p);
1319 }
1320
1321 template <class T, class Allocator>
1322 template <class... Args>
1323 constexpr typename vector<T, Allocator>::iterator
1324 vector<T, Allocator>::emplace(const_iterator position, Args&&... args) {
1325 pointer p = this->begin_ + (position - begin());
1326 if (this->end_ < this->cap_) {
1327 if (p == this->end_) {
1328 emplace_back_assume_capacity(std::forward<Args>(args)...);
1329 } else {
1330 detail::temp_value<value_type, Allocator> tmp(this->alloc_, std::forward<Args>(args)...);
1331 move_range(p, this->end_, p + 1);
1332 *p = std::move(tmp.get());
1333 }
1334 } else {
1335 split_buffer<value_type, allocator_type&> v(
1336 recommend(size() + 1),
1337 p - this->begin_,
1338 this->alloc_
1339 );
1340 v.emplace_back(std::forward<Args>(args)...);
1341 p = swap_out_circular_buffer(v, p);
1342 }
1343 return make_iter(p);
1344 }
1345
1346 template <class T, class Allocator>
1347 constexpr typename vector<T, Allocator>::iterator
1348 vector<T, Allocator>::insert(const_iterator position, size_type n, const_reference x) {
1349 pointer p = this->begin_ + (position - begin());
1350 if (n > 0) {
1351 if (n <= static_cast<size_type>(this->cap_ - this->end_)) {
1352 size_type old_n = n;
1353 pointer old_last = this->end_;
1354 if (n > static_cast<size_type>(this->end_ - p)) {
1355 size_type cx = n - (this->end_ - p);
1356 construct_at_end(cx, x);
1357 n -= cx;
1358 }
1359 if (n > 0) {
1360 move_range(p, old_last, p + old_n);
1361 const_pointer xr = std::pointer_traits<const_pointer>::pointer_to(x);
1362 if (is_pointer_in_range(
1363 std::to_address(p),
1364 std::to_address(end_),
1365 std::addressof(x)
1366 )) {
1367 xr += old_n;
1368 }
1369 std::fill_n(p, n, *xr);
1370 }
1371 } else {
1372 split_buffer<value_type, allocator_type&> v(
1373 recommend(size() + n),
1374 p - this->begin_,
1375 this->alloc_
1376 );
1377 v.construct_at_end(n, x);
1378 p = swap_out_circular_buffer(v, p);
1379 }
1380 }
1381 return make_iter(p);
1382 }
1383
1384 template <class T, class Allocator>
1385 template <class InputIterator, class Sentinel>
1386 constexpr typename vector<T, Allocator>::iterator
1387 vector<T, Allocator>::insert_with_sentinel(
1388 const_iterator position,
1389 InputIterator first,
1390 Sentinel last
1391 ) {
1392 difference_type off = position - begin();
1393 pointer p = this->begin_ + off;
1394 pointer old_last = this->end_;
1395 for (; this->end_ != this->cap_ && first != last; ++first) {
1396 emplace_back_assume_capacity(*first);
1397 }
1398
1399 if (first == last) {
1400 (void) std::rotate(p, old_last, this->end_);
1401 } else {
1402 split_buffer<value_type, allocator_type&> v(alloc_);
1403 auto guard = make_exception_guard(
1404 AllocatorDestroyRangeReverse<allocator_type, pointer>(alloc_, old_last, this->end_)
1405 );
1406 v.construct_at_end_with_sentinel(std::move(first), std::move(last));
1407 split_buffer<value_type, allocator_type&> merged(
1408 recommend(size() + v.size()),
1409 off,
1410 alloc_
1411 ); // has `off` positions available at the front
1412 uninitialized_allocator_relocate(
1413 alloc_,
1414 std::to_address(old_last),
1415 std::to_address(this->end_),
1416 std::to_address(merged.end())
1417 );
1418 guard.complete(); // Release the guard once objects in [old_last_, end_) have been
1419 // successfully relocated.
1420 merged.set_sentinel(merged.end() + (this->end_ - old_last));
1421 this->end_ = old_last;
1422 uninitialized_allocator_relocate(
1423 alloc_,
1424 std::to_address(v.begin()),
1425 std::to_address(v.end()),
1426 std::to_address(merged.end())
1427 );
1428 merged.set_sentinel(merged.size() + v.size());
1429 v.set_sentinel(v.begin());
1430 p = swap_out_circular_buffer(merged, p);
1431 }
1432 return make_iter(p);
1433 }
1434
1435 template <class T, class Allocator>
1436 template <class Iterator, class Sentinel>
1437 constexpr typename vector<T, Allocator>::iterator
1438 vector<T, Allocator>::insert_with_size(
1439 const_iterator position,
1440 Iterator first,
1441 Sentinel last,
1442 difference_type n
1443 ) {
1444 pointer p = this->begin_ + (position - begin());
1445 if (n > 0) {
1446 if (n <= this->cap_ - this->end_) {
1447 pointer old_last = this->end_;
1448 difference_type dx = this->end_ - p;
1449 if (n > dx) {
1450#if PLUGIFY_HAS_CXX23
1451 if constexpr (!std::forward_iterator<Iterator>) {
1452 construct_at_end(std::move(first), std::move(last), n);
1453 std::rotate(p, old_last, this->end_);
1454 } else
1455#endif
1456 {
1457 Iterator m = std::next(first, dx);
1458 construct_at_end(m, last, n - dx);
1459 if (dx > 0) {
1460 move_range(p, old_last, p + n);
1461 insert_assign_n_unchecked(first, dx, p);
1462 }
1463 }
1464 } else {
1465 move_range(p, old_last, p + n);
1466 insert_assign_n_unchecked(std::move(first), n, p);
1467 }
1468 } else {
1469 split_buffer<value_type, allocator_type&> v(
1470 recommend(size() + n),
1471 p - this->begin_,
1472 this->alloc_
1473 );
1474 v.construct_at_end_with_size(std::move(first), n);
1475 p = swap_out_circular_buffer(v, p);
1476 }
1477 }
1478 return make_iter(p);
1479 }
1480
1481 template <class T, class Allocator>
1482 constexpr void vector<T, Allocator>::resize(size_type sz) {
1483 size_type cs = size();
1484 if (cs < sz) {
1485 this->append(sz - cs);
1486 } else if (cs > sz) {
1487 this->destruct_at_end(this->begin_ + sz);
1488 }
1489 }
1490
1491 template <class T, class Allocator>
1492 constexpr void
1493 vector<T, Allocator>::resize(size_type sz, const_reference x) {
1494 size_type cs = size();
1495 if (cs < sz) {
1496 this->append(sz - cs, x);
1497 } else if (cs > sz) {
1498 this->destruct_at_end(this->begin_ + sz);
1499 }
1500 }
1501
1502 template <class T, class Allocator>
1503 constexpr void vector<T, Allocator>::swap(vector& x)
1504 noexcept
1505 {
1506 PLUGIFY_ASSERT(
1507 alloc_traits::propagate_on_container_swap::value || this->alloc_ == x.alloc_,
1508 "vector::swap: Either propagate_on_container_swap must be true"
1509 " or the allocators must compare equal"
1510 );
1511 std::swap(this->begin_, x.begin_);
1512 std::swap(this->end_, x.end_);
1513 std::swap(this->cap_, x.cap_);
1514 swap_allocator(this->alloc_, x.alloc_);
1515 }
1516
1517 template <class T, class Allocator>
1518 constexpr bool vector<T, Allocator>::invariants() const {
1519 if (this->begin_ == nullptr) {
1520 if (this->end_ != nullptr || this->cap_ != nullptr) {
1521 return false;
1522 }
1523 } else {
1524 if (this->begin_ > this->end_) {
1525 return false;
1526 }
1527 if (this->begin_ == this->cap_) {
1528 return false;
1529 }
1530 if (this->end_ > this->cap_) {
1531 return false;
1532 }
1533 }
1534 return true;
1535 }
1536
1537 // comparisons
1538 template<typename T, typename Allocator>
1539 constexpr bool operator==(const vector<T, Allocator>& lhs, const vector<T, Allocator>& rhs) {
1540 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1541 }
1542
1543 template<typename T, typename Allocator>
1544 constexpr auto operator<=>(const vector<T, Allocator>& lhs, const vector<T, Allocator>& rhs) {
1545 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1546 }
1547
1548 // global swap for vector
1549 template<typename T, typename Allocator>
1550 constexpr void swap(vector<T, Allocator>& lhs, vector<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
1551 lhs.swap(rhs);
1552 }
1553
1554 template<typename T, typename Allocator, typename U>
1555 constexpr vector<T, Allocator>::size_type erase(vector<T, Allocator>& c, const U& value) {
1556 auto it = std::remove(c.begin(), c.end(), value);
1557 auto r = std::distance(it, c.end());
1558 c.erase(it, c.end());
1559 return r;
1560 }
1561
1562 template<typename T, typename Allocator, typename Pred>
1563 constexpr vector<T, Allocator>::size_type erase_if(vector<T, Allocator>& c, Pred pred) {
1564 auto it = std::remove_if(c.begin(), c.end(), pred);
1565 auto r = std::distance(it, c.end());
1566 c.erase(it, c.end());
1567 return r;
1568 }
1569
1570 namespace pmr {
1571 template<typename T>
1573 } // namespace pmr
1574} // namespace plg