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