Sleipnir C++ API
Loading...
Searching...
No Matches
small_vector.hpp
Go to the documentation of this file.
1
13#pragma once
14
15#include <stdint.h>
16
17#include <algorithm>
18#include <cassert>
19#include <compare>
20#include <concepts>
21#include <cstddef>
22#include <cstring>
23#include <initializer_list>
24#include <iterator>
25#include <limits>
26#include <memory>
27#include <new>
28#include <stdexcept>
29#include <type_traits>
30#include <utility>
31
32namespace sleipnir {
33
34namespace concepts {
35
36template <typename T>
37concept Complete = requires { sizeof(T); };
38
39// Note: this mirrors the named requirements, not the standard concepts, so we
40// don't require the destructor to be noexcept for Destructible.
41template <typename T>
42concept Destructible = std::is_destructible_v<T>;
43
44template <typename T>
45concept TriviallyDestructible = std::is_trivially_destructible_v<T>;
46
47template <typename T>
48concept NoThrowDestructible = std::is_nothrow_destructible_v<T>;
49
50// Note: this mirrors the named requirements, not the standard library concepts,
51// so we don't require Destructible here.
52
53template <typename T, typename... Args>
54concept ConstructibleFrom = std::is_constructible_v<T, Args...>;
55
56template <typename T, typename... Args>
57concept NoThrowConstructibleFrom = std::is_nothrow_constructible_v<T, Args...>;
58
59template <typename From, typename To>
61 std::is_convertible_v<From, To> &&
62 requires(typename std::add_rvalue_reference_t<From> (&f)()) {
63 static_cast<To>(f());
64 };
65
66template <typename From, typename To>
68 std::is_nothrow_convertible_v<From, To> &&
69 requires(typename std::add_rvalue_reference_t<From> (&f)() noexcept) {
70 { static_cast<To>(f()) } noexcept;
71 };
72
73// Note: std::default_initializable requires std::destructible.
74template <typename T>
76 T{};
77} && requires { ::new (static_cast<void*>(nullptr)) T; };
78
81
84 MoveAssignable<T> && std::assignable_from<T&, T&> &&
85 std::assignable_from<T&, const T&> && std::assignable_from<T&, const T>;
86
89
93
96 MoveConstructible<T> && ConstructibleFrom<T, T&> && ConvertibleTo<T&, T> &&
97 ConstructibleFrom<T, const T&> && ConvertibleTo<const T&, T> &&
99
106
109
112
113// T is a type
114// X is a Container
115// A is an Allocator
116// if X::allocator_type then
117// std::same_as<typename X::allocator_type,
118// typename std::allocator_traits<A>::template rebind_alloc<T>>
119// otherwise
120// no condition; we use std::allocator<T> regardless of A
121//
122// see [22.2.1].16
125 std::same_as<typename X::value_type, T> &&
126 // only perform this check if X is allocator-aware
127 (
128 (
129 requires { typename X::allocator_type; } &&
130 std::same_as<typename X::allocator_type,
131 typename std::allocator_traits<A>::template rebind_alloc<T>> &&
132 (
133 requires (A m, T* p, Args&&... args) {
134 m.construct(p, std::forward<Args>(args)...);
135 } ||
136 requires (T* p, Args&&... args) {
137 { std::construct_at(p, std::forward<Args>(args)...) } -> std::same_as<T*>;
138 }
139 )
140 ) ||
141 (
142 !requires { typename X::allocator_type; } &&
143 requires (T* p, Args&&... args) {
144 { std::construct_at(p, std::forward<Args> (args)...) } -> std::same_as<T*>;
145 }
146 )
147 );
148
149template <typename T, typename X,
150 typename A = typename std::conditional_t<requires {
151 typename X::allocator_type;
152 }, typename X::allocator_type, std::allocator<T>>>
154
155template <typename T, typename X,
156 typename A = typename std::conditional_t<requires {
157 typename X::allocator_type;
158 }, typename X::allocator_type, std::allocator<T>>>
160
161template <typename T, typename X,
162 typename A = typename std::conditional_t<requires {
163 typename X::allocator_type;
164 }, typename X::allocator_type, std::allocator<T>>>
168
169// same method as with EmplaceConstructible
170template <typename T, typename X,
171 typename A = typename std::conditional_t<requires {
172 typename X::allocator_type;
173 }, typename X::allocator_type, std::allocator<T>>>
174concept Erasable =
175 std::same_as<typename X::value_type, T> &&
176 ((requires { typename X::allocator_type; } // if X is allocator aware
177 && std::same_as<
178 typename X::allocator_type,
179 typename std::allocator_traits<A>::template rebind_alloc<T>> &&
180 (requires(A m, T* p) { m.destroy(p); } || std::is_destructible_v<T>)) ||
181 (!requires { typename X::allocator_type; } && std::is_destructible_v<T>));
182
183template <typename T>
184concept ContextuallyConvertibleToBool = std::constructible_from<bool, T>;
185
186template <typename T>
187concept BoolConstant = std::derived_from<T, std::true_type> ||
188 std::derived_from<T, std::false_type>;
189
190template <typename T>
204
205static_assert(NullablePointer<int*>);
206static_assert(!NullablePointer<int>);
207
208template <typename A, typename T, typename U = T*>
211 requires(A a, typename std::allocator_traits<A>::template rebind_alloc<U> b,
212 U xp, typename std::allocator_traits<A>::pointer p,
213 typename std::allocator_traits<A>::const_pointer cp,
214 typename std::allocator_traits<A>::void_pointer vp,
215 typename std::allocator_traits<A>::const_void_pointer cvp,
216 typename std::allocator_traits<A>::value_type& r,
217 typename std::allocator_traits<A>::size_type n) {
219 // A::pointer
221 requires std::random_access_iterator<
222 typename std::allocator_traits<A>::pointer>;
223 requires std::contiguous_iterator<
224 typename std::allocator_traits<A>::pointer>;
225
226 // A::const_pointer
227 requires NullablePointer<
228 typename std::allocator_traits<A>::const_pointer>;
229 requires std::random_access_iterator<
230 typename std::allocator_traits<A>::const_pointer>;
231 requires std::contiguous_iterator<
232 typename std::allocator_traits<A>::const_pointer>;
233
234 requires std::convertible_to<
235 typename std::allocator_traits<A>::pointer,
236 typename std::allocator_traits<A>::const_pointer>;
237
238 // A::void_pointer
240
241 requires std::convertible_to<
242 typename std::allocator_traits<A>::pointer,
243 typename std::allocator_traits<A>::void_pointer>;
244
245 requires std::same_as<
246 typename std::allocator_traits<A>::void_pointer,
247 typename std::allocator_traits<decltype(b)>::void_pointer>;
248
249 // A::const_void_pointer
250 requires NullablePointer<
251 typename std::allocator_traits<A>::const_void_pointer>;
252
253 requires std::convertible_to<
254 typename std::allocator_traits<A>::pointer,
255 typename std::allocator_traits<A>::const_void_pointer>;
256
257 requires std::convertible_to<
258 typename std::allocator_traits<A>::const_pointer,
259 typename std::allocator_traits<A>::const_void_pointer>;
260
261 requires std::convertible_to<
262 typename std::allocator_traits<A>::void_pointer,
263 typename std::allocator_traits<A>::const_void_pointer>;
264
265 requires std::same_as<
266 typename std::allocator_traits<A>::const_void_pointer,
267 typename std::allocator_traits<decltype(b)>::const_void_pointer>;
268
269 // A::value_type
270 typename A::value_type;
271 requires std::same_as<typename A::value_type, T>;
272 requires std::same_as<typename A::value_type,
273 typename std::allocator_traits<A>::value_type>;
274
275 // A::size_type
276 requires std::unsigned_integral<
277 typename std::allocator_traits<A>::size_type>;
278
279 // A::difference_type
280 requires std::signed_integral<
281 typename std::allocator_traits<A>::difference_type>;
282
283 // A::template rebind<U>::other [optional]
284 requires !requires {
285 typename A::template rebind<U>::other;
286 } || requires {
287 requires std::same_as<decltype(b),
288 typename A::template rebind<U>::other>;
289 requires std::same_as<A,
290 typename decltype(b)::template rebind<T>::other>;
291 };
292
294 { *p } -> std::same_as<typename A::value_type&>;
295 { *cp } -> std::same_as<const typename A::value_type&>;
296
297 // Language in the standard implies that `decltype (p)` must either
298 // be a raw pointer or implement `operator->`. There is no mention
299 // of `std::to_address` or `std::pointer_traits<Ptr>::to_address`.
300 requires std::same_as<decltype(p), typename A::value_type*> || requires {
301 { p.operator->() } -> std::same_as<typename A::value_type*>;
302 };
303
304 requires std::same_as<decltype(cp), const typename A::value_type*> ||
305 requires {
306 {
307 cp.operator->()
308 } -> std::same_as<const typename A::value_type*>;
309 };
310
311 { static_cast<decltype(p)>(vp) } -> std::same_as<decltype(p)>;
312 { static_cast<decltype(cp)>(cvp) } -> std::same_as<decltype(cp)>;
313
314 {
315 std::pointer_traits<decltype(p)>::pointer_to(r)
316 } -> std::same_as<decltype(p)>;
317
319 // a.allocate (n)
320 { a.allocate(n) } -> std::same_as<decltype(p)>;
321
322 // a.allocate (n, cvp) [optional]
323 requires !requires { a.allocate(n, cvp); } || requires {
324 { a.allocate(n, cvp) } -> std::same_as<decltype(p)>;
325 };
326
327 // a.deallocate (p, n)
328 { a.deallocate(p, n) } -> std::convertible_to<void>;
329
330 // a.max_size () [optional]
331 requires !requires { a.max_size(); } || requires {
332 { a.max_size() } -> std::same_as<decltype(n)>;
333 };
334
335 // a.construct (xp, args) [optional]
336 requires !requires { a.construct(xp); } || requires {
337 { a.construct(xp) } -> std::convertible_to<void>;
338 };
339
340 // a.destroy (xp) [optional]
341 requires !requires { a.destroy(xp); } || requires {
342 { a.destroy(xp) } -> std::convertible_to<void>;
343 };
344
346 requires NoThrowConstructibleFrom<A, decltype(b)>;
347 requires NoThrowConstructibleFrom<A, decltype(std::move(b))>;
348
350
352 // a.select_on_container_copy_construction () [optional]
353 requires !requires { a.select_on_container_copy_construction(); } ||
354 requires {
355 {
356 a.select_on_container_copy_construction()
357 } -> std::same_as<A>;
358 };
359
360 requires BoolConstant<typename std::allocator_traits<
362
363 requires BoolConstant<typename std::allocator_traits<
365
366 requires BoolConstant<
367 typename std::allocator_traits<A>::propagate_on_container_swap>;
368
369 { a == b } -> std::same_as<bool>;
370 { a != b } -> std::same_as<bool>;
371 } &&
372 requires(A a1, A a2) {
373 { a1 == a2 } -> std::same_as<bool>;
374 { a1 != a2 } -> std::same_as<bool>;
375 };
376
377static_assert(
379 "std::allocator<int> failed to meet Allocator concept requirements.");
380
381template <typename A>
383
384namespace small_vector {
385
386// Basically, these shut off the concepts if we have an incomplete type.
387// This namespace is only needed because of issues on Clang
388// preventing us from short-circuiting for incomplete types.
389
390template <typename T>
392
393template <typename T>
395
396template <typename T>
398
399template <typename T>
402
403template <typename T>
406
407template <typename T>
409
410template <typename T, typename SmallVector, typename Alloc>
413
414template <typename T, typename SmallVector, typename Alloc>
417
418template <typename T, typename SmallVector, typename Alloc>
421
422template <typename T, typename SmallVector, typename Alloc>
423concept Erasable =
425
426template <typename T, typename SmallVector, typename Alloc, typename... Args>
430
431template <typename Alloc, typename T>
434
435template <typename Alloc>
437
438} // namespace small_vector
439
440} // namespace concepts
441
442template <typename Allocator>
445
446template <typename T,
447 unsigned InlineCapacity =
449 typename Allocator = std::allocator<T>>
451class small_vector;
452
453template <typename Allocator>
456 private:
457 template <typename, typename Enable = void>
458 struct is_complete : std::false_type {};
459
460 template <typename U>
461 struct is_complete<U, decltype(static_cast<void>(sizeof(U)))>
462 : std::true_type {};
463
464 template <typename U>
465 inline static constexpr bool is_complete_v = is_complete<U>::value;
466
467 public:
468 using allocator_type = Allocator;
469 using value_type = typename std::allocator_traits<allocator_type>::value_type;
471
472 static_assert(is_complete_v<value_type>,
473 "Calculation of a default number of elements requires that `T` "
474 "be complete.");
475
476 static constexpr unsigned buffer_max = 256;
477
478 static constexpr unsigned ideal_total = 64;
479
480 static constexpr unsigned ideal_buffer =
481 ideal_total - sizeof(empty_small_vector);
482
483 static_assert(sizeof(empty_small_vector) != 0,
484 "Empty `small_vector` should not have size 0.");
485
486 static_assert(ideal_buffer < ideal_total,
487 "Empty `small_vector` is larger than ideal_total.");
488
489 static constexpr unsigned value = (sizeof(value_type) <= ideal_buffer)
490 ? (ideal_buffer / sizeof(value_type))
491 : 1;
492};
493
494template <typename Allocator>
495inline constexpr unsigned default_buffer_size_v =
497
498template <typename Pointer, typename DifferenceType>
500 public:
501 using difference_type = DifferenceType;
502 using value_type = typename std::iterator_traits<Pointer>::value_type;
503 using pointer = typename std::iterator_traits<Pointer>::pointer;
504 using reference = typename std::iterator_traits<Pointer>::reference;
506 typename std::iterator_traits<Pointer>::iterator_category;
507 using iterator_concept = std::contiguous_iterator_tag;
508
511 small_vector_iterator& operator=(const small_vector_iterator&) = default;
512 small_vector_iterator& operator=(small_vector_iterator&&) noexcept = default;
514
515#ifdef NDEBUG
516 small_vector_iterator() = default;
517#else
518 constexpr small_vector_iterator() noexcept : m_ptr() {}
519#endif
520
521 constexpr explicit small_vector_iterator(const Pointer& p) noexcept
522 : m_ptr(p) {}
523
524 template <typename U, typename D>
525 requires std::is_convertible_v<U, Pointer>
526 constexpr small_vector_iterator( // NOLINT
527 const small_vector_iterator<U, D>& other) noexcept
528 : m_ptr(other.base()) {}
529
530 constexpr small_vector_iterator& operator++() noexcept {
531 ++m_ptr;
532 return *this;
533 }
534
535 constexpr small_vector_iterator operator++(int) noexcept {
536 return small_vector_iterator(m_ptr++);
537 }
538
539 constexpr small_vector_iterator& operator--() noexcept {
540 --m_ptr;
541 return *this;
542 }
543
544 constexpr small_vector_iterator operator--(int) noexcept {
545 return small_vector_iterator(m_ptr--);
546 }
547
549 m_ptr += n;
550 return *this;
551 }
552
553 constexpr small_vector_iterator operator+(difference_type n) const noexcept {
554 return small_vector_iterator(m_ptr + n);
555 }
556
558 m_ptr -= n;
559 return *this;
560 }
561
562 constexpr small_vector_iterator operator-(difference_type n) const noexcept {
563 return small_vector_iterator(m_ptr - n);
564 }
565
566 constexpr reference operator*() const noexcept {
567 return launder_and_dereference(m_ptr);
568 }
569
570 constexpr pointer operator->() const noexcept { return get_pointer(m_ptr); }
571
572 constexpr reference operator[](difference_type n) const noexcept {
573 return launder_and_dereference(m_ptr + n);
574 }
575
576 constexpr const Pointer& base() const noexcept { return m_ptr; }
577
578 private:
579 static constexpr pointer get_pointer(Pointer ptr) noexcept
580 requires std::is_pointer_v<Pointer>
581 {
582 return ptr;
583 }
584
585 static constexpr pointer get_pointer(Pointer ptr) noexcept
586 requires(!std::is_pointer_v<Pointer>)
587 {
588 // Given the requirements for Allocator, Pointer must either be a raw
589 // pointer, or have a defined operator-> which returns a raw pointer.
590 return ptr.operator->();
591 }
592
593 static constexpr reference launder_and_dereference(Pointer ptr) noexcept
594 requires std::is_pointer_v<Pointer>
595 {
596 return *std::launder(ptr);
597 }
598
599 static constexpr reference launder_and_dereference(Pointer ptr) noexcept
600 requires(!std::is_pointer_v<Pointer>)
601 {
602 return *ptr;
603 }
604
605 Pointer m_ptr;
606};
607
608template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS,
609 typename DifferenceTypeRHS>
610constexpr bool operator==(
613 rhs) noexcept(noexcept(lhs.base() == rhs.base()))
614 requires requires {
615 { lhs.base() == rhs.base() } -> std::convertible_to<bool>;
616 }
617{
618 return lhs.base() == rhs.base();
619}
620
621template <typename Pointer, typename DifferenceType>
622constexpr bool operator==(
625 rhs) noexcept(noexcept(lhs.base() == rhs.base()))
626 requires requires {
627 { lhs.base() == rhs.base() } -> std::convertible_to<bool>;
628 }
629{
630 return lhs.base() == rhs.base();
631}
632
633template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS,
634 typename DifferenceTypeRHS>
635 requires std::three_way_comparable_with<PointerLHS, PointerRHS>
636constexpr auto operator<=>(
639 rhs) noexcept(noexcept(lhs.base() <=> rhs.base())) {
640 return lhs.base() <=> rhs.base();
641}
642
643template <typename Pointer, typename DifferenceType>
644 requires std::three_way_comparable<Pointer>
645constexpr auto operator<=>(
648 rhs) noexcept(noexcept(lhs.base() <=> rhs.base())) {
649 return lhs.base() <=> rhs.base();
650}
651
652template <typename PointerLHS, typename DifferenceTypeLHS, typename PointerRHS,
653 typename DifferenceTypeRHS>
654constexpr auto operator<=>(
657 rhs) noexcept(noexcept(lhs.base() < rhs.base()) &&
658 noexcept(rhs.base() < lhs.base())) {
659 using ordering = std::weak_ordering;
660 return (lhs.base() < rhs.base()) ? ordering::less
661 : (rhs.base() < lhs.base()) ? ordering::greater
662 : ordering::equivalent;
663}
664
665template <typename Pointer, typename DifferenceType>
666constexpr auto operator<=>(
669 rhs) noexcept(noexcept(lhs.base() < rhs.base()) &&
670 noexcept(rhs.base() < lhs.base())) {
671 using ordering = std::weak_ordering;
672 return (lhs.base() < rhs.base()) ? ordering::less
673 : (rhs.base() < lhs.base()) ? ordering::greater
674 : ordering::equivalent;
675}
676
677template <typename PointerLHS, typename PointerRHS, typename DifferenceType>
681 return static_cast<DifferenceType>(lhs.base() - rhs.base());
682}
683
684template <typename Pointer, typename DifferenceType>
688 return static_cast<DifferenceType>(lhs.base() - rhs.base());
689}
690
691template <typename Pointer, typename DifferenceType>
697
698namespace detail {
699
700template <typename T, unsigned InlineCapacity>
701class inline_storage {
702 public:
703 using value_ty = T;
704
705 inline_storage() = default;
706 inline_storage(const inline_storage&) = delete;
707 inline_storage(inline_storage&&) noexcept = delete;
708 inline_storage& operator=(const inline_storage&) = delete;
709 inline_storage& operator=(inline_storage&&) noexcept = delete;
710 ~inline_storage() = default;
711
712 [[nodiscard]]
713 constexpr value_ty* get_inline_ptr() noexcept {
714 return static_cast<value_ty*>(static_cast<void*>(std::addressof(*m_data)));
715 }
716
717 [[nodiscard]]
718 constexpr const value_ty* get_inline_ptr() const noexcept {
719 return static_cast<const value_ty*>(
720 static_cast<const void*>(std::addressof(*m_data)));
721 }
722
723 static constexpr size_t element_size() noexcept { return sizeof(value_ty); }
724
725 static constexpr size_t alignment() noexcept { return alignof(value_ty); }
726
727 static constexpr unsigned num_elements() noexcept { return InlineCapacity; }
728
729 static constexpr size_t num_bytes() noexcept {
730 return num_elements() * element_size();
731 }
732
733 private:
734 alignas(alignment()) std::byte m_data[element_size()][num_elements()];
735};
736
737template <typename T>
738class inline_storage<T, 0> {
739 public:
740 using value_ty = T;
741
742 inline_storage() = default;
743 inline_storage(const inline_storage&) = delete;
744 inline_storage(inline_storage&&) noexcept = delete;
745 inline_storage& operator=(const inline_storage&) = delete;
746 inline_storage& operator=(inline_storage&&) noexcept = delete;
747 ~inline_storage() = default;
748
749 [[nodiscard]]
750 constexpr value_ty* get_inline_ptr() noexcept {
751 return nullptr;
752 }
753
754 [[nodiscard]]
755 constexpr const value_ty* get_inline_ptr() const noexcept {
756 return nullptr;
757 }
758
759 static constexpr size_t element_size() noexcept { return sizeof(value_ty); }
760
761 static constexpr size_t alignment() noexcept { return alignof(value_ty); }
762
763 static constexpr unsigned num_elements() noexcept { return 0; }
764
765 static constexpr size_t num_bytes() noexcept { return 0; }
766};
767
768template <typename Allocator,
769 bool AvailableForEBO =
770 std::is_empty_v<Allocator> && !std::is_final_v<Allocator>>
771class allocator_inliner;
772
773template <typename Allocator>
774class allocator_inliner<Allocator, true> : private Allocator {
775 using alloc_traits = std::allocator_traits<Allocator>;
776
777 static constexpr bool copy_assign_is_noop =
778 !alloc_traits::propagate_on_container_copy_assignment::value;
779
780 static constexpr bool move_assign_is_noop =
781 !alloc_traits::propagate_on_container_move_assignment::value;
782
783 static constexpr bool swap_is_noop =
784 !alloc_traits::propagate_on_container_swap::value;
785
786 template <bool IsNoOp = copy_assign_is_noop>
787 requires IsNoOp
788 constexpr void maybe_assign(const allocator_inliner&) noexcept {}
789
790 template <bool IsNoOp = copy_assign_is_noop>
791 requires(!IsNoOp)
792 constexpr void maybe_assign(const allocator_inliner& other) noexcept(
793 noexcept(std::declval<Allocator&>().operator=(other))) {
794 Allocator::operator=(other);
795 }
796
797 template <bool IsNoOp = move_assign_is_noop>
798 requires IsNoOp
799 constexpr void maybe_assign(allocator_inliner&&) noexcept {}
800
801 template <bool IsNoOp = move_assign_is_noop>
802 requires(!IsNoOp)
803 constexpr void maybe_assign(allocator_inliner&& other) noexcept(
804 noexcept(std::declval<Allocator&>().operator=(std::move(other)))) {
805 Allocator::operator=(std::move(other));
806 }
807
808 public:
809 allocator_inliner() = default;
810 allocator_inliner(const allocator_inliner&) = default;
811 allocator_inliner(allocator_inliner&&) noexcept = default;
812 ~allocator_inliner() = default;
813
814 constexpr explicit allocator_inliner(const Allocator& alloc) noexcept
815 : Allocator(alloc) {}
816
817 constexpr allocator_inliner&
818 operator=(const allocator_inliner& other) noexcept(
819 noexcept(std::declval<allocator_inliner&>().maybe_assign(other))) {
820 assert(
821 &other != this &&
822 "`allocator_inliner` should not participate in self-copy-assignment.");
823 maybe_assign(other);
824 return *this;
825 }
826
827 constexpr allocator_inliner& operator=(allocator_inliner&& other) noexcept(
828 noexcept(
829 std::declval<allocator_inliner&>().maybe_assign(std::move(other)))) {
830 assert(
831 &other != this &&
832 "`allocator_inliner` should not participate in self-move-assignment.");
833 maybe_assign(std::move(other));
834 return *this;
835 }
836
837 constexpr Allocator& allocator_ref() noexcept { return *this; }
838
839 constexpr const Allocator& allocator_ref() const noexcept { return *this; }
840
841 template <bool IsNoOp = swap_is_noop>
842 requires IsNoOp
843 constexpr void swap(allocator_inliner&) {}
844
845 template <bool IsNoOp = swap_is_noop>
846 requires(!IsNoOp)
847 constexpr void swap(allocator_inliner& other) {
848 using std::swap;
849 swap(static_cast<Allocator&>(*this), static_cast<Allocator&>(other));
850 }
851};
852
853template <typename Allocator>
854class allocator_inliner<Allocator, false> {
855 using alloc_traits = std::allocator_traits<Allocator>;
856
857 static constexpr bool copy_assign_is_noop =
858 !alloc_traits::propagate_on_container_copy_assignment::value;
859
860 static constexpr bool move_assign_is_noop =
861 !alloc_traits::propagate_on_container_move_assignment::value;
862
863 static constexpr bool swap_is_noop =
864 !alloc_traits::propagate_on_container_swap::value;
865
866 template <bool IsNoOp = copy_assign_is_noop>
867 requires IsNoOp
868 constexpr void maybe_assign(const allocator_inliner&) noexcept {}
869
870 template <bool IsNoOp = copy_assign_is_noop>
871 requires(!IsNoOp)
872 constexpr void maybe_assign(const allocator_inliner& other) noexcept(
873 noexcept(std::declval<decltype(other.m_alloc)&>() = other.m_alloc)) {
874 m_alloc = other.m_alloc;
875 }
876
877 template <bool IsNoOp = move_assign_is_noop>
878 requires IsNoOp
879 constexpr void maybe_assign(allocator_inliner&&) noexcept {}
880
881 template <bool IsNoOp = move_assign_is_noop>
882 requires(!IsNoOp)
883 constexpr void maybe_assign(allocator_inliner&& other) noexcept(noexcept(
884 std::declval<decltype(other.m_alloc)&>() = std::move(other.m_alloc))) {
885 m_alloc = std::move(other.m_alloc);
886 }
887
888 public:
889 allocator_inliner() = default;
890 allocator_inliner(const allocator_inliner&) = default;
891 allocator_inliner(allocator_inliner&&) noexcept = default;
892 ~allocator_inliner() = default;
893
894 constexpr explicit allocator_inliner(const Allocator& alloc) noexcept
895 : m_alloc(alloc) {}
896
897 constexpr allocator_inliner&
898 operator=(const allocator_inliner& other) noexcept(
899 noexcept(std::declval<allocator_inliner&>().maybe_assign(other))) {
900 assert(
901 &other != this &&
902 "`allocator_inliner` should not participate in self-copy-assignment.");
903 maybe_assign(other);
904 return *this;
905 }
906
907 constexpr allocator_inliner& operator=(allocator_inliner&& other) noexcept(
908 noexcept(
909 std::declval<allocator_inliner&>().maybe_assign(std::move(other)))) {
910 assert(
911 &other != this &&
912 "`allocator_inliner` should not participate in self-move-assignment.");
913 maybe_assign(std::move(other));
914 return *this;
915 }
916
917 constexpr Allocator& allocator_ref() noexcept { return m_alloc; }
918
919 constexpr const Allocator& allocator_ref() const noexcept { return m_alloc; }
920
921 template <bool IsNoOp = swap_is_noop>
922 requires IsNoOp
923 constexpr void swap(allocator_inliner&) {}
924
925 template <bool IsNoOp = swap_is_noop>
926 requires(!IsNoOp)
927 constexpr void swap(allocator_inliner& other) {
928 using std::swap;
929 swap(m_alloc, other.m_alloc);
930 }
931
932 private:
933 Allocator m_alloc;
934};
935
936template <typename Allocator>
937class allocator_interface : public allocator_inliner<Allocator> {
938 public:
939 template <typename U>
940 inline static constexpr bool is_complete_v = requires { sizeof(U); };
941
942 using size_type = typename std::allocator_traits<Allocator>::size_type;
943
944 // If difference_type is larger than size_type then we need
945 // to rectify that problem.
946 using difference_type = typename std::conditional_t<
947 (static_cast<size_t>(
948 (std::numeric_limits<size_type>::max)()) < // less-than
949 static_cast<size_t>((std::numeric_limits<typename std::allocator_traits<
950 Allocator>::difference_type>::max)())),
951 typename std::make_signed_t<size_type>,
952 typename std::allocator_traits<Allocator>::difference_type>;
953
954 private:
955 using alloc_base = allocator_inliner<Allocator>;
956
957 protected:
958 using alloc_ty = Allocator;
959 using alloc_traits = std::allocator_traits<alloc_ty>;
960 using value_ty = typename alloc_traits::value_type;
961 using ptr = typename alloc_traits::pointer;
962 using cptr = typename alloc_traits::const_pointer;
963 using vptr = typename alloc_traits::void_pointer;
964 using cvptr = typename alloc_traits::const_void_pointer;
965
966 // Select the fastest types larger than the user-facing types. These are only
967 // intended for internal computations, and should not have any memory
968 // footprint visible to consumers.
969 using size_ty = typename std::conditional_t<
970 (sizeof(size_type) <= sizeof(uint8_t)), uint_fast8_t,
971 typename std::conditional_t<
972 (sizeof(size_type) <= sizeof(uint16_t)), uint_fast16_t,
973 typename std::conditional_t<
974 (sizeof(size_type) <= sizeof(uint32_t)), uint_fast32_t,
975 typename std::conditional_t<(sizeof(size_type) <=
976 sizeof(uint64_t)),
977 uint_fast64_t, size_type>>>>;
978
979 using diff_ty = typename std::conditional_t<
980 (sizeof(difference_type) <= sizeof(int8_t)), int_fast8_t,
981 typename std::conditional_t<
982 (sizeof(difference_type) <= sizeof(int16_t)), int_fast16_t,
983 typename std::conditional_t<
984 (sizeof(difference_type) <= sizeof(int32_t)), int_fast32_t,
985 typename std::conditional_t<(sizeof(difference_type) <=
986 sizeof(int64_t)),
987 int_fast64_t, difference_type>>>>;
988
989 using alloc_base::allocator_ref;
990
991 private:
992 template <typename T>
993 struct underlying_if_enum {
994 using type = T;
995 };
996
997 template <typename T>
998 requires std::is_enum_v<T>
999 struct underlying_if_enum<T> : std::underlying_type<T> {};
1000
1001 template <typename T>
1002 using underlying_if_enum_t = typename underlying_if_enum<T>::type;
1003
1004 template <typename P>
1005 inline static constexpr bool has_ptr_traits_to_address_v =
1006 requires { std::pointer_traits<P>::to_address(std::declval<P>()); };
1007
1008 template <typename A, typename V, typename... Args>
1009 inline static constexpr bool has_alloc_construct_v =
1010 is_complete_v<V> && requires {
1011 std::declval<A&>().construct(std::declval<V*>(),
1012 std::declval<Args>()...);
1013 };
1014
1015 template <typename A, typename V, typename... Args>
1016 inline static constexpr bool must_use_alloc_construct_v =
1017 !std::is_same_v<A, std::allocator<V>> &&
1018 has_alloc_construct_v<A, V, Args...>;
1019
1020 template <typename A, typename V>
1021 inline static constexpr bool has_alloc_destroy_v =
1022 is_complete_v<V> &&
1023 requires { std::declval<A&>().destroy(std::declval<V*>()); };
1024
1025 template <typename A, typename V>
1026 inline static constexpr bool must_use_alloc_destroy_v =
1027 !std::is_same_v<A, std::allocator<V>> && has_alloc_destroy_v<A, V>;
1028
1029 public:
1030 allocator_interface() = default;
1031 allocator_interface(allocator_interface&&) noexcept = default;
1032
1033 constexpr allocator_interface& operator=(const allocator_interface&) =
1034 default;
1035
1036 constexpr allocator_interface& operator=(allocator_interface&&) noexcept =
1037 default;
1038
1039 ~allocator_interface() = default;
1040
1041 constexpr allocator_interface(const allocator_interface& other) noexcept
1042 : alloc_base(alloc_traits::select_on_container_copy_construction(
1043 other.allocator_ref())) {}
1044
1045 constexpr explicit allocator_interface(const alloc_ty& alloc) noexcept
1046 : alloc_base(alloc) {}
1047
1048 template <typename T>
1049 constexpr explicit allocator_interface(T&&, const alloc_ty& alloc) noexcept
1050 : allocator_interface(alloc) {}
1051
1052 template <typename From, typename To>
1053 inline static constexpr bool is_memcpyable_integral_v =
1054 is_complete_v<From> &&
1055 (sizeof(underlying_if_enum_t<From>) ==
1056 sizeof(underlying_if_enum_t<To>)) &&
1057 (std::is_same_v<bool, underlying_if_enum_t<From>> ==
1058 std::is_same_v<bool, underlying_if_enum_t<To>>) &&
1059 std::is_integral_v<underlying_if_enum_t<From>> &&
1060 std::is_integral_v<underlying_if_enum_t<To>>;
1061
1062 template <typename From, typename To>
1063 inline static constexpr bool is_convertible_pointer_v =
1064 std::is_pointer_v<From> && std::is_pointer_v<To> &&
1065 std::is_convertible_v<From, To>;
1066
1067 // Memcpyable assignment.
1068 template <typename QualifiedFrom, typename QualifiedTo = value_ty>
1069 inline static constexpr bool is_memcpyable_v =
1070 is_complete_v<QualifiedFrom> && !std::is_reference_v<QualifiedTo> &&
1071 std::is_trivially_assignable_v<QualifiedTo&, QualifiedFrom> &&
1072 std::is_trivially_copyable_v<std::remove_cv_t<QualifiedTo>> &&
1073 (std::is_same_v<typename std::remove_cv_t<std::remove_reference_t<
1074 std::remove_cv_t<QualifiedFrom>>>,
1075 std::remove_cv_t<QualifiedTo>> ||
1076 is_memcpyable_integral_v<
1077 std::remove_reference_t<std::remove_cv_t<QualifiedFrom>>,
1078 std::remove_cv_t<QualifiedTo>> ||
1079 is_convertible_pointer_v<
1080 std::remove_reference_t<std::remove_cv_t<QualifiedFrom>>,
1081 std::remove_cv_t<QualifiedTo>>);
1082
1083 // Memcpyable construction.
1084 template <typename To, typename From>
1085 inline static constexpr bool is_uninitialized_memcpyable_v =
1086 !std::is_reference_v<To> && std::is_trivially_constructible_v<To, From> &&
1087 std::is_trivially_copyable_v<std::remove_cv_t<To>> &&
1088 (std::is_same_v<
1089 std::remove_cv_t<std::remove_reference_t<std::remove_cv_t<From>>>,
1090 std::remove_cv_t<To>> ||
1091 is_memcpyable_integral_v<std::remove_reference_t<std::remove_cv_t<From>>,
1092 std::remove_cv_t<To>> ||
1093 is_convertible_pointer_v<std::remove_reference_t<std::remove_cv_t<From>>,
1094 std::remove_cv_t<To>>) &&
1095 (!must_use_alloc_construct_v<
1096 alloc_ty, value_ty,
1097 std::remove_reference_t<std::remove_cv_t<From>>> &&
1098 !must_use_alloc_destroy_v<alloc_ty, value_ty>);
1099
1100 template <typename Iterator>
1101 struct is_small_vector_iterator : std::false_type {};
1102
1103 template <typename... Ts>
1105 : std::true_type {};
1106
1107 template <typename... Ts>
1108 inline static constexpr bool is_small_vector_iterator_v =
1109 is_small_vector_iterator<Ts...>::value;
1110
1111 template <typename InputIt>
1112 inline static constexpr bool is_contiguous_iterator_v =
1113 std::is_same_v<InputIt, ptr> || std::is_same_v<InputIt, cptr> ||
1114 is_small_vector_iterator_v<InputIt> || std::contiguous_iterator<InputIt>;
1115
1116 template <typename InputIt>
1118 inline static constexpr bool value =
1119 is_memcpyable_v<decltype(*std::declval<InputIt>())> &&
1120 is_contiguous_iterator_v<InputIt>;
1121 };
1122
1123 // Unwrap move_iterators
1124 template <typename InputIt>
1125 struct is_memcpyable_iterator<std::move_iterator<InputIt>>
1126 : is_memcpyable_iterator<InputIt> {};
1127
1128 template <typename InputIt>
1129 inline static constexpr bool is_memcpyable_iterator_v =
1131
1132 template <typename InputIt, typename V = value_ty>
1134 inline static constexpr bool value =
1135 is_uninitialized_memcpyable_v<V, decltype(*std::declval<InputIt>())> &&
1136 is_contiguous_iterator_v<InputIt>;
1137 };
1138
1139 // Unwrap move_iterators
1140 template <typename U, typename V>
1141 struct is_uninitialized_memcpyable_iterator<std::move_iterator<U>, V>
1143
1144 template <typename U, typename V = value_ty>
1145 inline static constexpr bool is_uninitialized_memcpyable_iterator_v =
1147
1148 [[noreturn]]
1149 static constexpr void throw_range_length_error() {
1150 throw std::length_error("The specified range is too long.");
1151 }
1152
1153 static constexpr value_ty* to_address(value_ty* p) noexcept {
1154 static_assert(!std::is_function_v<value_ty>,
1155 "value_ty is a function pointer.");
1156 return p;
1157 }
1158
1159 static constexpr const value_ty* to_address(const value_ty* p) noexcept {
1160 static_assert(!std::is_function_v<value_ty>,
1161 "value_ty is a function pointer.");
1162 return p;
1163 }
1164
1165 template <typename Pointer>
1166 requires has_ptr_traits_to_address_v<Pointer>
1167 static constexpr auto to_address(const Pointer& p) noexcept
1168 -> decltype(std::pointer_traits<Pointer>::to_address(p)) {
1169 return std::pointer_traits<Pointer>::to_address(p);
1170 }
1171
1172 template <typename Pointer>
1173 requires(!has_ptr_traits_to_address_v<Pointer>)
1174 static constexpr auto to_address(const Pointer& p) noexcept
1175 -> decltype(to_address(p.operator->())) {
1176 return to_address(p.operator->());
1177 }
1178
1179 template <typename Integer>
1180 [[nodiscard]]
1181 static consteval size_t numeric_max() noexcept {
1182 static_assert(0 <= (std::numeric_limits<Integer>::max)(),
1183 "Integer is nonpositive.");
1184 return static_cast<size_t>((std::numeric_limits<Integer>::max)());
1185 }
1186
1187 [[nodiscard]]
1188 static constexpr size_ty internal_range_length(cptr first,
1189 cptr last) noexcept {
1190 // This is guaranteed to be less than or equal to max size_ty.
1191 return static_cast<size_ty>(last - first);
1192 }
1193
1194 template <typename RandomIt>
1195 [[nodiscard]]
1196 static constexpr size_ty external_range_length_impl(
1197 RandomIt first, RandomIt last, std::random_access_iterator_tag) {
1198 assert(0 <= (last - first) && "Invalid range.");
1199 const auto len = static_cast<size_t>(last - first);
1200#ifndef NDEBUG
1201 if (numeric_max<size_ty>() < len)
1202 throw_range_length_error();
1203#endif
1204 return static_cast<size_ty>(len);
1205 }
1206
1207 template <typename ForwardIt>
1208 [[nodiscard]]
1209 static constexpr size_ty external_range_length_impl(
1210 ForwardIt first, ForwardIt last, std::forward_iterator_tag) {
1211 if (std::is_constant_evaluated()) {
1212 // Make sure constexpr doesn't get broken by `using namespace
1213 // std::rel_ops`.
1214 typename std::iterator_traits<ForwardIt>::difference_type len = 0;
1215 for (; !(first == last); ++first) {
1216 ++len;
1217 }
1218 assert(static_cast<size_t>(len) <= numeric_max<size_ty>());
1219 return static_cast<size_ty>(len);
1220 }
1221
1222 const auto len = static_cast<size_t>(std::distance(first, last));
1223#ifndef NDEBUG
1224 if (numeric_max<size_ty>() < len)
1225 throw_range_length_error();
1226#endif
1227 return static_cast<size_ty>(len);
1228 }
1229
1230 template <typename ForwardIt,
1231 typename ItDiffT =
1232 typename std::iterator_traits<ForwardIt>::difference_type>
1233 requires(numeric_max<size_ty>() < numeric_max<ItDiffT>())
1234 [[nodiscard]]
1235 static constexpr size_ty external_range_length(ForwardIt first,
1236 ForwardIt last) {
1237 using iterator_cat =
1238 typename std::iterator_traits<ForwardIt>::iterator_category;
1239 return external_range_length_impl(first, last, iterator_cat{});
1240 }
1241
1242 template <typename ForwardIt,
1243 typename ItDiffT =
1244 typename std::iterator_traits<ForwardIt>::difference_type>
1245 requires(!(numeric_max<size_ty>() < numeric_max<ItDiffT>()))
1246 [[nodiscard]]
1247 static constexpr size_ty external_range_length(ForwardIt first,
1248 ForwardIt last) noexcept {
1249 if (std::is_constant_evaluated()) {
1250 // Make sure constexpr doesn't get broken by `using namespace
1251 // std::rel_ops`.
1252 size_ty len = 0;
1253 for (; !(first == last); ++first) {
1254 ++len;
1255 }
1256 return len;
1257 }
1258
1259 return static_cast<size_ty>(std::distance(first, last));
1260 }
1261
1262 template <typename Iterator,
1263 typename IteratorDiffT =
1264 typename std::iterator_traits<Iterator>::difference_type,
1265 typename Integer = IteratorDiffT>
1266 [[nodiscard]]
1267 static constexpr Iterator unchecked_next(Iterator pos,
1268 Integer n = 1) noexcept {
1269 unchecked_advance(pos, static_cast<IteratorDiffT>(n));
1270 return pos;
1271 }
1272
1273 template <typename Iterator,
1274 typename IteratorDiffT =
1275 typename std::iterator_traits<Iterator>::difference_type,
1276 typename Integer = IteratorDiffT>
1277 [[nodiscard]]
1278 static constexpr Iterator unchecked_prev(Iterator pos,
1279 Integer n = 1) noexcept {
1280 unchecked_advance(pos, -static_cast<IteratorDiffT>(n));
1281 return pos;
1282 }
1283
1284 template <typename Iterator,
1285 typename IteratorDiffT =
1286 typename std::iterator_traits<Iterator>::difference_type,
1287 typename Integer = IteratorDiffT>
1288 static constexpr void unchecked_advance(Iterator& pos, Integer n) noexcept {
1289 std::advance(pos, static_cast<IteratorDiffT>(n));
1290 }
1291
1292 [[nodiscard]]
1293 constexpr size_ty get_max_size() const noexcept {
1294 // This is protected from max/min macros.
1295 return (std::min)(
1296 static_cast<size_ty>(alloc_traits::max_size(allocator_ref())),
1297 static_cast<size_ty>(numeric_max<difference_type>()));
1298 }
1299
1300 [[nodiscard]]
1301 constexpr ptr allocate(size_ty n) {
1302 return alloc_traits::allocate(allocator_ref(), static_cast<size_type>(n));
1303 }
1304
1305 [[nodiscard]]
1306 constexpr ptr allocate_with_hint(size_ty n, cptr hint) {
1307 return alloc_traits::allocate(allocator_ref(), static_cast<size_type>(n),
1308 hint);
1309 }
1310
1311 constexpr void deallocate(ptr p, size_ty n) {
1312 alloc_traits::deallocate(allocator_ref(), to_address(p),
1313 static_cast<size_type>(n));
1314 }
1315
1316 template <typename U>
1317 requires is_uninitialized_memcpyable_v<value_ty, U>
1318 constexpr void construct(ptr p, U&& val) noexcept {
1319 if (std::is_constant_evaluated()) {
1320 alloc_traits::construct(allocator_ref(), to_address(p),
1321 std::forward<U>(val));
1322 return;
1323 }
1324 std::memcpy(to_address(p), &val, sizeof(value_ty));
1325 }
1326
1327 // basically alloc_traits::construct
1328 // all this is so we can replicate C++20 behavior in the other overload
1329 template <typename A = alloc_ty, typename V = value_ty, typename... Args>
1330 requires(sizeof...(Args) != 1 ||
1331 !is_uninitialized_memcpyable_v<V, Args...>) &&
1332 has_alloc_construct_v<A, V, Args...>
1333 constexpr void construct(ptr p, Args&&... args) noexcept(
1334 noexcept(alloc_traits::construct(std::declval<alloc_ty&>(),
1335 std::declval<value_ty*>(),
1336 std::forward<Args>(args)...))) {
1337 alloc_traits::construct(allocator_ref(), to_address(p),
1338 std::forward<Args>(args)...);
1339 }
1340
1341 template <typename A = alloc_ty, typename V = value_ty, typename... Args>
1342 requires(sizeof...(Args) != 1 ||
1343 !is_uninitialized_memcpyable_v<V, Args...>) &&
1344 (!has_alloc_construct_v<A, V, Args...>) && requires {
1345 ::new (std::declval<void*>()) V(std::declval<Args>()...);
1346 }
1347 constexpr void construct(ptr p, Args&&... args) noexcept(noexcept(
1348 ::new(std::declval<void*>()) value_ty(std::declval<Args>()...))) {
1349 construct_at(to_address(p), std::forward<Args>(args)...);
1350 }
1351
1352 template <typename A = alloc_ty, typename V = value_ty>
1353 requires std::is_trivially_destructible_v<V> &&
1354 (!must_use_alloc_destroy_v<A, V>)
1355 constexpr void destroy(ptr) const noexcept {}
1356
1357 template <typename A = alloc_ty, typename V = value_ty>
1358 requires(!std::is_trivially_destructible_v<V> ||
1359 must_use_alloc_destroy_v<A, V>) &&
1360 has_alloc_destroy_v<A, V>
1361 constexpr void destroy(ptr p) noexcept {
1362 alloc_traits::destroy(allocator_ref(), to_address(p));
1363 }
1364
1365 // defined so we match C++20 behavior in all cases.
1366 template <typename A = alloc_ty, typename V = value_ty>
1367 requires(!std::is_trivially_destructible_v<V> ||
1368 must_use_alloc_destroy_v<A, V>) &&
1369 (!has_alloc_destroy_v<A, V>)
1370 constexpr void destroy(ptr p) noexcept {
1371 destroy_at(to_address(p));
1372 }
1373
1374 template <typename A = alloc_ty, typename V = value_ty>
1375 requires std::is_trivially_destructible_v<V> &&
1376 (!must_use_alloc_destroy_v<A, V>)
1377 constexpr void destroy_range(ptr, ptr) const noexcept {}
1378
1379 template <typename A = alloc_ty, typename V = value_ty>
1380 requires(!std::is_trivially_destructible_v<V> ||
1381 must_use_alloc_destroy_v<A, V>)
1382 constexpr void destroy_range(ptr first, ptr last) noexcept {
1383 for (; !(first == last); ++first) {
1384 destroy(first);
1385 }
1386 }
1387
1388 // allowed if trivially copyable and we use the standard allocator
1389 // and InputIt is a contiguous iterator
1390 template <typename ForwardIt>
1391 requires is_uninitialized_memcpyable_iterator_v<ForwardIt>
1392 constexpr ptr uninitialized_copy(ForwardIt first, ForwardIt last,
1393 ptr dest) noexcept {
1394 static_assert(std::is_constructible_v<value_ty, decltype(*first)>,
1395 "`value_type` must be copy constructible.");
1396
1397 if (std::is_constant_evaluated()) {
1398 return default_uninitialized_copy(first, last, dest);
1399 }
1400
1401 const size_ty num_copy = external_range_length(first, last);
1402 if (num_copy != 0) {
1403 std::memcpy(to_address(dest), to_address(first),
1404 num_copy * sizeof(value_ty));
1405 }
1406 return unchecked_next(dest, num_copy);
1407 }
1408
1409 template <typename ForwardIt>
1410 requires is_uninitialized_memcpyable_iterator_v<ForwardIt>
1411 constexpr ptr uninitialized_copy(std::move_iterator<ForwardIt> first,
1412 std::move_iterator<ForwardIt> last,
1413 ptr dest) noexcept {
1414 return uninitialized_copy(first.base(), last.base(), dest);
1415 }
1416
1417 template <typename InputIt>
1418 requires(!is_uninitialized_memcpyable_iterator_v<InputIt>)
1419 constexpr ptr uninitialized_copy(InputIt first, InputIt last, ptr d_first) {
1420 return default_uninitialized_copy(first, last, d_first);
1421 }
1422
1423 template <typename InputIt>
1424 constexpr ptr default_uninitialized_copy(InputIt first, InputIt last,
1425 ptr d_first) {
1426 ptr d_last = d_first;
1427 try {
1428 for (; !(first == last); ++first, static_cast<void>(++d_last)) {
1429 construct(d_last, *first);
1430 }
1431 return d_last;
1432 } catch (...) {
1433 destroy_range(d_first, d_last);
1434 throw;
1435 }
1436 }
1437
1438 template <typename A = alloc_ty, typename V = value_ty>
1439 requires(std::is_trivially_constructible_v<V> &&
1440 !must_use_alloc_construct_v<A, V>)
1441 constexpr ptr uninitialized_value_construct(ptr first, ptr last) {
1442 if (std::is_constant_evaluated()) {
1443 return default_uninitialized_value_construct(first, last);
1444 }
1445 std::fill(first, last, value_ty());
1446 return last;
1447 }
1448
1449 template <typename A = alloc_ty, typename V = value_ty>
1450 requires(!std::is_trivially_constructible_v<V> ||
1451 must_use_alloc_construct_v<A, V>)
1452 constexpr ptr uninitialized_value_construct(ptr first, ptr last) {
1453 return default_uninitialized_value_construct(first, last);
1454 }
1455
1456 constexpr ptr default_uninitialized_value_construct(ptr first, ptr last) {
1457 ptr curr = first;
1458 try {
1459 for (; !(curr == last); ++curr) {
1460 construct(curr);
1461 }
1462 return curr;
1463 } catch (...) {
1464 destroy_range(first, curr);
1465 throw;
1466 }
1467 }
1468
1469 constexpr ptr uninitialized_fill(ptr first, ptr last) {
1470 return uninitialized_value_construct(first, last);
1471 }
1472
1473 constexpr ptr uninitialized_fill(ptr first, ptr last, const value_ty& val) {
1474 ptr curr = first;
1475 try {
1476 for (; !(curr == last); ++curr) {
1477 construct(curr, val);
1478 }
1479 return curr;
1480 } catch (...) {
1481 destroy_range(first, curr);
1482 throw;
1483 }
1484 }
1485
1486 private:
1487 // If value_ty is an array, replicate C++20 behavior (I don't think that
1488 // value_ty can actually be an array because of the Erasable requirement, but
1489 // there shouldn't be any runtime cost for being defensive here).
1490 template <typename V = value_ty>
1491 requires std::is_array_v<V>
1492 static constexpr void destroy_at(value_ty* p) noexcept {
1493 for (auto& e : *p) {
1494 destroy_at(std::addressof(e));
1495 }
1496 }
1497
1498 template <typename V = value_ty>
1499 requires(!std::is_array_v<V>)
1500 static constexpr void destroy_at(value_ty* p) noexcept {
1501 p->~value_ty();
1502 }
1503
1504 template <typename V = value_ty, typename... Args>
1505 static constexpr auto construct_at(value_ty* p, Args&&... args) noexcept(
1506 noexcept(::new(std::declval<void*>()) V(std::declval<Args>()...)))
1507 -> decltype(::new(std::declval<void*>()) V(std::declval<Args>()...)) {
1508 if (std::is_constant_evaluated()) {
1509 return std::construct_at(p, std::forward<Args>(args)...);
1510 }
1511 void* vp = const_cast<void*>(static_cast<const volatile void*>(p));
1512 return ::new (vp) value_ty(std::forward<Args>(args)...);
1513 }
1514};
1515
1516template <typename Pointer, typename SizeT>
1517class small_vector_data_base {
1518 public:
1519 using ptr = Pointer;
1520 using size_ty = SizeT;
1521
1522 small_vector_data_base() = default;
1523 small_vector_data_base(const small_vector_data_base&) = default;
1524 small_vector_data_base(small_vector_data_base&&) noexcept = default;
1525 small_vector_data_base& operator=(const small_vector_data_base&) = default;
1526 small_vector_data_base& operator=(small_vector_data_base&&) noexcept =
1527 default;
1528 ~small_vector_data_base() = default;
1529
1530 constexpr ptr data_ptr() const noexcept { return m_data_ptr; }
1531 constexpr size_ty capacity() const noexcept { return m_capacity; }
1532 constexpr size_ty size() const noexcept { return m_size; }
1533
1534 constexpr void set_data_ptr(ptr data_ptr) noexcept { m_data_ptr = data_ptr; }
1535 constexpr void set_capacity(size_ty capacity) noexcept {
1536 m_capacity = capacity;
1537 }
1538 constexpr void set_size(size_ty size) noexcept { m_size = size; }
1539
1540 constexpr void set(ptr data_ptr, size_ty capacity, size_ty size) {
1541 m_data_ptr = data_ptr;
1542 m_capacity = capacity;
1543 m_size = size;
1544 }
1545
1546 constexpr void swap_data_ptr(small_vector_data_base& other) noexcept {
1547 using std::swap;
1548 swap(m_data_ptr, other.m_data_ptr);
1549 }
1550
1551 constexpr void swap_capacity(small_vector_data_base& other) noexcept {
1552 using std::swap;
1553 swap(m_capacity, other.m_capacity);
1554 }
1555
1556 constexpr void swap_size(small_vector_data_base& other) noexcept {
1557 using std::swap;
1558 swap(m_size, other.m_size);
1559 }
1560
1561 constexpr void swap(small_vector_data_base& other) noexcept {
1562 using std::swap;
1563 swap(m_data_ptr, other.m_data_ptr);
1564 swap(m_capacity, other.m_capacity);
1565 swap(m_size, other.m_size);
1566 }
1567
1568 private:
1569 ptr m_data_ptr;
1570 size_ty m_capacity;
1571 size_ty m_size;
1572};
1573
1574template <typename Pointer, typename SizeT, typename T, unsigned InlineCapacity>
1575class small_vector_data : public small_vector_data_base<Pointer, SizeT> {
1576 public:
1577 using value_ty = T;
1578
1579 small_vector_data() = default;
1580 small_vector_data(const small_vector_data&) = delete;
1581 small_vector_data(small_vector_data&&) noexcept = delete;
1582 small_vector_data& operator=(const small_vector_data&) = delete;
1583 small_vector_data& operator=(small_vector_data&&) noexcept = delete;
1584 ~small_vector_data() = default;
1585
1586 constexpr value_ty* storage() noexcept { return m_storage.get_inline_ptr(); }
1587
1588 constexpr const value_ty* storage() const noexcept {
1589 return m_storage.get_inline_ptr();
1590 }
1591
1592 private:
1593 inline_storage<value_ty, InlineCapacity> m_storage;
1594};
1595
1596template <typename Pointer, typename SizeT, typename T>
1597class small_vector_data<Pointer, SizeT, T, 0>
1598 : public small_vector_data_base<Pointer, SizeT>,
1599 private inline_storage<T, 0> {
1600 using base = inline_storage<T, 0>;
1601
1602 public:
1603 using value_ty = T;
1604
1605 small_vector_data() = default;
1606 small_vector_data(const small_vector_data&) = delete;
1607 small_vector_data(small_vector_data&&) noexcept = delete;
1608 small_vector_data& operator=(const small_vector_data&) = delete;
1609 small_vector_data& operator=(small_vector_data&&) noexcept = delete;
1610 ~small_vector_data() = default;
1611
1612 constexpr value_ty* storage() noexcept { return base::get_inline_ptr(); }
1613
1614 constexpr const value_ty* storage() const noexcept {
1615 return base::get_inline_ptr();
1616 }
1617};
1618
1619template <typename Allocator, unsigned InlineCapacity>
1620class small_vector_base : public allocator_interface<Allocator> {
1621 public:
1622 using size_type = typename allocator_interface<Allocator>::size_type;
1623 using difference_type =
1624 typename allocator_interface<Allocator>::difference_type;
1625
1626 template <typename SameAllocator, unsigned DifferentInlineCapacity>
1627 friend class small_vector_base;
1628
1629 protected:
1630 using alloc_interface = allocator_interface<Allocator>;
1631 using alloc_traits = typename alloc_interface::alloc_traits;
1632 using alloc_ty = Allocator;
1633
1634 using value_ty = typename alloc_interface::value_ty;
1635 using ptr = typename alloc_interface::ptr;
1636 using cptr = typename alloc_interface::cptr;
1637 using size_ty = typename alloc_interface::size_ty;
1638 using diff_ty = typename alloc_interface::diff_ty;
1639
1640 static_assert(
1641 alloc_interface::template is_complete_v<value_ty> || InlineCapacity == 0,
1642 "`value_type` must be complete for instantiation of a non-zero number "
1643 "of inline elements.");
1644
1645 template <typename T>
1646 inline static constexpr bool is_complete_v =
1647 alloc_interface::template is_complete_v<T>;
1648
1649 using alloc_interface::allocator_ref;
1650 using alloc_interface::construct;
1651 using alloc_interface::deallocate;
1652 using alloc_interface::destroy;
1653 using alloc_interface::destroy_range;
1654 using alloc_interface::external_range_length;
1655 using alloc_interface::get_max_size;
1656 using alloc_interface::internal_range_length;
1657 using alloc_interface::to_address;
1658 using alloc_interface::unchecked_advance;
1659 using alloc_interface::unchecked_next;
1660 using alloc_interface::unchecked_prev;
1661 using alloc_interface::uninitialized_copy;
1662 using alloc_interface::uninitialized_fill;
1663 using alloc_interface::uninitialized_value_construct;
1664
1665 template <typename Integer>
1666 [[nodiscard]]
1667 static consteval size_t numeric_max() noexcept {
1668 return alloc_interface::template numeric_max<Integer>();
1669 }
1670
1671 [[nodiscard]]
1672 static consteval size_ty get_inline_capacity() noexcept {
1673 return static_cast<size_ty>(InlineCapacity);
1674 }
1675
1676 template <typename... Args>
1677 inline static constexpr bool is_emplace_constructible_v =
1678 is_complete_v<Args...> && requires {
1679 std::declval<alloc_interface&>().construct(std::declval<value_ty*>(),
1680 std::declval<Args>()...);
1681 };
1682
1683 template <typename... Args>
1684 inline static constexpr bool is_nothrow_emplace_constructible_v =
1685 is_complete_v<Args...> && requires {
1686 noexcept(std::declval<alloc_interface&>().construct(
1687 std::declval<value_ty*>(), std::declval<Args>()...));
1688 };
1689
1690 template <typename V = value_ty>
1691 inline static constexpr bool is_explicitly_move_insertable_v =
1692 is_emplace_constructible_v<V&&>;
1693
1694 template <typename V = value_ty>
1695 inline static constexpr bool is_explicitly_nothrow_move_insertable_v =
1696 is_nothrow_emplace_constructible_v<V&&>;
1697
1698 template <typename V = value_ty>
1699 inline static constexpr bool is_explicitly_copy_insertable_v =
1700 is_emplace_constructible_v<V&> && is_emplace_constructible_v<const V&>;
1701
1702 template <typename V = value_ty>
1703 inline static constexpr bool is_explicitly_nothrow_copy_insertable_v =
1704 is_nothrow_emplace_constructible_v<V&> &&
1705 is_nothrow_emplace_constructible_v<const V&>;
1706
1707 template <typename V>
1708 inline static constexpr bool relocate_with_move_v =
1709 std::is_nothrow_move_constructible_v<V> ||
1710 !is_explicitly_copy_insertable_v<V>;
1711
1712 template <typename A>
1713 inline static constexpr bool allocations_are_movable_v =
1714 std::is_same_v<std::allocator<value_ty>, A> ||
1715 std::allocator_traits<A>::propagate_on_container_move_assignment::value ||
1716 std::allocator_traits<A>::is_always_equal::value;
1717
1718 template <typename A>
1719 inline static constexpr bool allocations_are_swappable_v =
1720 std::is_same_v<std::allocator<value_ty>, A> ||
1721 std::allocator_traits<A>::propagate_on_container_swap::value ||
1722 std::allocator_traits<A>::is_always_equal::value;
1723
1724 template <typename... Args>
1725 inline static constexpr bool is_memcpyable_v =
1726 alloc_interface::template is_memcpyable_v<Args...>;
1727
1728 template <typename... Args>
1729 inline static constexpr bool is_memcpyable_iterator_v =
1730 alloc_interface::template is_memcpyable_iterator_v<Args...>;
1731
1732 [[noreturn]]
1733 static constexpr void throw_overflow_error() {
1734 throw std::overflow_error("The requested conversion would overflow.");
1735 }
1736
1737 [[noreturn]]
1738 static constexpr void throw_index_error() {
1739 throw std::out_of_range("The requested index was out of range.");
1740 }
1741
1742 [[noreturn]]
1743 static constexpr void throw_increment_error() {
1744 throw std::domain_error(
1745 "The requested increment was outside of the allowed range.");
1746 }
1747
1748 [[noreturn]]
1749 static constexpr void throw_allocation_size_error() {
1750 throw std::length_error(
1751 "The required allocation exceeds the maximum size.");
1752 }
1753
1754 [[nodiscard]]
1755 constexpr ptr ptr_cast(
1756 const small_vector_iterator<cptr, diff_ty>& it) noexcept {
1757 return unchecked_next(begin_ptr(), it.base() - begin_ptr());
1758 }
1759
1760 private:
1761 class stack_temporary {
1762 public:
1763 stack_temporary() = delete;
1764 stack_temporary(const stack_temporary&) = delete;
1765 stack_temporary(stack_temporary&&) noexcept = delete;
1766 stack_temporary& operator=(const stack_temporary&) = delete;
1767 stack_temporary& operator=(stack_temporary&&) noexcept = delete;
1768
1769 template <typename... Args>
1770 constexpr explicit stack_temporary(alloc_interface& alloc_iface,
1771 Args&&... args)
1772 : m_interface(alloc_iface) {
1773 m_interface.construct(get_pointer(), std::forward<Args>(args)...);
1774 }
1775
1776 constexpr ~stack_temporary() { m_interface.destroy(get_pointer()); }
1777
1778 [[nodiscard]]
1779 constexpr const value_ty& get() const noexcept {
1780 return *get_pointer();
1781 }
1782
1783 [[nodiscard]]
1784 constexpr value_ty&& release() noexcept {
1785 return std::move(*get_pointer());
1786 }
1787
1788 private:
1789 [[nodiscard]]
1790 constexpr cptr get_pointer() const noexcept {
1791 return static_cast<cptr>(
1792 static_cast<const void*>(std::addressof(m_data)));
1793 }
1794
1795 [[nodiscard]]
1796 constexpr ptr get_pointer() noexcept {
1797 return static_cast<ptr>(static_cast<void*>(std::addressof(m_data)));
1798 }
1799
1800 alloc_interface& m_interface;
1801 alignas(value_ty) std::byte m_data[sizeof(value_ty)];
1802 };
1803
1804 class heap_temporary {
1805 public:
1806 heap_temporary() = delete;
1807 heap_temporary(const heap_temporary&) = delete;
1808 heap_temporary(heap_temporary&&) noexcept = delete;
1809 heap_temporary& operator=(const heap_temporary&) = delete;
1810 heap_temporary& operator=(heap_temporary&&) noexcept = delete;
1811
1812 template <typename... Args>
1813 constexpr explicit heap_temporary(alloc_interface& alloc_iface,
1814 Args&&... args)
1815 : m_interface(alloc_iface),
1816 m_data_ptr(alloc_iface.allocate(sizeof(value_ty))) {
1817 try {
1818 m_interface.construct(m_data_ptr, std::forward<Args>(args)...);
1819 } catch (...) {
1820 m_interface.deallocate(m_data_ptr, sizeof(value_ty));
1821 throw;
1822 }
1823 }
1824
1825 constexpr ~heap_temporary() {
1826 m_interface.destroy(m_data_ptr);
1827 m_interface.deallocate(m_data_ptr, sizeof(value_ty));
1828 }
1829
1830 [[nodiscard]]
1831 constexpr const value_ty& get() const noexcept {
1832 return *m_data_ptr;
1833 }
1834
1835 [[nodiscard]]
1836 constexpr value_ty&& release() noexcept {
1837 return std::move(*m_data_ptr);
1838 }
1839
1840 private:
1841 alloc_interface& m_interface;
1842 ptr m_data_ptr;
1843 };
1844
1845 constexpr void wipe() {
1846 destroy_range(begin_ptr(), end_ptr());
1847 if (has_allocation()) {
1848 deallocate(data_ptr(), get_capacity());
1849 }
1850 }
1851
1852 constexpr void set_data_ptr(ptr data_ptr) noexcept {
1853 m_data.set_data_ptr(data_ptr);
1854 }
1855
1856 constexpr void set_capacity(size_ty capacity) noexcept {
1857 m_data.set_capacity(static_cast<size_type>(capacity));
1858 }
1859
1860 constexpr void set_size(size_ty size) noexcept {
1861 m_data.set_size(static_cast<size_type>(size));
1862 }
1863
1864 constexpr void set_data(ptr data_ptr, size_ty capacity,
1865 size_ty size) noexcept {
1866 m_data.set(data_ptr, static_cast<size_type>(capacity),
1867 static_cast<size_type>(size));
1868 }
1869
1870 constexpr void swap_data_ptr(small_vector_base& other) noexcept {
1871 m_data.swap_data_ptr(other.m_data);
1872 }
1873
1874 constexpr void swap_capacity(small_vector_base& other) noexcept {
1875 m_data.swap_capacity(other.m_data);
1876 }
1877
1878 constexpr void swap_size(small_vector_base& other) noexcept {
1879 m_data.swap_size(other.m_data);
1880 }
1881
1882 constexpr void swap_allocation(small_vector_base& other) noexcept {
1883 m_data.swap(other.m_data);
1884 }
1885
1886 constexpr void reset_data(ptr data_ptr, size_ty capacity, size_ty size) {
1887 wipe();
1888 m_data.set(data_ptr, static_cast<size_type>(capacity),
1889 static_cast<size_type>(size));
1890 }
1891
1892 constexpr void increase_size(size_ty n) noexcept {
1893 m_data.set_size(get_size() + n);
1894 }
1895
1896 constexpr void decrease_size(size_ty n) noexcept {
1897 m_data.set_size(get_size() - n);
1898 }
1899
1900 constexpr ptr unchecked_allocate(size_ty n) {
1901 assert(InlineCapacity < n &&
1902 "Allocated capacity should be greater than InlineCapacity.");
1903 return alloc_interface::allocate(n);
1904 }
1905
1906 constexpr ptr unchecked_allocate(size_ty n, cptr hint) {
1907 assert(InlineCapacity < n &&
1908 "Allocated capacity should be greater than InlineCapacity.");
1909 return alloc_interface::allocate_with_hint(n, hint);
1910 }
1911
1912 constexpr ptr checked_allocate(size_ty n) {
1913 if (get_max_size() < n) {
1914 throw_allocation_size_error();
1915 }
1916 return unchecked_allocate(n);
1917 }
1918
1919 protected:
1920 [[nodiscard]]
1921 constexpr size_ty unchecked_calculate_new_capacity(
1922 const size_ty minimum_required_capacity) const noexcept {
1923 const size_ty current_capacity = get_capacity();
1924
1925 assert(current_capacity < minimum_required_capacity);
1926
1927 if (get_max_size() - current_capacity <= current_capacity) {
1928 return get_max_size();
1929 }
1930
1931 // Note: This growth factor might be theoretically superior, but in testing
1932 // it falls flat: size_ty new_capacity = current_capacity +
1933 // (current_capacity / 2);
1934
1935 const size_ty new_capacity = 2 * current_capacity;
1936 if (new_capacity < minimum_required_capacity) {
1937 return minimum_required_capacity;
1938 }
1939 return new_capacity;
1940 }
1941
1942 [[nodiscard]]
1943 constexpr size_ty checked_calculate_new_capacity(
1944 const size_ty minimum_required_capacity) const {
1945 if (get_max_size() < minimum_required_capacity) {
1946 throw_allocation_size_error();
1947 }
1948 return unchecked_calculate_new_capacity(minimum_required_capacity);
1949 }
1950
1951 template <unsigned I>
1952 constexpr small_vector_base& copy_assign_default(
1953 const small_vector_base<Allocator, I>& other) {
1954 if (get_capacity() < other.get_size()) {
1955 // Reallocate.
1956 size_ty new_capacity = unchecked_calculate_new_capacity(other.get_size());
1957 ptr new_data_ptr =
1958 unchecked_allocate(new_capacity, other.allocation_end_ptr());
1959
1960 try {
1961 uninitialized_copy(other.begin_ptr(), other.end_ptr(), new_data_ptr);
1962 } catch (...) {
1963 deallocate(new_data_ptr, new_capacity);
1964 throw;
1965 }
1966
1967 reset_data(new_data_ptr, new_capacity, other.get_size());
1968 } else {
1969 if (get_size() < other.get_size()) {
1970 // No reallocation, partially in uninitialized space.
1971 std::copy_n(other.begin_ptr(), get_size(), begin_ptr());
1972 uninitialized_copy(unchecked_next(other.begin_ptr(), get_size()),
1973 other.end_ptr(), end_ptr());
1974 } else {
1975 destroy_range(
1976 copy_range(other.begin_ptr(), other.end_ptr(), begin_ptr()),
1977 end_ptr());
1978 }
1979
1980 // data_ptr and capacity do not change in this case.
1981 set_size(other.get_size());
1982 }
1983
1984 alloc_interface::operator=(other);
1985 return *this;
1986 }
1987
1988 template <unsigned I, typename AT = alloc_traits>
1989 requires(AT::propagate_on_container_copy_assignment::value &&
1990 !AT::is_always_equal::value)
1991 constexpr small_vector_base& copy_assign(
1992 const small_vector_base<Allocator, I>& other) {
1993 if (other.allocator_ref() == allocator_ref()) {
1994 return copy_assign_default(other);
1995 }
1996
1997 if (InlineCapacity < other.get_size()) {
1998 alloc_interface new_alloc(other);
1999
2000 const size_ty new_capacity = other.get_size();
2001 const ptr new_data_ptr = new_alloc.allocate_with_hint(
2002 new_capacity, other.allocation_end_ptr());
2003
2004 try {
2005 uninitialized_copy(other.begin_ptr(), other.end_ptr(), new_data_ptr);
2006 } catch (...) {
2007 new_alloc.deallocate(new_data_ptr, new_capacity);
2008 throw;
2009 }
2010
2011 reset_data(new_data_ptr, new_capacity, other.get_size());
2012 alloc_interface::operator=(new_alloc);
2013 } else {
2014 if (has_allocation()) {
2015 ptr new_data_ptr;
2016 if (std::is_constant_evaluated()) {
2017 alloc_interface new_alloc(other);
2018 new_data_ptr = new_alloc.allocate(InlineCapacity);
2019 } else {
2020 new_data_ptr = storage_ptr();
2021 }
2022
2023 uninitialized_copy(other.begin_ptr(), other.end_ptr(), new_data_ptr);
2024 destroy_range(begin_ptr(), end_ptr());
2025 deallocate(data_ptr(), get_capacity());
2026 set_data_ptr(new_data_ptr);
2027 set_capacity(InlineCapacity);
2028 } else if (get_size() < other.get_size()) {
2029 std::copy_n(other.begin_ptr(), get_size(), begin_ptr());
2030 uninitialized_copy(unchecked_next(other.begin_ptr(), get_size()),
2031 other.end_ptr(), end_ptr());
2032 } else {
2033 destroy_range(
2034 copy_range(other.begin_ptr(), other.end_ptr(), begin_ptr()),
2035 end_ptr());
2036 }
2037 set_size(other.get_size());
2038 alloc_interface::operator=(other);
2039 }
2040
2041 return *this;
2042 }
2043
2044 template <unsigned I, typename AT = alloc_traits>
2045 requires(!AT::propagate_on_container_copy_assignment::value ||
2046 AT::is_always_equal::value)
2047 constexpr small_vector_base& copy_assign(
2048 const small_vector_base<Allocator, I>& other) {
2049 return copy_assign_default(other);
2050 }
2051
2052 template <unsigned I>
2053 constexpr void move_allocation_pointer(
2054 small_vector_base<alloc_ty, I>&& other) noexcept {
2055 reset_data(other.data_ptr(), other.get_capacity(), other.get_size());
2056 other.set_default();
2057 }
2058
2059 template <unsigned N = InlineCapacity>
2060 requires(N == 0)
2061 constexpr small_vector_base& move_assign_default(
2062 small_vector_base&& other) noexcept {
2063 move_allocation_pointer(std::move(other));
2064 alloc_interface::operator=(std::move(other));
2065 return *this;
2066 }
2067
2068 template <unsigned LessEqualI>
2069 requires(LessEqualI <= InlineCapacity)
2070 constexpr small_vector_base& move_assign_default(
2071 small_vector_base<Allocator, LessEqualI>&&
2072 other) noexcept(std::is_nothrow_move_assignable_v<value_ty> &&
2073 std::is_nothrow_move_constructible_v<value_ty>) {
2074 // We only move the allocation pointer over if it has strictly greater
2075 // capacity than the inline capacity of `*this` because allocations can
2076 // never have a smaller capacity than the inline capacity.
2077 if (InlineCapacity < other.get_capacity()) {
2078 move_allocation_pointer(std::move(other));
2079 } else {
2080 // We are guaranteed to have sufficient capacity to store the elements.
2081 if (InlineCapacity < get_capacity()) {
2082 ptr new_data_ptr;
2083 if (std::is_constant_evaluated()) {
2084 new_data_ptr = other.allocate(InlineCapacity);
2085 } else {
2086 new_data_ptr = storage_ptr();
2087 }
2088
2089 uninitialized_move(other.begin_ptr(), other.end_ptr(), new_data_ptr);
2090 destroy_range(begin_ptr(), end_ptr());
2091 deallocate(data_ptr(), get_capacity());
2092 set_data_ptr(new_data_ptr);
2093 set_capacity(InlineCapacity);
2094 } else if (get_size() < other.get_size()) {
2095 // There are more elements in `other`.
2096 // Overwrite the existing range and uninitialized move the rest.
2097 ptr other_pivot = unchecked_next(other.begin_ptr(), get_size());
2098 std::move(other.begin_ptr(), other_pivot, begin_ptr());
2099 uninitialized_move(other_pivot, other.end_ptr(), end_ptr());
2100 } else {
2101 // There are the same number or fewer elements in `other`.
2102 // Overwrite part of the existing range and destroy the rest.
2103 ptr new_end =
2104 std::move(other.begin_ptr(), other.end_ptr(), begin_ptr());
2105 destroy_range(new_end, end_ptr());
2106 }
2107
2108 set_size(other.get_size());
2109
2110 // Note: We do not need to deallocate any allocations in `other` because
2111 // the value of
2112 // an object meeting the Allocator named requirements does not
2113 // change value after a move.
2114 }
2115
2116 alloc_interface::operator=(std::move(other));
2117 return *this;
2118 }
2119
2120 template <unsigned GreaterI>
2121 requires(InlineCapacity < GreaterI)
2122 constexpr small_vector_base& move_assign_default(
2123 small_vector_base<Allocator, GreaterI>&& other) {
2124 if (other.has_allocation()) {
2125 move_allocation_pointer(std::move(other));
2126 } else if (get_capacity() < other.get_size() ||
2127 (has_allocation() &&
2128 !(other.allocator_ref() == allocator_ref()))) {
2129 // Reallocate.
2130
2131 // The compiler should be able to optimize this.
2132 size_ty new_capacity =
2133 get_capacity() < other.get_size()
2134 ? unchecked_calculate_new_capacity(other.get_size())
2135 : get_capacity();
2136
2137 ptr new_data_ptr =
2138 other.allocate_with_hint(new_capacity, other.allocation_end_ptr());
2139
2140 try {
2141 uninitialized_move(other.begin_ptr(), other.end_ptr(), new_data_ptr);
2142 } catch (...) {
2143 other.deallocate(new_data_ptr, new_capacity);
2144 throw;
2145 }
2146
2147 reset_data(new_data_ptr, new_capacity, other.get_size());
2148 } else {
2149 if (get_size() < other.get_size()) {
2150 // There are more elements in `other`.
2151 // Overwrite the existing range and uninitialized move the rest.
2152 ptr other_pivot = unchecked_next(other.begin_ptr(), get_size());
2153 std::move(other.begin_ptr(), other_pivot, begin_ptr());
2154 uninitialized_move(other_pivot, other.end_ptr(), end_ptr());
2155 } else {
2156 // fewer elements in other
2157 // overwrite part of the existing range and destroy the rest
2158 ptr new_end =
2159 std::move(other.begin_ptr(), other.end_ptr(), begin_ptr());
2160 destroy_range(new_end, end_ptr());
2161 }
2162
2163 // `data_ptr` and `capacity` do not change in this case.
2164 set_size(other.get_size());
2165 }
2166
2167 alloc_interface::operator=(std::move(other));
2168 return *this;
2169 }
2170
2171 template <unsigned I>
2172 constexpr small_vector_base& move_assign_unequal_no_propagate(
2173 small_vector_base<Allocator, I>&& other) {
2174 if (get_capacity() < other.get_size()) {
2175 // Reallocate.
2176 size_ty new_capacity = unchecked_calculate_new_capacity(other.get_size());
2177 ptr new_data_ptr =
2178 unchecked_allocate(new_capacity, other.allocation_end_ptr());
2179
2180 try {
2181 uninitialized_move(other.begin_ptr(), other.end_ptr(), new_data_ptr);
2182 } catch (...) {
2183 deallocate(new_data_ptr, new_capacity);
2184 throw;
2185 }
2186
2187 reset_data(new_data_ptr, new_capacity, other.get_size());
2188 } else {
2189 if (get_size() < other.get_size()) {
2190 // There are more elements in `other`.
2191 // Overwrite the existing range and uninitialized move the rest.
2192 ptr other_pivot = unchecked_next(other.begin_ptr(), get_size());
2193 std::move(other.begin_ptr(), other_pivot, begin_ptr());
2194 uninitialized_move(other_pivot, other.end_ptr(), end_ptr());
2195 } else {
2196 // There are fewer elements in `other`.
2197 // Overwrite part of the existing range and destroy the rest.
2198 destroy_range(
2199 std::move(other.begin_ptr(), other.end_ptr(), begin_ptr()),
2200 end_ptr());
2201 }
2202
2203 // data_ptr and capacity do not change in this case
2204 set_size(other.get_size());
2205 }
2206
2207 alloc_interface::operator=(std::move(other));
2208 return *this;
2209 }
2210
2211 template <unsigned I, typename A = alloc_ty>
2212 requires allocations_are_movable_v<A>
2213 constexpr small_vector_base&
2214 move_assign(small_vector_base<Allocator, I>&& other) noexcept(
2215 noexcept(std::declval<small_vector_base&>().move_assign_default(
2216 std::move(other)))) {
2217 return move_assign_default(std::move(other));
2218 }
2219
2220 template <unsigned I, typename A = alloc_ty>
2221 requires(!allocations_are_movable_v<A>)
2222 constexpr small_vector_base& move_assign(
2223 small_vector_base<Allocator, I>&& other) {
2224 if (other.allocator_ref() == allocator_ref()) {
2225 return move_assign_default(std::move(other));
2226 }
2227 return move_assign_unequal_no_propagate(std::move(other));
2228 }
2229
2230 template <unsigned I = InlineCapacity>
2231 requires(I == 0)
2232 constexpr void move_initialize(small_vector_base&& other) noexcept {
2233 set_data(other.data_ptr(), other.get_capacity(), other.get_size());
2234 other.set_default();
2235 }
2236
2237 template <unsigned LessEqualI>
2238 requires(LessEqualI <= InlineCapacity)
2239 constexpr void
2240 move_initialize(small_vector_base<Allocator, LessEqualI>&& other) noexcept(
2241 std::is_nothrow_move_constructible_v<value_ty>) {
2242 if (InlineCapacity < other.get_capacity()) {
2243 set_data(other.data_ptr(), other.get_capacity(), other.get_size());
2244 other.set_default();
2245 } else {
2246 set_to_inline_storage();
2247 uninitialized_move(other.begin_ptr(), other.end_ptr(), data_ptr());
2248 set_size(other.get_size());
2249 }
2250 }
2251
2252 template <unsigned GreaterI>
2253 requires(InlineCapacity < GreaterI)
2254 constexpr void move_initialize(
2255 small_vector_base<Allocator, GreaterI>&& other) {
2256 if (other.has_allocation()) {
2257 set_data(other.data_ptr(), other.get_capacity(), other.get_size());
2258 other.set_default();
2259 } else {
2260 if (InlineCapacity < other.get_size()) {
2261 // We may throw in this case.
2262 set_data_ptr(
2263 unchecked_allocate(other.get_size(), other.allocation_end_ptr()));
2264 set_capacity(other.get_size());
2265
2266 try {
2267 uninitialized_move(other.begin_ptr(), other.end_ptr(), data_ptr());
2268 } catch (...) {
2269 deallocate(data_ptr(), get_capacity());
2270 throw;
2271 }
2272 } else {
2273 set_to_inline_storage();
2274 uninitialized_move(other.begin_ptr(), other.end_ptr(), data_ptr());
2275 }
2276
2277 set_size(other.get_size());
2278 }
2279 }
2280
2281 public:
2282 small_vector_base(const small_vector_base&) = delete;
2283 small_vector_base(small_vector_base&&) noexcept = delete;
2284 small_vector_base& operator=(const small_vector_base&) = delete;
2285 small_vector_base& operator=(small_vector_base&&) noexcept = delete;
2286
2287 constexpr small_vector_base() noexcept { set_default(); }
2288
2289 static constexpr struct bypass_tag {
2290 } bypass{};
2291
2292 template <unsigned I, typename... MaybeAlloc>
2293 constexpr small_vector_base(bypass_tag,
2294 const small_vector_base<Allocator, I>& other,
2295 const MaybeAlloc&... alloc)
2296 : alloc_interface(other, alloc...) {
2297 if (InlineCapacity < other.get_size()) {
2298 set_data_ptr(
2299 unchecked_allocate(other.get_size(), other.allocation_end_ptr()));
2300 set_capacity(other.get_size());
2301
2302 try {
2303 uninitialized_copy(other.begin_ptr(), other.end_ptr(), data_ptr());
2304 } catch (...) {
2305 deallocate(data_ptr(), get_capacity());
2306 throw;
2307 }
2308 } else {
2309 set_to_inline_storage();
2310 uninitialized_copy(other.begin_ptr(), other.end_ptr(), data_ptr());
2311 }
2312
2313 set_size(other.get_size());
2314 }
2315
2316 template <unsigned I>
2317 constexpr small_vector_base(
2318 bypass_tag,
2319 small_vector_base<Allocator, I>&&
2320 other) noexcept(std::is_nothrow_move_constructible_v<value_ty> ||
2321 (I == 0 && I == InlineCapacity))
2322 : alloc_interface(std::move(other)) {
2323 move_initialize(std::move(other));
2324 }
2325
2326 template <unsigned I, typename A = alloc_ty>
2327 requires std::same_as<A, std::allocator<value_ty>> ||
2328 std::allocator_traits<A>::is_always_equal::value
2329 constexpr small_vector_base(
2330 bypass_tag, small_vector_base<Allocator, I>&& other,
2331 const alloc_ty&) noexcept(noexcept(small_vector_base(bypass,
2332 std::move(other))))
2333 : small_vector_base(bypass, std::move(other)) {}
2334
2335 template <unsigned I, typename A = alloc_ty>
2336 requires(!(std::same_as<A, std::allocator<value_ty>> ||
2337 std::allocator_traits<A>::is_always_equal::value))
2338 constexpr small_vector_base(bypass_tag,
2339 small_vector_base<Allocator, I>&& other,
2340 const alloc_ty& alloc)
2341 : alloc_interface(alloc) {
2342 if (other.allocator_ref() == alloc) {
2343 move_initialize(std::move(other));
2344 return;
2345 }
2346
2347 if (InlineCapacity < other.get_size()) {
2348 // We may throw in this case.
2349 set_data_ptr(
2350 unchecked_allocate(other.get_size(), other.allocation_end_ptr()));
2351 set_capacity(other.get_size());
2352
2353 try {
2354 uninitialized_move(other.begin_ptr(), other.end_ptr(), data_ptr());
2355 } catch (...) {
2356 deallocate(data_ptr(), get_capacity());
2357 throw;
2358 }
2359 } else {
2360 set_to_inline_storage();
2361 uninitialized_move(other.begin_ptr(), other.end_ptr(), data_ptr());
2362 }
2363
2364 set_size(other.get_size());
2365 }
2366
2367 constexpr explicit small_vector_base(const alloc_ty& alloc) noexcept
2368 : alloc_interface(alloc) {
2369 set_default();
2370 }
2371
2372 constexpr small_vector_base(size_ty count, const alloc_ty& alloc)
2373 : alloc_interface(alloc) {
2374 if (InlineCapacity < count) {
2375 set_data_ptr(checked_allocate(count));
2376 set_capacity(count);
2377 } else {
2378 set_to_inline_storage();
2379 }
2380
2381 try {
2382 uninitialized_value_construct(begin_ptr(),
2383 unchecked_next(begin_ptr(), count));
2384 } catch (...) {
2385 if (has_allocation()) {
2386 deallocate(data_ptr(), get_capacity());
2387 }
2388 throw;
2389 }
2390 set_size(count);
2391 }
2392
2393 constexpr small_vector_base(size_ty count, const value_ty& val,
2394 const alloc_ty& alloc)
2395 : alloc_interface(alloc) {
2396 if (InlineCapacity < count) {
2397 set_data_ptr(checked_allocate(count));
2398 set_capacity(count);
2399 } else {
2400 set_to_inline_storage();
2401 }
2402
2403 try {
2404 uninitialized_fill(begin_ptr(), unchecked_next(begin_ptr(), count), val);
2405 } catch (...) {
2406 if (has_allocation()) {
2407 deallocate(data_ptr(), get_capacity());
2408 }
2409 throw;
2410 }
2411 set_size(count);
2412 }
2413
2414 template <typename Generator>
2415 constexpr small_vector_base(size_ty count, Generator& g,
2416 const alloc_ty& alloc)
2417 : alloc_interface(alloc) {
2418 if (InlineCapacity < count) {
2419 set_data_ptr(checked_allocate(count));
2420 set_capacity(count);
2421 } else {
2422 set_to_inline_storage();
2423 }
2424
2425 ptr curr = begin_ptr();
2426 const ptr new_end = unchecked_next(begin_ptr(), count);
2427 try {
2428 for (; !(curr == new_end); ++curr) {
2429 construct(curr, g());
2430 }
2431 } catch (...) {
2432 destroy_range(begin_ptr(), curr);
2433 if (has_allocation()) {
2434 deallocate(data_ptr(), get_capacity());
2435 }
2436 throw;
2437 }
2438 set_size(count);
2439 }
2440
2441 template <std::input_iterator InputIt>
2442 constexpr small_vector_base(InputIt first, InputIt last,
2443 std::input_iterator_tag, const alloc_ty& alloc)
2444 : small_vector_base(alloc) {
2445 using iterator_cat =
2446 typename std::iterator_traits<InputIt>::iterator_category;
2447 append_range(first, last, iterator_cat{});
2448 }
2449
2450 template <std::forward_iterator ForwardIt>
2451 constexpr small_vector_base(ForwardIt first, ForwardIt last,
2452 std::forward_iterator_tag, const alloc_ty& alloc)
2453 : alloc_interface(alloc) {
2454 size_ty count = external_range_length(first, last);
2455 if (InlineCapacity < count) {
2456 set_data_ptr(unchecked_allocate(count));
2457 set_capacity(count);
2458 try {
2459 uninitialized_copy(first, last, begin_ptr());
2460 } catch (...) {
2461 deallocate(data_ptr(), get_capacity());
2462 throw;
2463 }
2464 } else {
2465 set_to_inline_storage();
2466 uninitialized_copy(first, last, begin_ptr());
2467 }
2468
2469 set_size(count);
2470 }
2471
2472 constexpr ~small_vector_base() noexcept {
2473 assert(InlineCapacity <= get_capacity() && "Invalid capacity.");
2474 wipe();
2475 }
2476
2477 protected:
2478 constexpr void set_to_inline_storage() {
2479 set_capacity(InlineCapacity);
2480 if (std::is_constant_evaluated()) {
2481 return set_data_ptr(alloc_interface::allocate(InlineCapacity));
2482 }
2483 set_data_ptr(storage_ptr());
2484 }
2485
2486 constexpr void assign_with_copies(size_ty count, const value_ty& val) {
2487 if (get_capacity() < count) {
2488 size_ty new_capacity = checked_calculate_new_capacity(count);
2489 ptr new_begin = unchecked_allocate(new_capacity);
2490
2491 try {
2492 uninitialized_fill(new_begin, unchecked_next(new_begin, count), val);
2493 } catch (...) {
2494 deallocate(new_begin, new_capacity);
2495 throw;
2496 }
2497
2498 reset_data(new_begin, new_capacity, count);
2499 } else if (get_size() < count) {
2500 std::fill(begin_ptr(), end_ptr(), val);
2501 uninitialized_fill(end_ptr(), unchecked_next(begin_ptr(), count), val);
2502 set_size(count);
2503 } else {
2504 erase_range(std::fill_n(begin_ptr(), count, val), end_ptr());
2505 }
2506 }
2507
2508 template <typename InputIt>
2509 requires std::is_assignable_v<value_ty&, decltype(*std::declval<InputIt>())>
2510 constexpr void assign_with_range(InputIt first, InputIt last,
2511 std::input_iterator_tag) {
2512 using iterator_cat =
2513 typename std::iterator_traits<InputIt>::iterator_category;
2514
2515 ptr curr = begin_ptr();
2516 for (; !(end_ptr() == curr || first == last);
2517 ++curr, static_cast<void>(++first)) {
2518 *curr = *first;
2519 }
2520
2521 if (first == last) {
2522 erase_to_end(curr);
2523 } else {
2524 append_range(first, last, iterator_cat{});
2525 }
2526 }
2527
2528 template <typename ForwardIt>
2529 requires std::is_assignable_v<value_ty&,
2530 decltype(*std::declval<ForwardIt>())>
2531 constexpr void assign_with_range(ForwardIt first, ForwardIt last,
2532 std::forward_iterator_tag) {
2533 const size_ty count = external_range_length(first, last);
2534 if (get_capacity() < count) {
2535 size_ty new_capacity = checked_calculate_new_capacity(count);
2536 ptr new_begin = unchecked_allocate(new_capacity);
2537
2538 try {
2539 uninitialized_copy(first, last, new_begin);
2540 } catch (...) {
2541 deallocate(new_begin, new_capacity);
2542 throw;
2543 }
2544
2545 reset_data(new_begin, new_capacity, count);
2546 } else if (get_size() < count) {
2547 ForwardIt pivot = copy_n_return_in(first, get_size(), begin_ptr());
2548 uninitialized_copy(pivot, last, end_ptr());
2549 set_size(count);
2550 } else {
2551 erase_range(copy_range(first, last, begin_ptr()), end_ptr());
2552 }
2553 }
2554
2555 template <typename InputIt>
2556 requires(
2557 !std::is_assignable_v<value_ty&, decltype(*std::declval<InputIt>())>)
2558 constexpr void assign_with_range(InputIt first, InputIt last,
2559 std::input_iterator_tag) {
2560 using iterator_cat =
2561 typename std::iterator_traits<InputIt>::iterator_category;
2562
2563 // If not assignable then destroy all elements and append.
2564 erase_all();
2565 append_range(first, last, iterator_cat{});
2566 }
2567
2568 // Ie. move-if-noexcept.
2570
2571 template <typename Policy = void, typename V = value_ty>
2572 requires is_explicitly_move_insertable_v<V> &&
2573 (!std::same_as<Policy, strong_exception_policy> ||
2574 relocate_with_move_v<V>)
2575 constexpr ptr uninitialized_move(ptr first, ptr last, ptr d_first) noexcept(
2576 std::is_nothrow_move_constructible_v<value_ty>) {
2577 return uninitialized_copy(std::make_move_iterator(first),
2578 std::make_move_iterator(last), d_first);
2579 }
2580
2581 template <typename Policy = void, typename V = value_ty>
2582 requires(!is_explicitly_move_insertable_v<V> ||
2583 (std::same_as<Policy, strong_exception_policy> &&
2584 !relocate_with_move_v<V>))
2585 constexpr ptr uninitialized_move(ptr first, ptr last, ptr d_first) noexcept(
2586 alloc_interface::template is_uninitialized_memcpyable_iterator_v<ptr>) {
2587 return uninitialized_copy(first, last, d_first);
2588 }
2589
2590 constexpr ptr shift_into_uninitialized(ptr pos, size_ty n_shift) {
2591 // Shift elements over to the right into uninitialized space.
2592 // Returns the start of the shifted range.
2593 // Precondition: shift < end_ptr () - pos
2594 assert(n_shift != 0 && "The value of `n_shift` should not be 0.");
2595
2596 const ptr original_end = end_ptr();
2597 const ptr pivot = unchecked_prev(original_end, n_shift);
2598
2599 uninitialized_move(pivot, original_end, original_end);
2600 increase_size(n_shift);
2601 return move_right(pos, pivot, original_end);
2602 }
2603
2604 template <typename... Args>
2605 constexpr ptr append_element(Args&&... args) {
2606 if (get_size() < get_capacity()) {
2607 return emplace_into_current_end(std::forward<Args>(args)...);
2608 }
2609 return emplace_into_reallocation_end(std::forward<Args>(args)...);
2610 }
2611
2612 constexpr ptr append_copies(size_ty count, const value_ty& val) {
2613 if (num_uninitialized() < count) {
2614 // Reallocate.
2615 if (get_max_size() - get_size() < count) {
2616 throw_allocation_size_error();
2617 }
2618
2619 size_ty original_size = get_size();
2620 size_ty new_size = get_size() + count;
2621
2622 // The check is handled by the if-guard.
2623 size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
2624 ptr new_data_ptr = unchecked_allocate(new_capacity, allocation_end_ptr());
2625 ptr new_last = unchecked_next(new_data_ptr, original_size);
2626
2627 try {
2628 new_last =
2629 uninitialized_fill(new_last, unchecked_next(new_last, count), val);
2630 uninitialized_move(begin_ptr(), end_ptr(), new_data_ptr);
2631 } catch (...) {
2632 destroy_range(unchecked_next(new_data_ptr, original_size), new_last);
2633 deallocate(new_data_ptr, new_capacity);
2634 throw;
2635 }
2636
2637 reset_data(new_data_ptr, new_capacity, new_size);
2638 return unchecked_next(new_data_ptr, original_size);
2639 } else {
2640 const ptr ret = end_ptr();
2641 uninitialized_fill(ret, unchecked_next(ret, count), val);
2642 increase_size(count);
2643 return ret;
2644 }
2645 }
2646
2647 template <std::same_as<strong_exception_policy> MovePolicy, typename InputIt>
2648 constexpr ptr append_range(InputIt first, InputIt last,
2649 std::input_iterator_tag) {
2650 // Append with a strong exception guarantee.
2651 size_ty original_size = get_size();
2652 for (; !(first == last); ++first) {
2653 try {
2654 append_element(*first);
2655 } catch (...) {
2656 erase_range(unchecked_next(begin_ptr(), original_size), end_ptr());
2657 throw;
2658 }
2659 }
2660 return unchecked_next(begin_ptr(), original_size);
2661 }
2662
2663 template <typename MovePolicy = void, typename InputIt>
2664 requires(!std::same_as<MovePolicy, strong_exception_policy>)
2665 constexpr ptr append_range(InputIt first, InputIt last,
2666 std::input_iterator_tag) {
2667 size_ty original_size = get_size();
2668 for (; !(first == last); ++first) {
2669 append_element(*first);
2670 }
2671 return unchecked_next(begin_ptr(), original_size);
2672 }
2673
2674 template <typename MovePolicy = void, typename ForwardIt>
2675 constexpr ptr append_range(ForwardIt first, ForwardIt last,
2676 std::forward_iterator_tag) {
2677 const size_ty num_insert = external_range_length(first, last);
2678
2679 if (num_uninitialized() < num_insert) {
2680 // Reallocate.
2681 if (get_max_size() - get_size() < num_insert) {
2682 throw_allocation_size_error();
2683 }
2684
2685 size_ty original_size = get_size();
2686 size_ty new_size = get_size() + num_insert;
2687
2688 // The check is handled by the if-guard.
2689 size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
2690 ptr new_data_ptr = unchecked_allocate(new_capacity, allocation_end_ptr());
2691 ptr new_last = unchecked_next(new_data_ptr, original_size);
2692
2693 try {
2694 new_last = uninitialized_copy(first, last, new_last);
2695 uninitialized_move<MovePolicy>(begin_ptr(), end_ptr(), new_data_ptr);
2696 } catch (...) {
2697 destroy_range(unchecked_next(new_data_ptr, original_size), new_last);
2698 deallocate(new_data_ptr, new_capacity);
2699 throw;
2700 }
2701
2702 reset_data(new_data_ptr, new_capacity, new_size);
2703 return unchecked_next(new_data_ptr, original_size);
2704 } else {
2705 ptr ret = end_ptr();
2706 uninitialized_copy(first, last, ret);
2707 increase_size(num_insert);
2708 return ret;
2709 }
2710 }
2711
2712 template <typename... Args>
2713 constexpr ptr emplace_at(ptr pos, Args&&... args) {
2714 assert(get_size() <= get_capacity() && "size was greater than capacity");
2715
2716 if (get_size() < get_capacity()) {
2717 return emplace_into_current(pos, std::forward<Args>(args)...);
2718 }
2719 return emplace_into_reallocation(pos, std::forward<Args>(args)...);
2720 }
2721
2722 constexpr ptr insert_copies(ptr pos, size_ty count, const value_ty& val) {
2723 if (0 == count) {
2724 return pos;
2725 }
2726
2727 if (end_ptr() == pos) {
2728 if (1 == count) {
2729 return append_element(val);
2730 }
2731 return append_copies(count, val);
2732 }
2733
2734 if (num_uninitialized() < count) {
2735 // Reallocate.
2736 if (get_max_size() - get_size() < count) {
2737 throw_allocation_size_error();
2738 }
2739
2740 const size_ty offset = internal_range_length(begin_ptr(), pos);
2741
2742 const size_ty new_size = get_size() + count;
2743
2744 // The check is handled by the if-guard.
2745 const size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
2746 ptr new_data_ptr = unchecked_allocate(new_capacity, allocation_end_ptr());
2747 ptr new_first = unchecked_next(new_data_ptr, offset);
2748 ptr new_last = new_first;
2749
2750 try {
2751 uninitialized_fill(new_first, unchecked_next(new_first, count), val);
2752 unchecked_advance(new_last, count);
2753
2754 uninitialized_move(begin_ptr(), pos, new_data_ptr);
2755 new_first = new_data_ptr;
2756 uninitialized_move(pos, end_ptr(), new_last);
2757 } catch (...) {
2758 destroy_range(new_first, new_last);
2759 deallocate(new_data_ptr, new_capacity);
2760 throw;
2761 }
2762
2763 reset_data(new_data_ptr, new_capacity, new_size);
2764 return unchecked_next(begin_ptr(), offset);
2765 } else {
2766 // If we have fewer to insert than tailing elements after `pos`, we shift
2767 // into uninitialized and then copy over.
2768
2769 const size_ty tail_size = internal_range_length(pos, end_ptr());
2770 if (tail_size < count) {
2771 // The number inserted is larger than the number after `pos`,
2772 // so part of the input will be used to construct new elements,
2773 // and another part of it will assign existing ones.
2774 // In order:
2775 // Construct new elements immediately after end_ptr () using the
2776 // input. Move-construct existing elements over to the tail. Assign
2777 // existing elements using the input.
2778
2779 ptr original_end = end_ptr();
2780
2781 // Place a portion of the input into the uninitialized section.
2782 size_ty num_val_tail = count - tail_size;
2783
2784 if (std::is_constant_evaluated()) {
2785 uninitialized_fill(end_ptr(), unchecked_next(end_ptr(), num_val_tail),
2786 val);
2787 increase_size(num_val_tail);
2788
2789 const heap_temporary tmp(*this, val);
2790
2791 uninitialized_move(pos, original_end, end_ptr());
2792 increase_size(tail_size);
2793
2794 std::fill_n(pos, tail_size, tmp.get());
2795
2796 return pos;
2797 }
2798
2799 uninitialized_fill(end_ptr(), unchecked_next(end_ptr(), num_val_tail),
2800 val);
2801 increase_size(num_val_tail);
2802
2803 try {
2804 // We need to handle possible aliasing here.
2805 const stack_temporary tmp(*this, val);
2806
2807 // Now, move the tail to the end.
2808 uninitialized_move(pos, original_end, end_ptr());
2809 increase_size(tail_size);
2810
2811 try {
2812 // Finally, try to copy the rest of the elements over.
2813 std::fill_n(pos, tail_size, tmp.get());
2814 } catch (...) {
2815 // Attempt to roll back and destroy the tail if we fail.
2816 ptr inserted_end = unchecked_prev(end_ptr(), tail_size);
2817 move_left(inserted_end, end_ptr(), pos);
2818 destroy_range(inserted_end, end_ptr());
2819 decrease_size(tail_size);
2820 throw;
2821 }
2822 } catch (...) {
2823 // Destroy the elements constructed from the input.
2824 destroy_range(original_end, end_ptr());
2825 decrease_size(internal_range_length(original_end, end_ptr()));
2826 throw;
2827 }
2828 } else {
2829 if (std::is_constant_evaluated()) {
2830 const heap_temporary tmp(*this, val);
2831
2832 ptr inserted_end = shift_into_uninitialized(pos, count);
2833 std::fill(pos, inserted_end, tmp.get());
2834
2835 return pos;
2836 }
2837 const stack_temporary tmp(*this, val);
2838
2839 ptr inserted_end = shift_into_uninitialized(pos, count);
2840
2841 // Attempt to copy over the elements.
2842 // If we fail we'll attempt a full roll-back.
2843 try {
2844 std::fill(pos, inserted_end, tmp.get());
2845 } catch (...) {
2846 ptr original_end = move_left(inserted_end, end_ptr(), pos);
2847 destroy_range(original_end, end_ptr());
2848 decrease_size(count);
2849 throw;
2850 }
2851 }
2852 return pos;
2853 }
2854 }
2855
2856 template <typename ForwardIt>
2857 constexpr ptr insert_range_helper(ptr pos, ForwardIt first, ForwardIt last) {
2858 assert(!(first == last) && "The range should not be empty.");
2859 assert(!(end_ptr() == pos) && "`pos` should not be at the end.");
2860
2861 const size_ty num_insert = external_range_length(first, last);
2862 if (num_uninitialized() < num_insert) {
2863 // Reallocate.
2864 if (get_max_size() - get_size() < num_insert) {
2865 throw_allocation_size_error();
2866 }
2867
2868 const size_ty offset = internal_range_length(begin_ptr(), pos);
2869 const size_ty new_size = get_size() + num_insert;
2870
2871 // The check is handled by the if-guard.
2872 const size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
2873 const ptr new_data_ptr =
2874 unchecked_allocate(new_capacity, allocation_end_ptr());
2875 ptr new_first = unchecked_next(new_data_ptr, offset);
2876 ptr new_last = new_first;
2877
2878 try {
2879 uninitialized_copy(first, last, new_first);
2880 unchecked_advance(new_last, num_insert);
2881
2882 uninitialized_move(begin_ptr(), pos, new_data_ptr);
2883 new_first = new_data_ptr;
2884 uninitialized_move(pos, end_ptr(), new_last);
2885 } catch (...) {
2886 destroy_range(new_first, new_last);
2887 deallocate(new_data_ptr, new_capacity);
2888 throw;
2889 }
2890
2891 reset_data(new_data_ptr, new_capacity, new_size);
2892 return unchecked_next(begin_ptr(), offset);
2893 } else {
2894 // if we have fewer to insert than tailing elements after
2895 // `pos` we shift into uninitialized and then copy over
2896 const size_ty tail_size = internal_range_length(pos, end_ptr());
2897 if (tail_size < num_insert) {
2898 // Use the same method as insert_copies.
2899 ptr original_end = end_ptr();
2900 ForwardIt pivot = unchecked_next(first, tail_size);
2901
2902 // Place a portion of the input into the uninitialized section.
2903 uninitialized_copy(pivot, last, end_ptr());
2904 increase_size(num_insert - tail_size);
2905
2906 try {
2907 // Now move the tail to the end.
2908 uninitialized_move(pos, original_end, end_ptr());
2909 increase_size(tail_size);
2910
2911 try {
2912 // Finally, try to copy the rest of the elements over.
2913 copy_range(first, pivot, pos);
2914 } catch (...) {
2915 // Attempt to roll back and destroy the tail if we fail.
2916 ptr inserted_end = unchecked_prev(end_ptr(), tail_size);
2917 move_left(inserted_end, end_ptr(), pos);
2918 destroy_range(inserted_end, end_ptr());
2919 decrease_size(tail_size);
2920 throw;
2921 }
2922 } catch (...) {
2923 // If we throw, destroy the first copy we made.
2924 destroy_range(original_end, end_ptr());
2925 decrease_size(internal_range_length(original_end, end_ptr()));
2926 throw;
2927 }
2928 } else {
2929 shift_into_uninitialized(pos, num_insert);
2930
2931 // Attempt to copy over the elements.
2932 // If we fail we'll attempt a full roll-back.
2933 try {
2934 copy_range(first, last, pos);
2935 } catch (...) {
2936 ptr inserted_end = unchecked_next(pos, num_insert);
2937 ptr original_end = move_left(inserted_end, end_ptr(), pos);
2938 destroy_range(original_end, end_ptr());
2939 decrease_size(num_insert);
2940 throw;
2941 }
2942 }
2943 return pos;
2944 }
2945 }
2946
2947 template <typename InputIt>
2948 constexpr ptr insert_range(ptr pos, InputIt first, InputIt last,
2949 std::input_iterator_tag) {
2950 assert(!(first == last) && "The range should not be empty.");
2951
2952 // Ensure we use this specific overload to give a strong exception guarantee
2953 // for 1 element.
2954 if (end_ptr() == pos) {
2955 return append_range(first, last, std::input_iterator_tag{});
2956 }
2957
2958 using iterator_cat =
2959 typename std::iterator_traits<InputIt>::iterator_category;
2960 small_vector_base tmp(first, last, iterator_cat{}, allocator_ref());
2961
2962 return insert_range_helper(pos, std::make_move_iterator(tmp.begin_ptr()),
2963 std::make_move_iterator(tmp.end_ptr()));
2964 }
2965
2966 template <typename ForwardIt>
2967 constexpr ptr insert_range(ptr pos, ForwardIt first, ForwardIt last,
2968 std::forward_iterator_tag) {
2969 if (!(end_ptr() == pos)) {
2970 return insert_range_helper(pos, first, last);
2971 }
2972
2973 if (unchecked_next(first) == last) {
2974 return append_element(*first);
2975 }
2976
2977 using iterator_cat =
2978 typename std::iterator_traits<ForwardIt>::iterator_category;
2979 return append_range(first, last, iterator_cat{});
2980 }
2981
2982 template <typename... Args>
2983 constexpr ptr emplace_into_current_end(Args&&... args) {
2984 construct(end_ptr(), std::forward<Args>(args)...);
2985 increase_size(1);
2986 return unchecked_prev(end_ptr());
2987 }
2988
2989 template <typename V = value_ty>
2990 requires std::is_nothrow_move_constructible_v<V>
2991 constexpr ptr emplace_into_current(ptr pos, value_ty&& val) {
2992 if (pos == end_ptr()) {
2993 return emplace_into_current_end(std::move(val));
2994 }
2995
2996 // In the special case of value_ty&& we don't make a copy because behavior
2997 // is unspecified when it is an internal element. Hence, we'll take the
2998 // opportunity to optimize and assume that it isn't an internal element.
2999 shift_into_uninitialized(pos, 1);
3000 destroy(pos);
3001 construct(pos, std::move(val));
3002 return pos;
3003 }
3004
3005 template <typename... Args>
3006 constexpr ptr emplace_into_current(ptr pos, Args&&... args) {
3007 if (pos == end_ptr()) {
3008 return emplace_into_current_end(std::forward<Args>(args)...);
3009 }
3010
3011 if (std::is_constant_evaluated()) {
3012 heap_temporary tmp(*this, std::forward<Args>(args)...);
3013 shift_into_uninitialized(pos, 1);
3014 *pos = tmp.release();
3015 return pos;
3016 }
3017
3018 // This is necessary because of possible aliasing.
3019 stack_temporary tmp(*this, std::forward<Args>(args)...);
3020 shift_into_uninitialized(pos, 1);
3021 *pos = tmp.release();
3022 return pos;
3023 }
3024
3025 template <typename... Args>
3026 constexpr ptr emplace_into_reallocation_end(Args&&... args) {
3027 // Appending; strong exception guarantee.
3028 if (get_max_size() == get_size()) {
3029 throw_allocation_size_error();
3030 }
3031
3032 const size_ty new_size = get_size() + 1;
3033
3034 // The check is handled by the if-guard.
3035 const size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
3036 const ptr new_data_ptr =
3037 unchecked_allocate(new_capacity, allocation_end_ptr());
3038 const ptr emplace_pos = unchecked_next(new_data_ptr, get_size());
3039
3040 try {
3041 construct(emplace_pos, std::forward<Args>(args)...);
3042 try {
3043 uninitialized_move<strong_exception_policy>(begin_ptr(), end_ptr(),
3044 new_data_ptr);
3045 } catch (...) {
3046 destroy(emplace_pos);
3047 throw;
3048 }
3049 } catch (...) {
3050 deallocate(new_data_ptr, new_capacity);
3051 throw;
3052 }
3053
3054 reset_data(new_data_ptr, new_capacity, new_size);
3055 return emplace_pos;
3056 }
3057
3058 template <typename... Args>
3059 constexpr ptr emplace_into_reallocation(ptr pos, Args&&... args) {
3060 const size_ty offset = internal_range_length(begin_ptr(), pos);
3061 if (offset == get_size()) {
3062 return emplace_into_reallocation_end(std::forward<Args>(args)...);
3063 }
3064
3065 if (get_max_size() == get_size()) {
3066 throw_allocation_size_error();
3067 }
3068
3069 const size_ty new_size = get_size() + 1;
3070
3071 // The check is handled by the if-guard.
3072 const size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
3073 const ptr new_data_ptr =
3074 unchecked_allocate(new_capacity, allocation_end_ptr());
3075 ptr new_first = unchecked_next(new_data_ptr, offset);
3076 ptr new_last = new_first;
3077
3078 try {
3079 construct(new_first, std::forward<Args>(args)...);
3080 unchecked_advance(new_last, 1);
3081
3082 uninitialized_move(begin_ptr(), pos, new_data_ptr);
3083 new_first = new_data_ptr;
3084 uninitialized_move(pos, end_ptr(), new_last);
3085 } catch (...) {
3086 destroy_range(new_first, new_last);
3087 deallocate(new_data_ptr, new_capacity);
3088 throw;
3089 }
3090
3091 reset_data(new_data_ptr, new_capacity, new_size);
3092 return unchecked_next(begin_ptr(), offset);
3093 }
3094
3095 constexpr ptr shrink_to_size() {
3096 if (!has_allocation() || get_size() == get_capacity()) {
3097 return begin_ptr();
3098 }
3099
3100 // The rest runs only if allocated.
3101
3102 size_ty new_capacity;
3103 ptr new_data_ptr;
3104
3105 if (InlineCapacity < get_size()) {
3106 new_capacity = get_size();
3107 new_data_ptr = unchecked_allocate(new_capacity, allocation_end_ptr());
3108 } else {
3109 // We move to inline storage.
3110 new_capacity = InlineCapacity;
3111 if (std::is_constant_evaluated()) {
3112 new_data_ptr = alloc_interface::allocate(InlineCapacity);
3113 } else {
3114 new_data_ptr = storage_ptr();
3115 }
3116 }
3117
3118 uninitialized_move(begin_ptr(), end_ptr(), new_data_ptr);
3119
3120 destroy_range(begin_ptr(), end_ptr());
3121 deallocate(data_ptr(), get_capacity());
3122
3123 set_data_ptr(new_data_ptr);
3124 set_capacity(new_capacity);
3125
3126 return begin_ptr();
3127 }
3128
3129 template <typename... ValueT>
3130 constexpr void resize_with(size_ty new_size, const ValueT&... val) {
3131 // ValueT... should either be value_ty or empty.
3132
3133 if (new_size == 0) {
3134 erase_all();
3135 }
3136
3137 if (get_capacity() < new_size) {
3138 // Reallocate.
3139
3140 if (get_max_size() < new_size) {
3141 throw_allocation_size_error();
3142 }
3143
3144 const size_ty original_size = get_size();
3145
3146 // The check is handled by the if-guard.
3147 const size_ty new_capacity = unchecked_calculate_new_capacity(new_size);
3148 ptr new_data_ptr = unchecked_allocate(new_capacity, allocation_end_ptr());
3149 ptr new_last = unchecked_next(new_data_ptr, original_size);
3150
3151 try {
3152 new_last = uninitialized_fill(
3153 new_last, unchecked_next(new_data_ptr, new_size), val...);
3154
3155 // Strong exception guarantee.
3156 uninitialized_move<strong_exception_policy>(begin_ptr(), end_ptr(),
3157 new_data_ptr);
3158 } catch (...) {
3159 destroy_range(unchecked_next(new_data_ptr, original_size), new_last);
3160 deallocate(new_data_ptr, new_capacity);
3161 throw;
3162 }
3163
3164 reset_data(new_data_ptr, new_capacity, new_size);
3165 } else if (get_size() < new_size) {
3166 // Construct in the uninitialized section.
3167 uninitialized_fill(end_ptr(), unchecked_next(begin_ptr(), new_size),
3168 val...);
3169 set_size(new_size);
3170 } else {
3171 erase_range(unchecked_next(begin_ptr(), new_size), end_ptr());
3172 }
3173
3174 // Do nothing if the count is the same as the current size.
3175 }
3176
3177 constexpr void request_capacity(size_ty request) {
3178 if (request <= get_capacity()) {
3179 return;
3180 }
3181
3182 size_ty new_capacity = checked_calculate_new_capacity(request);
3183 ptr new_begin = unchecked_allocate(new_capacity);
3184
3185 try {
3186 uninitialized_move<strong_exception_policy>(begin_ptr(), end_ptr(),
3187 new_begin);
3188 } catch (...) {
3189 deallocate(new_begin, new_capacity);
3190 throw;
3191 }
3192
3193 wipe();
3194
3195 set_data_ptr(new_begin);
3196 set_capacity(new_capacity);
3197 }
3198
3199 constexpr ptr erase_at(ptr pos) {
3200 move_left(unchecked_next(pos), end_ptr(), pos);
3201 erase_last();
3202 return pos;
3203 }
3204
3205 constexpr void erase_last() {
3206 decrease_size(1);
3207
3208 // The element located at end_ptr is still alive since the size decreased.
3209 destroy(end_ptr());
3210 }
3211
3212 constexpr ptr erase_range(ptr first, ptr last) {
3213 if (!(first == last)) {
3214 erase_to_end(move_left(last, end_ptr(), first));
3215 }
3216 return first;
3217 }
3218
3219 constexpr void erase_to_end(ptr pos) {
3220 assert(0 <= (end_ptr() - pos) && "`pos` was in the uninitialized range");
3221 if (size_ty change = internal_range_length(pos, end_ptr())) {
3222 decrease_size(change);
3223 destroy_range(pos, unchecked_next(pos, change));
3224 }
3225 }
3226
3227 constexpr void erase_all() {
3228 ptr curr_end = end_ptr();
3229 set_size(0);
3230 destroy_range(begin_ptr(), curr_end);
3231 }
3232
3233 constexpr void swap_elements(small_vector_base& other) noexcept(
3234 std::is_nothrow_move_constructible_v<value_ty> &&
3235 std::is_nothrow_swappable_v<value_ty>) {
3236 assert(get_size() <= other.get_size());
3237
3238 const ptr other_tail =
3239 std::swap_ranges(begin_ptr(), end_ptr(), other.begin_ptr());
3240 uninitialized_move(other_tail, other.end_ptr(), end_ptr());
3241 destroy_range(other_tail, other.end_ptr());
3242
3243 swap_size(other);
3244 }
3245
3246 constexpr void swap_default(small_vector_base& other) noexcept(
3247 std::is_nothrow_move_constructible_v<value_ty> &&
3248 std::is_nothrow_swappable_v<value_ty>) {
3249 // This function is used when:
3250 // We are using the standard allocator.
3251 // The allocators propagate and are equal.
3252 // The allocators are always equal.
3253 // The allocators do not propagate and are equal.
3254 // The allocators propagate and are not equal.
3255
3256 // Not handled:
3257 // The allocators do not propagate and are not equal.
3258
3259 assert(get_capacity() <= other.get_capacity());
3260
3261 if (has_allocation()) { // Implies that `other` also has an allocation.
3262 swap_allocation(other);
3263 } else if (other.has_allocation()) {
3264 // Note: This will never be constant evaluated because both are always
3265 // allocated.
3266 uninitialized_move(begin_ptr(), end_ptr(), other.storage_ptr());
3267 destroy_range(begin_ptr(), end_ptr());
3268
3269 set_data_ptr(other.data_ptr());
3270 set_capacity(other.get_capacity());
3271
3272 other.set_data_ptr(other.storage_ptr());
3273 other.set_capacity(InlineCapacity);
3274
3275 swap_size(other);
3276 } else if (get_size() < other.get_size()) {
3277 swap_elements(other);
3278 } else {
3279 other.swap_elements(*this);
3280 }
3281
3282 alloc_interface::swap(other);
3283 }
3284
3285 constexpr void swap_unequal_no_propagate(small_vector_base& other) {
3286 assert(get_capacity() <= other.get_capacity());
3287
3288 if (get_capacity() < other.get_size()) {
3289 // Reallocation required.
3290 // We should always be able to reuse the allocation of `other`.
3291 const size_ty new_capacity =
3292 unchecked_calculate_new_capacity(other.get_size());
3293 const ptr new_data_ptr = unchecked_allocate(new_capacity, end_ptr());
3294
3295 try {
3296 uninitialized_move(other.begin_ptr(), other.end_ptr(), new_data_ptr);
3297 try {
3298 destroy_range(std::move(begin_ptr(), end_ptr(), other.begin_ptr()),
3299 other.end_ptr());
3300 } catch (...) {
3301 destroy_range(new_data_ptr,
3302 unchecked_next(new_data_ptr, other.get_size()));
3303 throw;
3304 }
3305 } catch (...) {
3306 deallocate(new_data_ptr, new_capacity);
3307 throw;
3308 }
3309
3310 destroy_range(begin_ptr(), end_ptr());
3311 if (has_allocation()) {
3312 deallocate(data_ptr(), get_capacity());
3313 }
3314
3315 set_data_ptr(new_data_ptr);
3316 set_capacity(new_capacity);
3317 swap_size(other);
3318 } else if (get_size() < other.get_size()) {
3319 swap_elements(other);
3320 } else {
3321 other.swap_elements(*this);
3322 }
3323
3324 // This should have no effect.
3325 alloc_interface::swap(other);
3326 }
3327
3328 template <typename A = alloc_ty>
3329 requires allocations_are_swappable_v<A> && (InlineCapacity == 0)
3330 constexpr void swap(small_vector_base& other) noexcept {
3331 swap_allocation(other);
3332 alloc_interface::swap(other);
3333 }
3334
3335 template <typename A = alloc_ty>
3336 requires allocations_are_swappable_v<A> && (InlineCapacity != 0)
3337 constexpr void swap(small_vector_base& other) noexcept(
3338 std::is_nothrow_move_constructible_v<value_ty> &&
3339 std::is_nothrow_swappable_v<value_ty>) {
3340 if (get_capacity() < other.get_capacity()) {
3341 swap_default(other);
3342 } else {
3343 other.swap_default(*this);
3344 }
3345 }
3346
3347 template <typename A = alloc_ty>
3348 requires(!allocations_are_swappable_v<A>)
3349 constexpr void swap(small_vector_base& other) {
3350 if (get_capacity() < other.get_capacity()) {
3351 if (other.allocator_ref() == allocator_ref()) {
3352 swap_default(other);
3353 } else {
3354 swap_unequal_no_propagate(other);
3355 }
3356 } else {
3357 if (other.allocator_ref() == allocator_ref()) {
3358 other.swap_default(*this);
3359 } else {
3360 other.swap_unequal_no_propagate(*this);
3361 }
3362 }
3363 }
3364
3365#ifdef __GLIBCXX__
3366
3367 // These are compatibility fixes for libstdc++ because std::copy doesn't work
3368 // for `move_iterator`s when constant evaluated.
3369
3370 template <typename InputIt>
3371 static constexpr InputIt unmove_iterator(InputIt it) {
3372 return it;
3373 }
3374
3375 template <typename InputIt>
3376 static constexpr auto unmove_iterator(std::move_iterator<InputIt> it)
3377 -> decltype(unmove_iterator(it.base())) {
3378 return unmove_iterator(it.base());
3379 }
3380
3381 template <typename InputIt>
3382 static constexpr auto unmove_iterator(std::reverse_iterator<InputIt> it)
3383 -> std::reverse_iterator<decltype(unmove_iterator(it.base()))> {
3384 return std::reverse_iterator<decltype(unmove_iterator(it.base()))>(
3385 unmove_iterator(it.base()));
3386 }
3387
3388#endif
3389
3390 template <typename InputIt>
3391 constexpr ptr copy_range(InputIt first, InputIt last, ptr dest) {
3392#ifdef __GLIBCXX__
3393 if (std::is_constant_evaluated()) {
3394 if constexpr (!std::is_same_v<decltype(unmove_iterator(
3395 std::declval<InputIt>())),
3396 InputIt>) {
3397 return std::move(unmove_iterator(first), unmove_iterator(last), dest);
3398 }
3399 }
3400#endif
3401
3402 return std::copy(first, last, dest);
3403 }
3404
3405 template <typename InputIt>
3406 requires is_memcpyable_iterator_v<InputIt>
3407 constexpr InputIt copy_n_return_in(InputIt first, size_ty count,
3408 ptr dest) noexcept {
3409 if (std::is_constant_evaluated()) {
3410 std::copy_n(first, count, dest);
3411 return unchecked_next(first, count);
3412 }
3413
3414 if (count != 0) {
3415 std::memcpy(to_address(dest), to_address(first),
3416 count * sizeof(value_ty));
3417 }
3418 // Note: The unsafe cast here should be proven to be safe in the caller
3419 // function.
3420 return unchecked_next(first, count);
3421 }
3422
3423 template <typename InputIt>
3424 requires is_memcpyable_iterator_v<InputIt>
3425 constexpr std::move_iterator<InputIt> copy_n_return_in(
3426 std::move_iterator<InputIt> first, size_ty count, ptr dest) noexcept {
3427 return std::move_iterator<InputIt>(
3428 copy_n_return_in(first.base(), count, dest));
3429 }
3430
3431 template <typename RandomIt>
3432 requires(!is_memcpyable_iterator_v<RandomIt> &&
3433 std::is_base_of_v<
3434 std::random_access_iterator_tag,
3435 typename std::iterator_traits<RandomIt>::iterator_category>)
3436 constexpr RandomIt copy_n_return_in(RandomIt first, size_ty count, ptr dest) {
3437#ifdef __GLIBCXX__
3438 if (std::is_constant_evaluated()) {
3439 if constexpr (!std::is_same_v<decltype(unmove_iterator(
3440 std::declval<RandomIt>())),
3441 RandomIt>) {
3442 auto bfirst = unmove_iterator(first);
3443 auto blast = unchecked_next(bfirst, count);
3444 std::move(bfirst, blast, dest);
3445 return unchecked_next(first, count);
3446 }
3447 }
3448#endif
3449
3450 std::copy_n(first, count, dest);
3451 // Note: This unsafe cast should be proven safe in the caller function.
3452 return unchecked_next(first, count);
3453 }
3454
3455 template <typename InputIt>
3456 requires(!is_memcpyable_iterator_v<InputIt> &&
3457 !std::is_base_of_v<
3458 std::random_access_iterator_tag,
3459 typename std::iterator_traits<InputIt>::iterator_category>)
3460 constexpr InputIt copy_n_return_in(InputIt first, size_ty count, ptr dest) {
3461 for (; count != 0;
3462 --count, static_cast<void>(++dest), static_cast<void>(++first)) {
3463 *dest = *first;
3464 }
3465 return first;
3466 }
3467
3468 template <typename V = value_ty>
3469 requires is_memcpyable_v<V>
3470 constexpr ptr move_left(ptr first, ptr last, ptr d_first) {
3471 // Shift initialized elements to the left.
3472
3473 if (std::is_constant_evaluated()) {
3474 return std::move(first, last, d_first);
3475 }
3476
3477 const size_ty num_moved = internal_range_length(first, last);
3478 if (num_moved != 0) {
3479 std::memmove(to_address(d_first), to_address(first),
3480 num_moved * sizeof(value_ty));
3481 }
3482 return unchecked_next(d_first, num_moved);
3483 }
3484
3485 template <typename V = value_ty>
3486 requires(!is_memcpyable_v<V>)
3487 constexpr ptr move_left(ptr first, ptr last, ptr d_first) {
3488 // Shift initialized elements to the left.
3489 return std::move(first, last, d_first);
3490 }
3491
3492 template <typename V = value_ty>
3493 requires is_memcpyable_v<V>
3494 constexpr ptr move_right(ptr first, ptr last, ptr d_last) {
3495 // Move initialized elements to the right.
3496
3497 if (std::is_constant_evaluated()) {
3498 return std::move_backward(first, last, d_last);
3499 }
3500
3501 const size_ty num_moved = internal_range_length(first, last);
3502 const ptr dest = unchecked_prev(d_last, num_moved);
3503 if (num_moved != 0) {
3504 std::memmove(to_address(dest), to_address(first),
3505 num_moved * sizeof(value_ty));
3506 }
3507 return dest;
3508 }
3509
3510 template <typename V = value_ty>
3511 requires(!is_memcpyable_v<V>)
3512 constexpr ptr move_right(ptr first, ptr last, ptr d_last) {
3513 // move initialized elements to the right
3514 // n should not be 0
3515 return std::move_backward(first, last, d_last);
3516 }
3517
3518 public:
3519 constexpr void set_default() {
3520 set_to_inline_storage();
3521 set_size(0);
3522 }
3523
3524 [[nodiscard]]
3525 constexpr ptr data_ptr() noexcept {
3526 return m_data.data_ptr();
3527 }
3528
3529 [[nodiscard]]
3530 constexpr cptr data_ptr() const noexcept {
3531 return m_data.data_ptr();
3532 }
3533
3534 [[nodiscard]]
3535 constexpr size_ty get_capacity() const noexcept {
3536 return m_data.capacity();
3537 }
3538
3539 [[nodiscard]]
3540 constexpr size_ty get_size() const noexcept {
3541 return m_data.size();
3542 }
3543
3544 [[nodiscard]]
3545 constexpr size_ty num_uninitialized() const noexcept {
3546 return get_capacity() - get_size();
3547 }
3548
3549 [[nodiscard]]
3550 constexpr ptr begin_ptr() noexcept {
3551 return data_ptr();
3552 }
3553
3554 [[nodiscard]]
3555 constexpr cptr begin_ptr() const noexcept {
3556 return data_ptr();
3557 }
3558
3559 [[nodiscard]]
3560 constexpr ptr end_ptr() noexcept {
3561 return unchecked_next(begin_ptr(), get_size());
3562 }
3563
3564 [[nodiscard]]
3565 constexpr cptr end_ptr() const noexcept {
3566 return unchecked_next(begin_ptr(), get_size());
3567 }
3568
3569 [[nodiscard]]
3570 constexpr ptr allocation_end_ptr() noexcept {
3571 return unchecked_next(begin_ptr(), get_capacity());
3572 }
3573
3574 [[nodiscard]]
3575 constexpr cptr allocation_end_ptr() const noexcept {
3576 return unchecked_next(begin_ptr(), get_capacity());
3577 }
3578
3579 [[nodiscard]]
3580 constexpr alloc_ty copy_allocator() const noexcept {
3581 return alloc_ty(allocator_ref());
3582 }
3583
3584 [[nodiscard]]
3585 constexpr ptr storage_ptr() noexcept {
3586 return m_data.storage();
3587 }
3588
3589 [[nodiscard]]
3590 constexpr cptr storage_ptr() const noexcept {
3591 return m_data.storage();
3592 }
3593
3594 [[nodiscard]]
3595 constexpr bool has_allocation() const noexcept {
3596 if (std::is_constant_evaluated()) {
3597 return true;
3598 }
3599 return InlineCapacity < get_capacity();
3600 }
3601
3602 [[nodiscard]]
3603 constexpr bool is_inlinable() const noexcept {
3604 return get_size() <= InlineCapacity;
3605 }
3606
3607 private:
3608 small_vector_data<ptr, size_type, value_ty, InlineCapacity> m_data;
3609};
3610
3611} // namespace detail
3612
3613template <typename T, unsigned InlineCapacity, typename Allocator>
3614 requires concepts::small_vector::AllocatorFor<Allocator, T>
3616 : private detail::small_vector_base<Allocator, InlineCapacity> {
3617 using base = detail::small_vector_base<Allocator, InlineCapacity>;
3618
3619 public:
3620 static_assert(std::is_same_v<T, typename Allocator::value_type>,
3621 "`Allocator::value_type` must be the same as `T`.");
3622
3623 template <typename SameT, unsigned DifferentInlineCapacity,
3624 typename SameAllocator>
3626 friend class small_vector;
3627
3628 using value_type = T;
3629 using allocator_type = Allocator;
3630 using size_type = typename base::size_type;
3631 using difference_type = typename base::difference_type;
3634 using pointer = typename std::allocator_traits<allocator_type>::pointer;
3636 typename std::allocator_traits<allocator_type>::const_pointer;
3637
3640 using reverse_iterator = std::reverse_iterator<iterator>;
3641 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
3642
3643 static_assert(InlineCapacity <= (std::numeric_limits<size_type>::max)(),
3644 "InlineCapacity must be less than or equal to the maximum "
3645 "value of size_type.");
3646
3647 static constexpr unsigned inline_capacity_v = InlineCapacity;
3648
3649 private:
3650 static constexpr bool Destructible =
3652
3653 static constexpr bool MoveAssignable =
3655
3656 static constexpr bool CopyAssignable =
3658
3659 static constexpr bool MoveConstructible =
3661
3662 static constexpr bool CopyConstructible =
3664
3665 static constexpr bool Swappable =
3667
3668 static constexpr bool DefaultInsertable =
3671
3672 static constexpr bool MoveInsertable =
3675
3676 static constexpr bool CopyInsertable =
3679
3680 static constexpr bool Erasable =
3683
3684 template <typename... Args>
3685 struct EmplaceConstructible {
3686 static constexpr bool value =
3688 allocator_type, Args...>;
3689 };
3690
3691 public:
3692 constexpr small_vector() noexcept(noexcept(allocator_type()))
3693 requires concepts::DefaultConstructible<allocator_type>
3694 = default;
3695
3696 constexpr small_vector(const small_vector& other)
3697 requires CopyInsertable
3698 : base(base::bypass, other) {}
3699
3700 constexpr small_vector(small_vector&& other) noexcept(
3701 std::is_nothrow_move_constructible_v<value_type> || InlineCapacity == 0)
3702 requires MoveInsertable
3703 : base(base::bypass, std::move(other)) {}
3704
3705 constexpr explicit small_vector(const allocator_type& alloc) noexcept
3706 : base(alloc) {}
3707
3708 constexpr small_vector(const small_vector& other, const allocator_type& alloc)
3709 requires CopyInsertable
3710 : base(base::bypass, other, alloc) {}
3711
3712 constexpr small_vector(small_vector&& other, const allocator_type& alloc)
3713 requires MoveInsertable
3714 : base(base::bypass, std::move(other), alloc) {}
3715
3716 constexpr explicit small_vector(
3717 size_type count, const allocator_type& alloc = allocator_type())
3718 requires DefaultInsertable
3719 : base(count, alloc) {}
3720
3722 const allocator_type& alloc = allocator_type())
3723 requires CopyInsertable
3724 : base(count, value, alloc) {}
3725
3726 template <typename Generator>
3727 requires std::invocable<Generator&> &&
3728 EmplaceConstructible<std::invoke_result_t<Generator&>>::value
3729 constexpr small_vector(size_type count, Generator g,
3730 const allocator_type& alloc = allocator_type())
3731 : base(count, g, alloc) {}
3732
3733 template <std::input_iterator InputIt>
3734 requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value &&
3735 (std::forward_iterator<InputIt> || MoveInsertable)
3736 constexpr small_vector(InputIt first, InputIt last,
3737 const allocator_type& alloc = allocator_type())
3738 : base(first, last,
3739 typename std::iterator_traits<InputIt>::iterator_category{},
3740 alloc) {}
3741
3742 constexpr small_vector(std::initializer_list<value_type> init,
3743 const allocator_type& alloc = allocator_type())
3744 requires EmplaceConstructible<const_reference>::value
3745 : small_vector(init.begin(), init.end(), alloc) {}
3746
3747 template <unsigned I>
3748 requires CopyInsertable
3749 constexpr explicit small_vector(const small_vector<T, I, Allocator>& other)
3750 : base(base::bypass, other) {}
3751
3752 template <unsigned I>
3753 requires MoveInsertable
3754 constexpr explicit small_vector(
3756 other) noexcept(std::is_nothrow_move_constructible<value_type>::
3757 value &&
3758 I < InlineCapacity)
3759 : base(base::bypass, std::move(other)) {}
3760
3761 template <unsigned I>
3762 requires CopyInsertable
3764 const allocator_type& alloc)
3765 : base(base::bypass, other, alloc) {}
3766
3767 template <unsigned I>
3768 requires MoveInsertable
3770 const allocator_type& alloc)
3771 : base(base::bypass, std::move(other), alloc) {}
3772
3773 constexpr ~small_vector()
3774 requires Erasable
3775 = default;
3776
3777 constexpr small_vector& operator=(const small_vector& other)
3778 requires CopyInsertable && CopyAssignable
3779 {
3780 assign(other);
3781 return *this;
3782 }
3783
3784 constexpr small_vector& operator=(small_vector&& other) noexcept(
3785 (std::is_same_v<std::allocator<value_type>, Allocator> ||
3786 std::allocator_traits<
3787 Allocator>::propagate_on_container_move_assignment::value ||
3788 std::allocator_traits<Allocator>::is_always_equal::value) &&
3789 ((std::is_nothrow_move_assignable_v<value_type> &&
3790 std::is_nothrow_move_constructible_v<value_type>) ||
3791 InlineCapacity == 0))
3792 // Note: The standard says here that
3793 // std::allocator_traits<allocator_type>::propagate_on_container_move_assignment
3794 // == false implies MoveInsertable && MoveAssignable, but since we have
3795 // inline storage we must always require moves [tab:container.alloc.req].
3796 requires MoveInsertable && MoveAssignable
3797 {
3798 assign(std::move(other));
3799 return *this;
3800 }
3801
3802 constexpr small_vector& operator=(std::initializer_list<value_type> ilist)
3803 requires CopyInsertable && CopyAssignable
3804 {
3805 assign(ilist);
3806 return *this;
3807 }
3808
3809 constexpr void assign(size_type count, const_reference value)
3810 requires CopyInsertable && CopyAssignable
3811 {
3812 base::assign_with_copies(count, value);
3813 }
3814
3815 template <std::input_iterator InputIt>
3816 requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value &&
3817 (std::forward_iterator<InputIt> || MoveInsertable)
3818 constexpr void assign(InputIt first, InputIt last) {
3819 using iterator_cat =
3820 typename std::iterator_traits<InputIt>::iterator_category;
3821 base::assign_with_range(first, last, iterator_cat{});
3822 }
3823
3824 constexpr void assign(std::initializer_list<value_type> ilist)
3825 requires EmplaceConstructible<const_reference>::value
3826 {
3827 assign(ilist.begin(), ilist.end());
3828 }
3829
3830 constexpr void assign(const small_vector& other)
3831 requires CopyInsertable && CopyAssignable
3832 {
3833 if (&other != this) {
3834 base::copy_assign(other);
3835 }
3836 }
3837
3838 template <unsigned I>
3839 requires CopyInsertable && CopyAssignable
3840 constexpr void assign(const small_vector<T, I, Allocator>& other) {
3841 base::copy_assign(other);
3842 }
3843
3844 constexpr void assign(small_vector&& other) noexcept(
3845 (std::is_same_v<std::allocator<value_type>, Allocator> ||
3846 std::allocator_traits<
3847 Allocator>::propagate_on_container_move_assignment::value ||
3848 std::allocator_traits<Allocator>::is_always_equal::value) &&
3849 ((std::is_nothrow_move_assignable_v<value_type> &&
3850 std::is_nothrow_move_constructible_v<value_type>) ||
3851 InlineCapacity == 0))
3852 requires MoveInsertable && MoveAssignable
3853 {
3854 if (&other != this) {
3855 base::move_assign(std::move(other));
3856 }
3857 }
3858
3859 template <unsigned I>
3860 requires MoveInsertable && MoveAssignable
3861 constexpr void assign(small_vector<T, I, Allocator>&& other) noexcept(
3862 I <= InlineCapacity &&
3863 (std::is_same_v<std::allocator<value_type>, Allocator> ||
3864 std::allocator_traits<
3865 Allocator>::propagate_on_container_move_assignment::value ||
3866 std::allocator_traits<Allocator>::is_always_equal::value) &&
3867 std::is_nothrow_move_assignable_v<value_type> &&
3868 std::is_nothrow_move_constructible_v<value_type>) {
3869 base::move_assign(std::move(other));
3870 }
3871
3872 constexpr void swap(small_vector& other) noexcept(
3873 (std::is_same_v<std::allocator<value_type>, Allocator> ||
3874 std::allocator_traits<Allocator>::propagate_on_container_swap::value ||
3875 std::allocator_traits<Allocator>::is_always_equal::value) &&
3876 ((std::is_nothrow_move_constructible_v<value_type> &&
3877 std::is_nothrow_move_assignable_v<value_type> &&
3878 std::is_nothrow_swappable_v<value_type>) ||
3879 InlineCapacity == 0))
3880 requires(MoveInsertable && MoveAssignable && Swappable) ||
3881 ((std::is_same_v<std::allocator<value_type>, Allocator> ||
3882 std::allocator_traits<
3883 Allocator>::propagate_on_container_swap::value ||
3884 std::allocator_traits<Allocator>::is_always_equal::value) &&
3885 InlineCapacity == 0)
3886 {
3887 base::swap(other);
3888 }
3889
3890 constexpr iterator begin() noexcept { return iterator{base::begin_ptr()}; }
3891
3892 constexpr const_iterator begin() const noexcept {
3893 return const_iterator{base::begin_ptr()};
3894 }
3895
3896 constexpr const_iterator cbegin() const noexcept { return begin(); }
3897
3898 constexpr iterator end() noexcept { return iterator{base::end_ptr()}; }
3899
3900 constexpr const_iterator end() const noexcept {
3901 return const_iterator{base::end_ptr()};
3902 }
3903
3904 constexpr const_iterator cend() const noexcept { return end(); }
3905
3906 constexpr reverse_iterator rbegin() noexcept {
3907 return reverse_iterator{end()};
3908 }
3909
3910 constexpr const_reverse_iterator rbegin() const noexcept {
3911 return const_reverse_iterator{end()};
3912 }
3913
3914 constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
3915
3916 constexpr reverse_iterator rend() noexcept {
3917 return reverse_iterator{begin()};
3918 }
3919
3920 constexpr const_reverse_iterator rend() const noexcept {
3921 return const_reverse_iterator{begin()};
3922 }
3923
3924 constexpr const_reverse_iterator crend() const noexcept { return rend(); }
3925
3926 constexpr reference at(size_type pos) {
3927 if (size() <= pos) {
3928 base::throw_index_error();
3929 }
3930 return begin()[static_cast<difference_type>(pos)];
3931 }
3932
3933 constexpr const_reference at(size_type pos) const {
3934 if (size() <= pos) {
3935 base::throw_index_error();
3936 }
3937 return begin()[static_cast<difference_type>(pos)];
3938 }
3939
3941 return begin()[static_cast<difference_type>(pos)];
3942 }
3943
3944 constexpr const_reference operator[](size_type pos) const {
3945 return begin()[static_cast<difference_type>(pos)];
3946 }
3947
3948 constexpr reference front() { return (*this)[0]; }
3949
3950 constexpr const_reference front() const { return (*this)[0]; }
3951
3952 constexpr reference back() { return (*this)[size() - 1]; }
3953
3954 constexpr const_reference back() const { return (*this)[size() - 1]; }
3955
3956 constexpr pointer data() noexcept { return base::begin_ptr(); }
3957
3958 constexpr const_pointer data() const noexcept { return base::begin_ptr(); }
3959
3960 constexpr size_type size() const noexcept {
3961 return static_cast<size_type>(base::get_size());
3962 }
3963
3964 [[nodiscard]]
3965 constexpr bool empty() const noexcept {
3966 return size() == 0;
3967 }
3968
3969 constexpr size_type max_size() const noexcept {
3970 return static_cast<size_type>(base::get_max_size());
3971 }
3972
3973 constexpr size_type capacity() const noexcept {
3974 return static_cast<size_type>(base::get_capacity());
3975 }
3976
3977 constexpr allocator_type get_allocator() const noexcept {
3978 return base::copy_allocator();
3979 }
3980
3982 requires CopyInsertable && CopyAssignable
3983 {
3984 return emplace(pos, value);
3985 }
3986
3988 requires MoveInsertable && MoveAssignable
3989 {
3990 return emplace(pos, std::move(value));
3991 }
3992
3994 const_reference value)
3995 requires CopyInsertable && CopyAssignable
3996 {
3997 return iterator(base::insert_copies(base::ptr_cast(pos), count, value));
3998 }
3999
4000 // Note: Unlike std::vector, this does not require MoveConstructible because
4001 // we
4002 // don't use std::rotate (as was the reason for the change in C++17).
4003 // Relevant: https://cplusplus.github.io/LWG/issue2266).
4004 template <std::input_iterator InputIt>
4005 requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value &&
4006 MoveInsertable && MoveAssignable
4007 constexpr iterator insert(const_iterator pos, InputIt first, InputIt last) {
4008 if (first == last) {
4009 return iterator(base::ptr_cast(pos));
4010 }
4011
4012 using iterator_cat =
4013 typename std::iterator_traits<InputIt>::iterator_category;
4014 return iterator(
4015 base::insert_range(base::ptr_cast(pos), first, last, iterator_cat{}));
4016 }
4017
4019 std::initializer_list<value_type> ilist)
4020 requires EmplaceConstructible<const_reference>::value && MoveInsertable
4021 && MoveAssignable
4022 {
4023 return insert(pos, ilist.begin(), ilist.end());
4024 }
4025
4026 template <typename... Args>
4027 requires EmplaceConstructible<Args...>::value && MoveInsertable &&
4028 MoveAssignable
4029 constexpr iterator emplace(const_iterator pos, Args&&... args) {
4030 return iterator(
4031 base::emplace_at(base::ptr_cast(pos), std::forward<Args>(args)...));
4032 }
4033
4035 requires MoveAssignable && Erasable
4036 {
4037 assert(0 <= (pos - begin()) &&
4038 "`pos` is out of bounds (before `begin ()`).");
4039 assert(0 < (end() - pos) &&
4040 "`pos` is out of bounds (at or after `end ()`).");
4041
4042 return iterator(base::erase_at(base::ptr_cast(pos)));
4043 }
4044
4046 requires MoveAssignable && Erasable
4047 {
4048 assert(0 <= (last - first) && "Invalid range.");
4049 assert(0 <= (first - begin()) &&
4050 "`first` is out of bounds (before `begin ()`).");
4051 assert(0 <= (end() - last) && "`last` is out of bounds (after `end ()`).");
4052
4053 return iterator(
4054 base::erase_range(base::ptr_cast(first), base::ptr_cast(last)));
4055 }
4056
4057 constexpr void push_back(const_reference value)
4058 requires CopyInsertable
4059 {
4060 emplace_back(value);
4061 }
4062
4063 constexpr void push_back(value_type&& value)
4064 requires MoveInsertable
4065 {
4066 emplace_back(std::move(value));
4067 }
4068
4069 template <typename... Args>
4070 requires EmplaceConstructible<Args...>::value && MoveInsertable
4071 constexpr reference emplace_back(Args&&... args) {
4072 return *base::append_element(std::forward<Args>(args)...);
4073 }
4074
4075 constexpr void pop_back()
4076 requires Erasable
4077 {
4078 assert(!empty() && "`pop_back ()` called on an empty `small_vector`.");
4079 base::erase_last();
4080 }
4081
4082 constexpr void reserve(size_type new_capacity)
4083 requires MoveInsertable
4084 {
4085 base::request_capacity(new_capacity);
4086 }
4087
4088 constexpr void shrink_to_fit()
4089 requires MoveInsertable
4090 {
4091 base::shrink_to_size();
4092 }
4093
4094 constexpr void clear() noexcept
4095 requires Erasable
4096 {
4097 base::erase_all();
4098 }
4099
4100 constexpr void resize(size_type count)
4101 requires MoveInsertable && DefaultInsertable
4102 {
4103 base::resize_with(count);
4104 }
4105
4106 constexpr void resize(size_type count, const_reference value)
4107 requires CopyInsertable
4108 {
4109 base::resize_with(count, value);
4110 }
4111
4112 [[nodiscard]]
4113 constexpr bool inlined() const noexcept {
4114 return !base::has_allocation();
4115 }
4116
4117 [[nodiscard]]
4118 constexpr bool inlinable() const noexcept {
4119 return base::is_inlinable();
4120 }
4121
4122 [[nodiscard]]
4123 static consteval size_type inline_capacity() noexcept {
4124 return static_cast<size_type>(inline_capacity_v);
4125 }
4126
4127 template <std::input_iterator InputIt>
4128 requires EmplaceConstructible<std::iter_reference_t<InputIt>>::value &&
4129 MoveInsertable
4130 constexpr small_vector& append(InputIt first, InputIt last) {
4131 using policy = typename base::strong_exception_policy;
4132 using iterator_cat =
4133 typename std::iterator_traits<InputIt>::iterator_category;
4134 base::template append_range<policy>(first, last, iterator_cat{});
4135 return *this;
4136 }
4137
4138 constexpr small_vector& append(std::initializer_list<value_type> ilist)
4139 requires EmplaceConstructible<const_reference>::value && MoveInsertable
4140 {
4141 return append(ilist.begin(), ilist.end());
4142 }
4143
4144 template <unsigned I>
4145 constexpr small_vector& append(const small_vector<T, I, Allocator>& other)
4146 requires CopyInsertable
4147 {
4148 return append(other.begin(), other.end());
4149 }
4150
4151 template <unsigned I>
4152 constexpr small_vector& append(small_vector<T, I, Allocator>&& other)
4153 requires MoveInsertable
4154 {
4155 // Provide a strong exception guarantee for `other` as well.
4156 using move_iter_type = typename std::conditional_t<
4157 base::template relocate_with_move_v<value_type>,
4158 std::move_iterator<iterator>, iterator>;
4159
4160 append(move_iter_type{other.begin()}, move_iter_type{other.end()});
4161 other.clear();
4162 return *this;
4163 }
4164};
4165
4166template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS,
4167 typename Allocator>
4168inline constexpr bool operator==(
4171 return lhs.size() == rhs.size() &&
4172 std::equal(lhs.begin(), lhs.end(), rhs.begin());
4173}
4174
4175template <typename T, unsigned InlineCapacity, typename Allocator>
4176inline constexpr bool operator==(
4179 return lhs.size() == rhs.size() &&
4180 std::equal(lhs.begin(), lhs.end(), rhs.begin());
4181}
4182
4183template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS,
4184 typename Allocator>
4185 requires std::three_way_comparable<T>
4186constexpr auto operator<=>(
4189 return std::lexicographical_compare_three_way(
4190 lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::compare_three_way{});
4191}
4192
4193template <typename T, unsigned InlineCapacity, typename Allocator>
4194 requires std::three_way_comparable<T>
4195constexpr auto operator<=>(
4198 return std::lexicographical_compare_three_way(
4199 lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::compare_three_way{});
4200}
4201
4202template <typename T, unsigned InlineCapacityLHS, unsigned InlineCapacityRHS,
4203 typename Allocator>
4204constexpr auto operator<=>(
4207 constexpr auto comparison = [](const T& l, const T& r) {
4208 return (l < r) ? std::weak_ordering::less
4209 : (r < l) ? std::weak_ordering::greater
4210 : std::weak_ordering::equivalent;
4211 };
4212
4213 return std::lexicographical_compare_three_way(
4214 lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), comparison);
4215}
4216
4217template <typename T, unsigned InlineCapacity, typename Allocator>
4218constexpr auto operator<=>(
4221 constexpr auto comparison = [](const T& l, const T& r) {
4222 return (l < r) ? std::weak_ordering::less
4223 : (r < l) ? std::weak_ordering::greater
4224 : std::weak_ordering::equivalent;
4225 };
4226
4227 return std::lexicographical_compare_three_way(
4228 lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), comparison);
4229}
4230
4231template <typename T, unsigned InlineCapacity, typename Allocator>
4234 rhs) noexcept(noexcept(lhs.swap(rhs)))
4235 requires concepts::MoveInsertable<
4238{
4239 lhs.swap(rhs);
4240}
4241
4242template <typename T, unsigned InlineCapacity, typename Allocator, typename U>
4243inline constexpr typename small_vector<T, InlineCapacity, Allocator>::size_type
4245 const auto original_size = v.size();
4246 v.erase(std::remove(v.begin(), v.end(), value), v.end());
4247 return original_size - v.size();
4248}
4249
4250template <typename T, unsigned InlineCapacity, typename Allocator,
4251 typename Pred>
4252inline constexpr typename small_vector<T, InlineCapacity, Allocator>::size_type
4254 const auto original_size = v.size();
4255 v.erase(std::remove_if(v.begin(), v.end(), pred), v.end());
4256 return original_size - v.size();
4257}
4258
4259template <typename T, unsigned InlineCapacity, typename Allocator>
4262 return v.begin();
4263}
4264
4265template <typename T, unsigned InlineCapacity, typename Allocator>
4266constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator
4268 return v.begin();
4269}
4270
4271template <typename T, unsigned InlineCapacity, typename Allocator>
4272constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator
4274 return begin(v);
4275}
4276
4277template <typename T, unsigned InlineCapacity, typename Allocator>
4280 return v.end();
4281}
4282
4283template <typename T, unsigned InlineCapacity, typename Allocator>
4284constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator
4286 return v.end();
4287}
4288
4289template <typename T, unsigned InlineCapacity, typename Allocator>
4290constexpr typename small_vector<T, InlineCapacity, Allocator>::const_iterator
4292 return end(v);
4293}
4294
4295template <typename T, unsigned InlineCapacity, typename Allocator>
4296constexpr typename small_vector<T, InlineCapacity, Allocator>::reverse_iterator
4298 return v.rbegin();
4299}
4300
4301template <typename T, unsigned InlineCapacity, typename Allocator>
4302constexpr
4303 typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator
4305 return v.rbegin();
4306}
4307
4308template <typename T, unsigned InlineCapacity, typename Allocator>
4309constexpr
4310 typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator
4312 return rbegin(v);
4313}
4314
4315template <typename T, unsigned InlineCapacity, typename Allocator>
4316constexpr typename small_vector<T, InlineCapacity, Allocator>::reverse_iterator
4318 return v.rend();
4319}
4320
4321template <typename T, unsigned InlineCapacity, typename Allocator>
4322constexpr
4323 typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator
4325 return v.rend();
4326}
4327
4328template <typename T, unsigned InlineCapacity, typename Allocator>
4329constexpr
4330 typename small_vector<T, InlineCapacity, Allocator>::const_reverse_iterator
4332 return rend(v);
4333}
4334
4335template <typename T, unsigned InlineCapacity, typename Allocator>
4338 return v.size();
4339}
4340
4341template <typename T, unsigned InlineCapacity, typename Allocator>
4342constexpr typename std::common_type_t<
4343 std::ptrdiff_t, typename std::make_signed_t<typename small_vector<
4344 T, InlineCapacity, Allocator>::size_type>>
4346 using ret_type = typename std::common_type_t<
4347 std::ptrdiff_t, typename std::make_signed_t<decltype(v.size())>>;
4348 return static_cast<ret_type>(v.size());
4349}
4350
4351template <typename T, unsigned InlineCapacity, typename Allocator>
4352[[nodiscard]]
4353constexpr bool empty(
4355 return v.empty();
4356}
4357
4358template <typename T, unsigned InlineCapacity, typename Allocator>
4361 return v.data();
4362}
4363
4364template <typename T, unsigned InlineCapacity, typename Allocator>
4365constexpr typename small_vector<T, InlineCapacity, Allocator>::const_pointer
4367 return v.data();
4368}
4369
4370template <
4371 typename InputIt,
4372 unsigned InlineCapacity = default_buffer_size_v<
4373 std::allocator<typename std::iterator_traits<InputIt>::value_type>>,
4374 typename Allocator =
4375 std::allocator<typename std::iterator_traits<InputIt>::value_type>>
4376small_vector(InputIt, InputIt, Allocator = Allocator())
4378 InlineCapacity, Allocator>;
4379
4380} // namespace sleipnir
Definition small_vector.hpp:499
constexpr reference operator*() const noexcept
Definition small_vector.hpp:566
constexpr small_vector_iterator & operator-=(difference_type n) noexcept
Definition small_vector.hpp:557
typename std::iterator_traits< Pointer >::pointer pointer
Definition small_vector.hpp:503
typename std::iterator_traits< Pointer >::value_type value_type
Definition small_vector.hpp:502
constexpr small_vector_iterator(const small_vector_iterator< U, D > &other) noexcept
Definition small_vector.hpp:526
constexpr small_vector_iterator operator--(int) noexcept
Definition small_vector.hpp:544
constexpr small_vector_iterator operator+(difference_type n) const noexcept
Definition small_vector.hpp:553
DifferenceType difference_type
Definition small_vector.hpp:501
constexpr small_vector_iterator & operator+=(difference_type n) noexcept
Definition small_vector.hpp:548
constexpr small_vector_iterator & operator++() noexcept
Definition small_vector.hpp:530
std::contiguous_iterator_tag iterator_concept
Definition small_vector.hpp:507
constexpr small_vector_iterator operator++(int) noexcept
Definition small_vector.hpp:535
typename std::iterator_traits< Pointer >::iterator_category iterator_category
Definition small_vector.hpp:506
typename std::iterator_traits< Pointer >::reference reference
Definition small_vector.hpp:504
small_vector_iterator(const small_vector_iterator &)=default
small_vector_iterator(small_vector_iterator &&) noexcept=default
constexpr small_vector_iterator() noexcept
Definition small_vector.hpp:518
constexpr small_vector_iterator(const Pointer &p) noexcept
Definition small_vector.hpp:521
constexpr const Pointer & base() const noexcept
Definition small_vector.hpp:576
constexpr small_vector_iterator & operator--() noexcept
Definition small_vector.hpp:539
constexpr reference operator[](difference_type n) const noexcept
Definition small_vector.hpp:572
constexpr pointer operator->() const noexcept
Definition small_vector.hpp:570
constexpr small_vector_iterator operator-(difference_type n) const noexcept
Definition small_vector.hpp:562
Definition small_vector.hpp:3616
::value &&std::forward_iterator< InputIt > MoveInsertable constexpr small_vector(InputIt first, InputIt last, const allocator_type &alloc=allocator_type())
Definition small_vector.hpp:3736
constexpr const_reverse_iterator crbegin() const noexcept
Definition small_vector.hpp:3914
constexpr small_vector(small_vector< T, I, Allocator > &&other, const allocator_type &alloc)
Definition small_vector.hpp:3769
constexpr const_iterator end() const noexcept
Definition small_vector.hpp:3900
constexpr const_reference operator[](size_type pos) const
Definition small_vector.hpp:3944
constexpr small_vector() noexcept(noexcept(allocator_type()))=default
constexpr iterator erase(const_iterator first, const_iterator last) &&Erasable
Definition small_vector.hpp:4045
constexpr size_type capacity() const noexcept
Definition small_vector.hpp:3973
typename base::size_type size_type
Definition small_vector.hpp:3630
constexpr const_reference at(size_type pos) const
Definition small_vector.hpp:3933
::value constexpr small_vector(size_type count, Generator g, const allocator_type &alloc=allocator_type())
Definition small_vector.hpp:3729
::value &&MoveInsertable &&MoveAssignable constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
Definition small_vector.hpp:4007
constexpr void swap(small_vector &other) noexcept((std::is_same_v< std::allocator< value_type >, Allocator >||std::allocator_traits< Allocator >::propagate_on_container_swap::value||std::allocator_traits< Allocator >::is_always_equal::value) &&((std::is_nothrow_move_constructible_v< value_type > &&std::is_nothrow_move_assignable_v< value_type > &&std::is_nothrow_swappable_v< value_type >)||InlineCapacity==0))
Definition small_vector.hpp:3872
constexpr const_reverse_iterator crend() const noexcept
Definition small_vector.hpp:3924
constexpr void pop_back()
Definition small_vector.hpp:4075
constexpr iterator insert(const_iterator pos, const_reference value) &&CopyAssignable
Definition small_vector.hpp:3981
typename std::allocator_traits< allocator_type >::const_pointer const_pointer
Definition small_vector.hpp:3636
constexpr small_vector & operator=(small_vector &&other) noexcept((std::is_same_v< std::allocator< value_type >, Allocator >||std::allocator_traits< Allocator >::propagate_on_container_move_assignment::value||std::allocator_traits< Allocator >::is_always_equal::value) &&((std::is_nothrow_move_assignable_v< value_type > &&std::is_nothrow_move_constructible_v< value_type >)||InlineCapacity==0)) &&MoveAssignable
Definition small_vector.hpp:3784
constexpr small_vector(small_vector &&other, const allocator_type &alloc)
Definition small_vector.hpp:3712
constexpr void resize(size_type count, const_reference value)
Definition small_vector.hpp:4106
constexpr iterator insert(const_iterator pos, std::initializer_list< value_type > ilist)
Definition small_vector.hpp:4018
constexpr small_vector(small_vector &&other) noexcept(std::is_nothrow_move_constructible_v< value_type >||InlineCapacity==0)
Definition small_vector.hpp:3700
constexpr ~small_vector()=default
constexpr const_iterator cend() const noexcept
Definition small_vector.hpp:3904
&&MoveAssignable constexpr void assign(small_vector< T, I, Allocator > &&other) noexcept(I<=InlineCapacity &&(std::is_same_v< std::allocator< value_type >, Allocator >||std::allocator_traits< Allocator >::propagate_on_container_move_assignment::value||std::allocator_traits< Allocator >::is_always_equal::value) &&std::is_nothrow_move_assignable_v< value_type > &&std::is_nothrow_move_constructible_v< value_type >)
Definition small_vector.hpp:3861
::value &&MoveInsertable constexpr small_vector & append(InputIt first, InputIt last)
Definition small_vector.hpp:4130
constexpr size_type size() const noexcept
Definition small_vector.hpp:3960
constexpr void clear() noexcept
Definition small_vector.hpp:4094
constexpr small_vector(small_vector< T, I, Allocator > &&other) noexcept(std::is_nothrow_move_constructible< value_type >::value &&I< InlineCapacity)
Definition small_vector.hpp:3754
constexpr const_reference back() const
Definition small_vector.hpp:3954
constexpr iterator insert(const_iterator pos, size_type count, const_reference value) &&CopyAssignable
Definition small_vector.hpp:3993
constexpr void assign(small_vector &&other) noexcept((std::is_same_v< std::allocator< value_type >, Allocator >||std::allocator_traits< Allocator >::propagate_on_container_move_assignment::value||std::allocator_traits< Allocator >::is_always_equal::value) &&((std::is_nothrow_move_assignable_v< value_type > &&std::is_nothrow_move_constructible_v< value_type >)||InlineCapacity==0)) &&MoveAssignable
Definition small_vector.hpp:3844
Allocator allocator_type
Definition small_vector.hpp:3629
constexpr void assign(std::initializer_list< value_type > ilist)
Definition small_vector.hpp:3824
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition small_vector.hpp:3641
constexpr void reserve(size_type new_capacity)
Definition small_vector.hpp:4082
constexpr reference at(size_type pos)
Definition small_vector.hpp:3926
constexpr bool empty() const noexcept
Definition small_vector.hpp:3965
constexpr reverse_iterator rbegin() noexcept
Definition small_vector.hpp:3906
constexpr small_vector(const small_vector< T, I, Allocator > &other)
Definition small_vector.hpp:3749
value_type & reference
Definition small_vector.hpp:3632
constexpr bool inlined() const noexcept
Definition small_vector.hpp:4113
constexpr pointer data() noexcept
Definition small_vector.hpp:3956
constexpr small_vector(size_type count, const_reference value, const allocator_type &alloc=allocator_type())
Definition small_vector.hpp:3721
constexpr void push_back(value_type &&value)
Definition small_vector.hpp:4063
constexpr const_reverse_iterator rend() const noexcept
Definition small_vector.hpp:3920
::value &&MoveInsertable constexpr reference emplace_back(Args &&... args)
Definition small_vector.hpp:4071
constexpr const_reference front() const
Definition small_vector.hpp:3950
constexpr reference operator[](size_type pos)
Definition small_vector.hpp:3940
constexpr void resize(size_type count) &&DefaultInsertable
Definition small_vector.hpp:4100
constexpr allocator_type get_allocator() const noexcept
Definition small_vector.hpp:3977
constexpr const_pointer data() const noexcept
Definition small_vector.hpp:3958
constexpr reverse_iterator rend() noexcept
Definition small_vector.hpp:3916
constexpr size_type max_size() const noexcept
Definition small_vector.hpp:3969
typename std::allocator_traits< allocator_type >::pointer pointer
Definition small_vector.hpp:3634
constexpr void assign(size_type count, const_reference value) &&CopyAssignable
Definition small_vector.hpp:3809
constexpr void shrink_to_fit()
Definition small_vector.hpp:4088
constexpr small_vector & operator=(std::initializer_list< value_type > ilist) &&CopyAssignable
Definition small_vector.hpp:3802
::value &&std::forward_iterator< InputIt > MoveInsertable constexpr void assign(InputIt first, InputIt last)
Definition small_vector.hpp:3818
constexpr iterator insert(const_iterator pos, value_type &&value) &&MoveAssignable
Definition small_vector.hpp:3987
constexpr small_vector(size_type count, const allocator_type &alloc=allocator_type())
Definition small_vector.hpp:3716
constexpr small_vector(const small_vector< T, I, Allocator > &other, const allocator_type &alloc)
Definition small_vector.hpp:3763
constexpr bool inlinable() const noexcept
Definition small_vector.hpp:4118
T value_type
Definition small_vector.hpp:3628
constexpr small_vector(const allocator_type &alloc) noexcept
Definition small_vector.hpp:3705
constexpr reference front()
Definition small_vector.hpp:3948
const value_type & const_reference
Definition small_vector.hpp:3633
typename base::difference_type difference_type
Definition small_vector.hpp:3631
std::reverse_iterator< iterator > reverse_iterator
Definition small_vector.hpp:3640
constexpr small_vector(const small_vector &other, const allocator_type &alloc)
Definition small_vector.hpp:3708
constexpr small_vector(std::initializer_list< value_type > init, const allocator_type &alloc=allocator_type())
Definition small_vector.hpp:3742
constexpr const_iterator cbegin() const noexcept
Definition small_vector.hpp:3896
constexpr iterator end() noexcept
Definition small_vector.hpp:3898
constexpr void push_back(const_reference value)
Definition small_vector.hpp:4057
constexpr iterator begin() noexcept
Definition small_vector.hpp:3890
constexpr const_iterator begin() const noexcept
Definition small_vector.hpp:3892
static consteval size_type inline_capacity() noexcept
Definition small_vector.hpp:4123
constexpr const_reverse_iterator rbegin() const noexcept
Definition small_vector.hpp:3910
constexpr iterator erase(const_iterator pos) &&Erasable
Definition small_vector.hpp:4034
constexpr reference back()
Definition small_vector.hpp:3952
Definition small_vector.hpp:209
Definition small_vector.hpp:382
Definition small_vector.hpp:187
Definition small_vector.hpp:37
Definition small_vector.hpp:54
Definition small_vector.hpp:60
Definition small_vector.hpp:83
Definition small_vector.hpp:95
Definition small_vector.hpp:165
Definition small_vector.hpp:75
Definition small_vector.hpp:153
Definition small_vector.hpp:42
Definition small_vector.hpp:124
Definition small_vector.hpp:111
Definition small_vector.hpp:174
Definition small_vector.hpp:80
Definition small_vector.hpp:88
Definition small_vector.hpp:159
Definition small_vector.hpp:57
Definition small_vector.hpp:67
Definition small_vector.hpp:101
Definition small_vector.hpp:48
Definition small_vector.hpp:91
Definition small_vector.hpp:191
Definition small_vector.hpp:108
Definition small_vector.hpp:45
Definition small_vector.hpp:432
Definition small_vector.hpp:436
Definition small_vector.hpp:391
Definition small_vector.hpp:423
Definition small_vector.hpp:408
Definition Expression.hpp:18
IntrusiveSharedPtr< T > AllocateIntrusiveShared(Alloc alloc, Args &&... args)
Definition IntrusiveSharedPtr.hpp:275
constexpr small_vector< T, InlineCapacity, Allocator >::pointer data(small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4359
small_vector(InputIt, InputIt, Allocator=Allocator()) -> small_vector< typename std::iterator_traits< InputIt >::value_type, InlineCapacity, Allocator >
constexpr bool empty(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4353
constexpr small_vector_iterator< Pointer, DifferenceType > operator+(DifferenceType n, const small_vector_iterator< Pointer, DifferenceType > &it) noexcept
Definition small_vector.hpp:692
constexpr small_vector< T, InlineCapacity, Allocator >::iterator begin(small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4260
constexpr auto operator<=>(const small_vector_iterator< PointerLHS, DifferenceTypeLHS > &lhs, const small_vector_iterator< PointerRHS, DifferenceTypeRHS > &rhs) noexcept(noexcept(lhs.base()<=> rhs.base()))
Definition small_vector.hpp:636
constexpr small_vector< T, InlineCapacity, Allocator >::reverse_iterator rend(small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4317
constexpr std::common_type_t< std::ptrdiff_t, typename std::make_signed_t< typename small_vector< T, InlineCapacity, Allocator >::size_type > > ssize(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4345
EqualityConstraints operator==(LHS &&lhs, RHS &&rhs)
Definition Variable.hpp:681
constexpr small_vector< T, InlineCapacity, Allocator >::size_type erase_if(small_vector< T, InlineCapacity, Allocator > &v, Pred pred)
Definition small_vector.hpp:4253
constexpr small_vector< T, InlineCapacity, Allocator >::const_reverse_iterator crbegin(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4311
constexpr void swap(function_ref< R(Args...)> &lhs, function_ref< R(Args...)> &rhs) noexcept
Definition FunctionRef.hpp:92
constexpr DifferenceType operator-(const small_vector_iterator< PointerLHS, DifferenceType > &lhs, const small_vector_iterator< PointerRHS, DifferenceType > &rhs) noexcept
Definition small_vector.hpp:678
constexpr small_vector< T, InlineCapacity, Allocator >::reverse_iterator rbegin(small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4297
constexpr small_vector< T, InlineCapacity, Allocator >::iterator end(small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4278
constexpr small_vector< T, InlineCapacity, Allocator >::const_reverse_iterator crend(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4331
constexpr small_vector< T, InlineCapacity, Allocator >::size_type erase(small_vector< T, InlineCapacity, Allocator > &v, const U &value)
Definition small_vector.hpp:4244
constexpr small_vector< T, InlineCapacity, Allocator >::const_iterator cbegin(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4273
constexpr small_vector< T, InlineCapacity, Allocator >::const_iterator cend(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4291
constexpr small_vector< T, InlineCapacity, Allocator >::size_type size(const small_vector< T, InlineCapacity, Allocator > &v) noexcept
Definition small_vector.hpp:4336
constexpr unsigned default_buffer_size_v
Definition small_vector.hpp:495
Definition small_vector.hpp:455
Allocator allocator_type
Definition small_vector.hpp:468
typename std::allocator_traits< allocator_type >::value_type value_type
Definition small_vector.hpp:469
Definition small_vector.hpp:2289