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