plugify 1.2.6
Loading...
Searching...
No Matches
vector.hpp
1#pragma once
2
3#include <iterator>
4#include <type_traits>
5#include <utility>
6#include <memory>
7#include <memory_resource>
8#include <initializer_list>
9#include <algorithm>
10#include <span>
11#include <limits>
12#include <optional>
13
14#include <cstdint>
15#include <cstddef>
16#include <cstring>
17
18#if PLUGIFY_VECTOR_CONTAINERS_RANGES && (__cplusplus <= 202002L || !__has_include(<ranges>) || !defined(__cpp_lib_containers_ranges))
19# undef PLUGIFY_VECTOR_CONTAINERS_RANGES
20# define PLUGIFY_VECTOR_CONTAINERS_RANGES 0
21#endif
22
23#if PLUGIFY_VECTOR_CONTAINERS_RANGES
24# include <ranges>
25#endif
26
27#include "allocator.hpp"
28
29namespace plg {
30 template<typename Allocator>
32 using allocator_traits = std::allocator_traits<Allocator>;
33 public:
34 using iterator_category = std::random_access_iterator_tag;
35 using value_type = typename allocator_traits::value_type;
36 using difference_type = std::ptrdiff_t;
37 using pointer = typename allocator_traits::pointer;
38 using reference = value_type&;
39 protected:
40 pointer _current;
41 public:
42 constexpr vector_iterator() = default;
43 constexpr vector_iterator(const vector_iterator& other) = default;
44 constexpr vector_iterator(vector_iterator&& other) = default;
45 constexpr vector_iterator(pointer ptr)
46 : _current(ptr) {}
47 constexpr vector_iterator& operator=(const vector_iterator& other) = default;
48 constexpr vector_iterator& operator=(vector_iterator&& other) = default;
49 constexpr ~vector_iterator() = default;
50 public:
51 constexpr reference operator*() const noexcept {
52 return *_current;
53 }
54 constexpr pointer operator->() const noexcept {
55 return _current;
56 }
57 constexpr vector_iterator& operator++() noexcept {
58 ++_current;
59 return *this;
60 }
61 constexpr vector_iterator operator++(int) const noexcept {
62 return vector_iterator(_current++);
63 }
64 constexpr vector_iterator& operator--() noexcept {
65 --_current;
66 return *this;
67 }
68 constexpr vector_iterator operator--(int) const noexcept {
69 return vector_iterator(_current--);
70 }
71 constexpr vector_iterator& operator+=(const difference_type n) noexcept {
72 _current += n;
73 return *this;
74 }
75 constexpr vector_iterator operator+(const difference_type n) const noexcept {
76 vector_iterator temp = *this;
77 return temp += n;
78 }
79 constexpr vector_iterator& operator-=(const difference_type n) noexcept {
80 _current -= n;
81 return *this;
82 }
83 constexpr vector_iterator operator-(const difference_type n) const noexcept {
84 vector_iterator temp = *this;
85 return temp -= n;
86 }
87 constexpr reference operator[](const difference_type n) const noexcept {
88 return _current[n];
89 }
90 template<typename Alloc>
91 constexpr friend typename vector_iterator<Alloc>::difference_type operator-(const vector_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
92 template<typename Alloc>
93 constexpr friend bool operator==(const vector_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
94#if __cpp_impl_three_way_comparison
95 template<typename Alloc>
96 constexpr friend auto operator<=>(const vector_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
97 operator const pointer() const noexcept {
98 return _current;
99 }
100#endif // __cpp_impl_three_way_comparison
101 pointer base() const noexcept {
102 return _current;
103 }
104 };
105
106 template<typename Allocator>
107 constexpr typename vector_iterator<Allocator>::difference_type operator-(const vector_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
108 using difference_type = typename vector_iterator<Allocator>::difference_type;
109 return difference_type(lhs.base() - rhs.base());
110 }
111 template<typename Allocator>
112 constexpr bool operator==(const vector_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
113 return lhs.base() == rhs.base();
114 }
115#if __cpp_impl_three_way_comparison
116 template<typename Allocator>
117 constexpr auto operator<=>(const vector_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
118 return lhs.base() <=> rhs.base();
119 }
120#endif // __cpp_impl_three_way_comparison
121
122 template<typename Allocator>
124 using allocator_traits = std::allocator_traits<Allocator>;
125 public:
126 using iterator_category = std::random_access_iterator_tag;
127 using value_type = typename allocator_traits::value_type;
128 using difference_type = std::ptrdiff_t;
129 using pointer = typename allocator_traits::const_pointer;
130 using reference = const value_type&;
131 protected:
132 pointer _current;
133 public:
134 constexpr vector_const_iterator() = default;
135 constexpr vector_const_iterator(const vector_const_iterator& other) = default;
136 constexpr vector_const_iterator(vector_const_iterator&& other) = default;
137 constexpr vector_const_iterator(pointer ptr)
138 : _current(ptr) {}
139 constexpr vector_const_iterator(const vector_iterator<Allocator>& other) // allow only iterator to const_iterator conversion
140 : _current(other.base()) {}
141 constexpr vector_const_iterator& operator=(const vector_const_iterator& other) = default;
142 constexpr vector_const_iterator& operator=(vector_const_iterator&& other) = default;
143 constexpr ~vector_const_iterator() = default;
144 public:
145 constexpr reference operator*() const noexcept {
146 return *_current;
147 }
148 constexpr pointer operator->() const noexcept {
149 return _current;
150 }
151 constexpr vector_const_iterator& operator++() noexcept {
152 ++_current;
153 return *this;
154 }
155 constexpr vector_const_iterator operator++(int) const noexcept {
156 return vector_const_iterator(_current++);
157 }
158 constexpr vector_const_iterator& operator--() noexcept {
159 --_current;
160 return *this;
161 }
162 constexpr vector_const_iterator operator--(int) const noexcept {
163 return vector_const_iterator(_current--);
164 }
165 constexpr vector_const_iterator& operator+=(const difference_type n) noexcept {
166 _current += n;
167 return *this;
168 }
169 constexpr vector_const_iterator operator+(const difference_type n) const noexcept {
171 return temp += n;
172 }
173 constexpr vector_const_iterator& operator-=(const difference_type n) noexcept {
174 _current -= n;
175 return *this;
176 }
177 constexpr vector_const_iterator operator-(const difference_type n) const noexcept {
179 return temp -= n;
180 }
181 constexpr reference operator[](const difference_type n) const noexcept {
182 return _current[n];
183 }
184 template<typename Alloc>
185 constexpr friend typename vector_const_iterator<Alloc>::difference_type operator-(const vector_const_iterator<Alloc>& lhs, const vector_const_iterator<Alloc>& rhs) noexcept;
186 template<typename Alloc>
187 constexpr friend bool operator==(const vector_const_iterator<Alloc>& lhs, const vector_const_iterator<Alloc>& rhs) noexcept;
188#if __cpp_impl_three_way_comparison
189 template<typename Alloc>
190 constexpr friend auto operator<=>(const vector_const_iterator<Alloc>& lhs, const vector_const_iterator<Alloc>& rhs) noexcept;
191#endif // __cpp_impl_three_way_comparison
192 template<typename Alloc>
193 constexpr friend typename vector_const_iterator<Alloc>::difference_type operator-(const vector_const_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
194 template<typename Alloc>
195 constexpr friend bool operator==(const vector_const_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
196#if __cpp_impl_three_way_comparison
197 template<typename Alloc>
198 constexpr friend auto operator<=>(const vector_const_iterator<Alloc>& lhs, const vector_iterator<Alloc>& rhs) noexcept;
199 operator const pointer() const noexcept {
200 return _current;
201 }
202#endif // __cpp_impl_three_way_comparison
203 pointer base() const noexcept {
204 return _current;
205 }
206 };
207
208 template<typename Allocator>
209 constexpr typename vector_const_iterator<Allocator>::difference_type operator-(const vector_const_iterator<Allocator>& lhs, const vector_const_iterator<Allocator>& rhs) noexcept {
210 using difference_type = typename vector_const_iterator<Allocator>::difference_type;
211 return difference_type(lhs.base() - rhs.base());
212 }
213 template<typename Allocator>
214 constexpr bool operator==(const vector_const_iterator<Allocator>& lhs, const vector_const_iterator<Allocator>& rhs) noexcept {
215 return lhs.base() == rhs.base();
216 }
217 template<typename Allocator>
218 constexpr auto operator<=>(const vector_const_iterator<Allocator>& lhs, const vector_const_iterator<Allocator>& rhs) noexcept {
219 return lhs.base() <=> rhs.base();
220 }
221 template<typename Allocator>
222 constexpr typename vector_const_iterator<Allocator>::difference_type operator-(const vector_const_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
223 using difference_type = typename vector_const_iterator<Allocator>::difference_type;
224 return difference_type(lhs.base() - rhs.base());
225 }
226 template<typename Allocator>
227 constexpr bool operator==(const vector_const_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
228 return lhs.base() == rhs.base();
229 }
230 template<typename Allocator>
231 constexpr auto operator<=>(const vector_const_iterator<Allocator>& lhs, const vector_iterator<Allocator>& rhs) noexcept {
232 return lhs.base() <=> rhs.base();
233 }
234
235 namespace detail {
237
238#if PLUGIFY_VECTOR_CONTAINERS_RANGES
239 template<typename Range, typename Type>
240 concept vector_compatible_range = std::ranges::input_range<Range> && std::convertible_to<std::ranges::range_reference_t<Range>, Type>;
241#endif
242 } // namespace detail
243
244 // vector
245 // based on implementations from libc++, libstdc++ and Microsoft STL
246 template<typename T, typename Allocator = plg::allocator<T>>
247 class vector {
248 using allocator_traits = std::allocator_traits<Allocator>;
249 public:
250 using value_type = T;
252 using size_type = typename allocator_traits::size_type;
253 using difference_type = typename allocator_traits::difference_type;
254 using reference = value_type&;
255 using const_reference = const value_type&;
256 using pointer = typename allocator_traits::pointer;
257 using const_pointer = typename allocator_traits::const_pointer;
260 using reverse_iterator = std::reverse_iterator<iterator>;
261 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
262
263 protected:
264 PLUGIFY_NO_UNIQUE_ADDRESS
265 allocator_type _allocator;
266 pointer _begin;
267 pointer _end;
268 pointer _capacity;
269
270 private:
271 constexpr static size_type growth_factor = 2; // When resizing, what number to scale by
272
273 constexpr void copy_constructor(const vector& other) {
274 const size_type capacity = other.capacity();
275 _begin = allocator_traits::allocate(_allocator, capacity);
276 std::uninitialized_copy(other.begin(), other.end(), begin());
277 _end = _begin + other.size();
278 _capacity = _begin + capacity;
279 }
280
281 template<std::input_iterator InputIterator>
282 constexpr void range_constructor(InputIterator first, InputIterator last) {
283 const size_type count = static_cast<size_type>(std::distance(first, last));
284 _begin = allocator_traits::allocate(_allocator, count);
285 std::uninitialized_copy(first, last, _begin);
286 _capacity = _begin + count;
287 _end = _begin + count;
288 }
289
290 constexpr bool is_full() const {
291 return _end == _capacity;
292 }
293
294 constexpr size_type calculate_new_capacity() const {
295 const size_type old_capacity = capacity();
296 return old_capacity == 0 ? 1 : growth_factor * old_capacity;
297 }
298
299 constexpr iterator const_iterator_cast(const_iterator iter) noexcept {
300 return begin() + (iter - cbegin());
301 }
302
303 constexpr void reallocate(size_type new_capacity) {
304 reallocate(new_capacity, [](pointer const) {});
305 }
306
307 template<typename F>
308 constexpr void reallocate(size_type new_capacity, const F& construct) {
309 const size_type old_size = size();
310 const size_type old_capacity = capacity();
311 PLUGIFY_ASSERT(new_capacity >= old_size, "plg::vector::reallocate(): resulted vector size would exceed size()", std::length_error);
313 return;
314
315 pointer const new_begin = allocator_traits::allocate(_allocator, new_capacity);
317 std::uninitialized_move(_begin, _end, new_begin);
318 std::destroy(_begin, _end);
319 allocator_traits::deallocate(_allocator, _begin, capacity());
320 _begin = new_begin;
321 _end = _begin + old_size;
322 _capacity = _begin + new_capacity;
323 }
324
325 template<typename F>
326 constexpr void emplace_at_end(const F& construct) {
327 if (is_full()) {
328 reallocate(calculate_new_capacity(), construct);
329 } else {
330 construct(_begin);
331 }
332 }
333
334 template<typename V>
335 constexpr void resize_to(size_type count, const V& value) {
336 if (count < size()) {
337 std::destroy(_begin + count, _end);
338 _end = _begin + count;
339 } else if (count > size()) {
340 const size_type old_size = size();
341 auto construct = [&](pointer const data) {
342 if constexpr (std::is_same_v<V, T>) {
343 std::uninitialized_fill(data + old_size, data + count, value);
344 } else {
345 std::uninitialized_value_construct(data + old_size, data + count);
346 }
347 };
348 if (count > capacity()) {
349 reallocate(count, construct);
350 } else {
351 construct(_begin);
352 }
353 _end = _begin + count;
354 }
355 }
356
357 constexpr void swap_without_allocator(vector&& other) noexcept {
358 using std::swap;
359 swap(_begin, other._begin);
360 swap(_end, other._end);
361 swap(_capacity, other._capacity);
362 }
363
364 public:
365 // constructor
366 constexpr vector() noexcept(std::is_nothrow_default_constructible<Allocator>::value)
367 : _allocator(Allocator()), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
368 }
369
370 constexpr explicit vector(const Allocator& allocator) noexcept
371 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
372 }
373
374 constexpr vector(size_type count, const T& value, const Allocator& allocator = Allocator())
375 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
376 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::vector(): constructed vector size would exceed max_size()", std::length_error);
377 _begin = allocator_traits::allocate(_allocator, count);
378 std::uninitialized_fill_n(_begin, count, value);
379 _capacity = _begin + count;
380 _end = _begin + count;
381 }
382
383 constexpr explicit vector(size_type count, const Allocator& allocator = Allocator())
384 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
385 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::vector(): constructed vector size would exceed max_size()", std::length_error);
386 _begin = allocator_traits::allocate(_allocator, count);
387 std::uninitialized_value_construct_n(_begin, count);
388 _capacity = _begin + count;
389 _end = _begin + count;
390 }
391
392 template<std::input_iterator InputIterator>
394 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
395 PLUGIFY_ASSERT(static_cast<size_type>(std::distance(first, last)) <= max_size(), "plg::vector::vector(): constructed vector size would exceed max_size()", std::length_error);
396 range_constructor(first, last);
397 }
398
399 constexpr vector(const vector& other)
400 : _allocator(other.get_allocator()), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
401 copy_constructor(other);
402 }
403
404 constexpr vector(const vector& other, const Allocator& allocator)
405 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
406 copy_constructor(other);
407 }
408
409 constexpr vector(vector&& other) noexcept(std::is_nothrow_move_constructible<Allocator>::value)
410 : _allocator(Allocator()), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
411 swap(other);
412 }
413
414 constexpr vector(vector&& other, const Allocator& allocator)
415 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
416 if constexpr (allocator_traits::is_always_equal::value) {
417 swap_without_allocator(std::move(other));
418 } else {
419 if (get_allocator() == other.get_allocator()) {
420 swap_without_allocator(std::move(other));
421 } else {
422 const size_type capacity = other.capacity();
423 _begin = allocator_traits::allocate(_allocator, capacity);
424 std::uninitialized_move(other.begin(), other.end(), begin());
425 _end = _begin + other.size();
426 _capacity = _begin + capacity;
427 }
428 }
429 }
430
431 constexpr vector(std::initializer_list<T> list, const Allocator& allocator = Allocator())
432 : _allocator(allocator), _begin{nullptr}, _end{nullptr}, _capacity{nullptr} {
433 PLUGIFY_ASSERT(list.size() <= max_size(), "plg::vector::vector(): constructed vector size would exceed max_size()", std::length_error);
434 range_constructor(list.begin(), list.end());
435 }
436
437#if PLUGIFY_VECTOR_CONTAINERS_RANGES
438 template<detail::vector_compatible_range<T> Range>
439 constexpr vector(std::from_range_t, Range&& range, const Allocator& alloc = Allocator())
440 : vector(std::ranges::begin(range), std::ranges::end(range), alloc) {}
441#endif // PLUGIFY_VECTOR_CONTAINERS_RANGES
442
443 // destructor
444 constexpr ~vector() {
445 std::destroy(_begin, _end);
446 allocator_traits::deallocate(_allocator, _begin, capacity());
447 }
448
449 // operator=
450 constexpr vector& operator=(const vector& other) {
451 if (this == &other) [[unlikely]] {
452 return *this;
453 }
454
455 clear();
456 if constexpr (allocator_traits::propagate_on_container_copy_assignment::value) {
457 if constexpr (!allocator_traits::is_always_equal::value) {
458 if (get_allocator() != other.get_allocator()) {
459 allocator_traits::deallocate(_allocator, _begin, capacity());
460 }
461 }
462 _allocator = other.get_allocator();
463 }
464
465 assign(other.begin(), other.end());
466 return *this;
467 }
468
469 constexpr vector& operator=(vector&& other) noexcept(
470 std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
471 std::allocator_traits<Allocator>::is_always_equal::value) {
472 if (this == &other) [[unlikely]] {
473 return *this;
474 }
475
476 clear();
477 if constexpr (allocator_traits::propagate_on_container_move_assignment::value) {
478 if constexpr (!allocator_traits::is_always_equal::value) {
479 if (get_allocator() != other.get_allocator()) {
480 allocator_traits::deallocate(_allocator, _begin, capacity());
481 }
482 }
483 _allocator = other.get_allocator();
484 }
485
486 if constexpr (allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) {
487 swap_without_allocator(std::move(other));
488 } else {
489 if (get_allocator() == other.get_allocator()) {
490 swap_without_allocator(std::move(other));
491 } else {
492 assign(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
493 other.clear();
494 }
495 }
496 return *this;
497 }
498
499 constexpr vector& operator=(std::initializer_list<T> list) {
500 assign(list.begin(), list.end());
501 return *this;
502 }
503
504 // assign
505 constexpr void assign(size_type count, const T& value) {
506 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::assign(): resulted vector size would exceed max_size()", std::length_error);
507 if (count > capacity()) {
508 pointer const new_begin = allocator_traits::allocate(_allocator, count);
509 std::uninitialized_fill_n(new_begin, count, value);
510 std::destroy(_begin, _end);
511 allocator_traits::deallocate(_allocator, _begin, capacity());
512 _begin = new_begin;
513 _capacity = _begin + count;
514 } else if (size() >= count) {
515 std::fill_n(_begin, count, value);
516 std::destroy(_begin + count, _end);
517 } else {
518 std::fill_n(_begin, size(), value);
519 std::uninitialized_fill_n(_begin + size(), count - size(), value);
520 }
521 _end = _begin + count;
522 }
523
524 template<std::input_iterator InputIterator>
525 constexpr void assign(InputIterator first, InputIterator last) {
526 const size_type count = static_cast<size_type>(std::distance(first, last));
527 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::assign(): resulted vector size would exceed max_size()", std::length_error);
528 if (count > capacity()) {
529 pointer const new_begin = allocator_traits::allocate(_allocator, count);
530 std::uninitialized_copy(first, last, new_begin);
531 std::destroy(_begin, _end);
532 allocator_traits::deallocate(_allocator, _begin, capacity());
533 _begin = new_begin;
534 _capacity = _begin + count;
535 } else if (size() >= count) {
536 std::copy(first, last, _begin);
537 std::destroy(_begin + count, _end);
538 } else {
539 std::copy(first, first + size(), _begin);
540 std::uninitialized_copy(first + size(), last, _begin + size());
541 }
542 _end = _begin + count;
543 }
544
545 constexpr void assign(std::initializer_list<T> list) {
546 assign(list.begin(), list.end());
547 }
548
549#if PLUGIFY_VECTOR_CONTAINERS_RANGES
550 template<detail::vector_compatible_range<T> Range>
551 constexpr void assign_range(Range&& range) {
552 assign(std::ranges::begin(range), std::ranges::end(range));
553 }
554#endif // PLUGIFY_VECTOR_CONTAINERS_RANGES
555
556 // get_allocator
557 constexpr allocator_type get_allocator() const {
558 return _allocator;
559 }
560
561 // element access
562 constexpr reference at(size_type position) {
563 PLUGIFY_ASSERT(position < size(), "plg::vector::at(): input index is out of bounds", std::out_of_range);
564 return *(_begin + position);
565 }
566
567 constexpr const_reference at(size_type position) const {
568 PLUGIFY_ASSERT(position < size(), "plg::vector::at(): input index is out of bounds", std::out_of_range);
569 return *(_begin + position);
570 }
571
572 constexpr reference operator[](size_type position) noexcept {
573 return *(_begin + position);
574 }
575
576 constexpr const_reference operator[](size_type position) const noexcept {
577 return *(_begin + position);
578 }
579
580 constexpr reference front() {
581 PLUGIFY_ASSERT(!empty(), "plg::vector::front(): vector is empty", std::length_error);
582 return *_begin;
583 }
584
585 constexpr const_reference front() const {
586 PLUGIFY_ASSERT(!empty(), "plg::vector::front(): vector is empty", std::length_error);
587 return *_begin;
588 }
589
590 constexpr reference back() {
591 PLUGIFY_ASSERT(!empty(), "plg::vector::back(): vector is empty", std::length_error);
592 return *(_end - 1);
593 }
594
595 constexpr const_reference back() const {
596 PLUGIFY_ASSERT(!empty(), "plg::vector::back(): vector is empty", std::length_error);
597 return *(_end - 1);
598 }
599
600 constexpr T* data() noexcept {
601 return _begin;
602 }
603
604 constexpr const T* data() const noexcept {
605 return _begin;
606 }
607
608 // iterators
609 constexpr iterator begin() noexcept {
610 return iterator(_begin);
611 }
612
613 constexpr const_iterator begin() const noexcept {
614 return const_iterator(_begin);
615 }
616
617 constexpr const_iterator cbegin() const noexcept {
618 return const_iterator(_begin);
619 }
620
621 constexpr iterator end() noexcept {
622 return iterator(_end);
623 }
624
625 constexpr const_iterator end() const noexcept {
626 return const_iterator(_end);
627 }
628
629 constexpr const_iterator cend() const noexcept {
630 return const_iterator(_end);
631 }
632
633 constexpr reverse_iterator rbegin() noexcept {
634 return reverse_iterator(_end);
635 }
636
637 constexpr const_reverse_iterator rbegin() const noexcept {
638 return const_reverse_iterator(_end);
639 }
640
641 constexpr const_reverse_iterator crbegin() const noexcept {
642 return const_reverse_iterator(_end);
643 }
644
645 constexpr reverse_iterator rend() noexcept {
646 return reverse_iterator(_begin);
647 }
648
649 constexpr const_reverse_iterator rend() const noexcept {
650 return const_reverse_iterator(_begin);
651 }
652
653 constexpr const_reverse_iterator crend() const noexcept {
654 return const_reverse_iterator(_begin);
655 }
656
657 // capacity
658 constexpr bool empty() const {
659 return (_begin == _end);
660 }
661
662 constexpr size_type size() const noexcept {
663 return static_cast<size_type>(_end - _begin);
664 }
665
666 constexpr size_type max_size() const noexcept {
667 return allocator_traits::max_size(_allocator);
668 }
669
670 constexpr void reserve(size_type new_capacity) {
671 PLUGIFY_ASSERT(new_capacity <= max_size(), "plg::vector::reserve(): allocated memory size would exceed max_size()", std::length_error);
672 if (new_capacity > capacity()) {
673 reallocate(new_capacity);
674 }
675 }
676
677 constexpr size_type capacity() const noexcept {
678 return static_cast<size_type>(_capacity - _begin);
679 }
680
681 constexpr void shrink_to_fit() {
682 reallocate(size());
683 }
684
685 // modifiers
686 constexpr void clear() noexcept {
687 std::destroy(_begin, _end);
688 _end = _begin;
689 }
690
691 constexpr iterator insert(const_iterator position, const T& value) {
692 return emplace(position, value);
693 }
694
695 constexpr iterator insert(const_iterator position, T&& value) {
696 return emplace(position, std::move(value));
697 }
698
699 constexpr iterator insert(const_iterator position, size_type count, const T& value) {
700 const size_type sz = size();
701 const size_type new_size = sz + count;
702 PLUGIFY_ASSERT(new_size <= max_size(), "plg::vector::insert(): resulted vector size would exceed max_size()", std::length_error);
703 const size_type position_distance = static_cast<size_type>(position - cbegin());
704 PLUGIFY_ASSERT(position_distance <= sz, "plg::vector::insert(): pos out of range", std::out_of_range);
705 if (count != 0) {
706 if (new_size > capacity()) {
707 pointer const new_begin = allocator_traits::allocate(_allocator, new_size);
708 pointer const old_position = _begin + position_distance;
709 std::uninitialized_move(_begin, old_position, new_begin);
710 pointer const new_position = new_begin + position_distance;
711 std::uninitialized_fill_n(new_position, count, value);
712 std::uninitialized_move(old_position, _end, new_position + count);
713 std::destroy(_begin, _end);
714 allocator_traits::deallocate(_allocator, _begin, capacity());
715 _begin = new_begin;
716 _end = _begin + new_size;
717 _capacity = _end;
718 } else {
719 pointer const pointer_position = _begin + position_distance;
720 std::uninitialized_fill_n(_end, count, value);
721 _end += count;
722 std::rotate(pointer_position, _end - count, _end);
723 }
724 }
725 return begin() + position_distance;
726 }
727
728 template<std::input_iterator InputIterator>
730 const size_type sz = size();
731 const size_type count = static_cast<size_type>(std::distance(first, last));
732 const size_type new_size = sz + count;
733 PLUGIFY_ASSERT(new_size <= max_size(), "plg::vector::insert(): resulted vector size would exceed max_size()", std::length_error);
734 const size_type position_distance = static_cast<size_type>(position - cbegin());
735 PLUGIFY_ASSERT(position_distance <= sz, "plg::vector::insert(): pos out of range", std::out_of_range);
736 if (count != 0) {
737 if (new_size > capacity()) {
738 pointer const new_begin = allocator_traits::allocate(_allocator, new_size);
739 pointer const old_position = _begin + position_distance;
740 pointer const new_position = new_begin + position_distance;
741 std::uninitialized_move(_begin, old_position, new_begin);
742 std::uninitialized_copy(first, last, new_position);
743 std::uninitialized_move(old_position, _end, new_position + count);
744 std::destroy(_begin, _end);
745 allocator_traits::deallocate(_allocator, _begin, capacity());
746 _begin = new_begin;
747 _end = _begin + new_size;
748 _capacity = _end;
749 } else {
750 pointer const pointer_position = _begin + position_distance;
751 std::uninitialized_copy(first, last, _end);
752 _end += count;
753 std::rotate(pointer_position, _end - count, _end);
754 }
755 }
756 return begin() + position_distance;
757 }
758
759 constexpr iterator insert(const_iterator position, std::initializer_list<T> list) {
760 return insert(position, list.begin(), list.end());
761 }
762
763#if PLUGIFY_VECTOR_CONTAINERS_RANGES
764 template<detail::vector_compatible_range<T> Range>
765 constexpr iterator insert_range(const_iterator pos, Range&& range) {
766 return insert(pos - begin(), std::ranges::begin(range), std::ranges::end(range));
767 }
768#endif // PLUGIFY_VECTOR_CONTAINERS_RANGES
769
770 template<typename... Args>
771 iterator emplace(const_iterator position, Args&&... args) {
772 const size_type sz = size();
773 const size_type new_size = sz + 1;
774 PLUGIFY_ASSERT(new_size <= max_size(), "plg::vector::emplace(): resulted vector size would exceed max_size()", std::length_error);
775 const size_type position_distance = static_cast<size_type>(position - cbegin());
776 PLUGIFY_ASSERT(position_distance <= sz, "plg::vector::emplace(): pos out of range", std::out_of_range);
777 if (position == cend()) {
778 emplace_back(std::forward<Args>(args)...);
779 } else {
780 if (is_full()) {
781 const size_type new_capacity = calculate_new_capacity();
782 pointer const new_begin = allocator_traits::allocate(_allocator, new_capacity);
783 pointer const old_position = _begin + position_distance;
784 pointer const new_position = new_begin + position_distance;
785 std::uninitialized_move(_begin, old_position, new_begin);
786 std::construct_at(new_position, std::forward<Args>(args)...);
787 std::uninitialized_move(old_position, _end, new_position + 1);
788 std::destroy(_begin, _end);
789 allocator_traits::deallocate(_allocator, _begin, capacity());
790 _begin = new_begin;
791 _end = _begin + new_size;
792 _capacity = _begin + new_capacity;
793 } else {
794 pointer const pointer_position = _begin + position_distance;
795 std::construct_at(_end, std::forward<Args>(args)...);
796 ++_end;
797 std::rotate(pointer_position, _end - 1, _end);
798 }
799 }
800 return begin() + position_distance;
801 }
802
803 constexpr iterator erase(const_iterator position) {
804 iterator nonconst_position = const_iterator_cast(position);
805 if (nonconst_position + 1 != end()) {
806 std::rotate(nonconst_position, nonconst_position + 1, end());
807 }
808 --_end;
809 std::destroy_at(_end);
810 return nonconst_position;
811 }
812
814 PLUGIFY_ASSERT(first <= last, "plg::vector::erase(): called with invalid range", std::out_of_range);
815 iterator nonconst_first = const_iterator_cast(first);
816 iterator nonconst_last = const_iterator_cast(last);
818 if (nonconst_last != end()) {
819 std::rotate(nonconst_first, nonconst_last, end());
820 }
821 _end = nonconst_first.base() + static_cast<size_type>(end() - nonconst_last);
822 std::destroy(_end, _end + static_cast<size_type>(std::distance(first, last)));
823 }
824 return nonconst_first;
825 }
826
827 constexpr void push_back(const T& value) {
828 const size_type sz = size();
829 PLUGIFY_ASSERT(sz + 1 <= max_size(), "plg::vector::push_back(): resulted vector size would exceed max_size()", std::length_error);
830 emplace_at_end([&](pointer const data) {
831 std::construct_at(data + sz, value);
832 });
833 ++_end;
834 }
835
836 constexpr void push_back(T&& value) {
837 const size_type sz = size();
838 PLUGIFY_ASSERT(sz + 1 <= max_size(), "plg::vector::push_back(): resulted vector size would exceed max_size()", std::length_error);
839 emplace_at_end([&](pointer const data) {
840 std::construct_at(data + sz, std::move(value));
841 });
842 ++_end;
843 }
844
845 template<typename... Args>
846 constexpr reference emplace_back(Args&&... args) {
847 const size_type sz = size();
848 PLUGIFY_ASSERT(sz + 1 <= max_size(), "plg::vector::emplace_back(): resulted vector size would exceed max_size()", std::length_error);
849 emplace_at_end([&](pointer const data) {
850 std::construct_at(data + sz, std::forward<Args>(args)...);
851 });
852 ++_end;
853 return back();
854 }
855
856 constexpr void pop_back() {
857 PLUGIFY_ASSERT(!empty(), "plg::vector::pop_back(): vector is empty", std::length_error);
858 --_end;
859 std::destroy_at(_end);
860 }
861
862 constexpr void resize(size_type count) {
863 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::resize(): allocated memory size would exceed max_size()", std::length_error);
865 }
866
867 constexpr void resize(size_type count, const T& value) {
868 PLUGIFY_ASSERT(count <= max_size(), "plg::vector::resize(): allocated memory size would exceed max_size()", std::length_error);
869 resize_to(count, value);
870 }
871
872 constexpr vector& operator+=(const T& value) {
873 push_back(value);
874 return *this;
875 }
876
877 constexpr vector& operator+=(T&& value) {
878 push_back(std::move(value));
879 return *this;
880 }
881
882 constexpr vector& operator+=(const vector& other) {
883 insert(end(), other.begin(), other.end());
884 return *this;
885 }
886
887 constexpr vector& operator+=(vector&& other) {
888 if (this == &other) [[unlikely]] {
889 return *this;
890 }
891
892 insert(end(), std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
893 return *this;
894 }
895
896#if PLUGIFY_VECTOR_CONTAINERS_RANGES
897 template<detail::vector_compatible_range<T> Range>
898 constexpr void append_range(Range&& range) {
899 return insert(end(), std::ranges::begin(range), std::ranges::end(range));
900 }
901#endif // PLUGIFY_VECTOR_CONTAINERS_RANGES
902
903 constexpr void swap(vector& other) noexcept(std::allocator_traits<Allocator>::propagate_on_container_swap::value || std::allocator_traits<Allocator>::is_always_equal::value) {
904 using std::swap;
905 if constexpr (allocator_traits::propagate_on_container_swap::value) {
906 swap(_allocator, other._allocator);
907 }
908 swap(_begin, other._begin);
909 swap(_end, other._end);
910 swap(_capacity, other._capacity);
911 }
912
913 constexpr std::span<const T> span() const noexcept {
914 return std::span<const T>(data(), size());
915 }
916
917 constexpr std::span<T> span() noexcept {
918 return std::span<T>(data(), size());
919 }
920
921 template<size_type Size>
922 constexpr std::span<T, Size> span_size() {
923 PLUGIFY_ASSERT(size() == Size, "plg::vector::span_size(): const_span_size argument does not match size of vector", std::length_error);
924 return std::span<T, Size>(data(), size());
925 }
926
927 template<size_type Size>
928 constexpr std::span<const T, Size> const_span_size() const {
929 PLUGIFY_ASSERT(size() == Size, "plg::vector::const_span_size(): const_span_size argument does not match size of vector", std::length_error);
930 return std::span<const T, Size>(data(), size());
931 }
932
933 constexpr std::span<const std::byte> byte_span() const noexcept {
934 return std::as_bytes(span());
935 }
936
937 constexpr std::span<std::byte> byte_span() noexcept {
938 return std::as_writable_bytes(span());
939 }
940
941 constexpr bool contains(const T& elem) const {
942 return std::find(begin(), end(), elem) != end();
943 }
944
945 template<typename F>
946 constexpr bool contains_if(F predicate) {
947 return std::find_if(begin(), end(), predicate) != end();
948 }
949
950 constexpr auto find(const T& value) const {
951 return std::find(begin(), end(), value);
952 }
953
954 constexpr auto find(const T& value) {
955 return std::find(begin(), end(), value);
956 }
957
958 template<typename F>
959 constexpr auto find_if(F predicate) const {
960 return std::find_if(begin(), end(), predicate);
961 }
962
963 template<typename F>
964 constexpr auto find_if(F predicate) {
965 return std::find_if(begin(), end(), predicate);
966 }
967
968 constexpr std::optional<size_type> find_index(const T& value) {
969 const auto iter = std::find(begin(), end(), value);
970 if (iter == end()) {
971 return {};
972 } else {
973 return iter - begin();
974 }
975 }
976
977 constexpr std::optional<size_type> find_index(const T& value) const {
978 const auto iter = std::find(begin(), end(), value);
979 if (iter == end()) {
980 return {};
981 } else {
982 return iter - begin();
983 }
984 }
985
986 template<typename F>
987 constexpr std::optional<size_type> find_index_if(F predicate) {
988 const auto iter = std::find_if(begin(), end(), predicate);
989 if (iter == end()) {
990 return {};
991 } else {
992 return iter - begin();
993 }
994 }
995
996 template<typename F>
997 constexpr std::optional<size_type> find_index_if(F predicate) const {
998 const auto iter = std::find_if(begin(), end(), predicate);
999 if (iter == end()) {
1000 return {};
1001 } else {
1002 return iter - begin();
1003 }
1004 }
1005 };
1006
1007 // comparisons
1008 template<typename T, typename Allocator>
1009 constexpr bool operator==(const vector<T, Allocator>& lhs, const vector<T, Allocator>& rhs) {
1010 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1011 }
1012
1013 template<typename T, typename Allocator>
1014 constexpr auto operator<=>(const vector<T, Allocator>& lhs, const vector<T, Allocator>& rhs) {
1015 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1016 }
1017
1018 // global swap for vector
1019 template<typename T, typename Allocator>
1020 constexpr void swap(vector<T, Allocator>& lhs, vector<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
1021 lhs.swap(rhs);
1022 }
1023
1024 template<typename T, typename Allocator, typename U>
1025 constexpr typename vector<T, Allocator>::size_type erase(vector<T, Allocator>& c, const U& value) {
1026 auto it = std::remove(c.begin(), c.end(), value);
1027 auto r = std::distance(it, c.end());
1028 c.erase(it, c.end());
1029 return r;
1030 }
1031
1032 template<typename T, typename Allocator, typename Pred>
1033 constexpr typename vector<T, Allocator>::size_type erase_if(vector<T, Allocator>& c, Pred pred) {
1034 auto it = std::remove_if(c.begin(), c.end(), pred);
1035 auto r = std::distance(it, c.end());
1036 c.erase(it, c.end());
1037 return r;
1038 }
1039
1040 // deduction guides
1041 template<typename InputIterator, typename Allocator = plg::allocator<typename std::iterator_traits<InputIterator>::value_type>>
1042 vector(InputIterator, InputIterator, Allocator = Allocator()) -> vector<typename std::iterator_traits<InputIterator>::value_type, Allocator>;
1043
1044 namespace pmr {
1045 template<typename T>
1047 } // namespace pmr
1048
1049} // namespace plg