sparrow 0.6.0
Loading...
Searching...
No Matches
buffer.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <algorithm>
18#include <concepts>
19#include <iterator>
20#include <memory>
21#include <ranges>
22#include <stdexcept>
23#include <type_traits>
24
29
30#if not defined(SPARROW_BUFFER_GROWTH_FACTOR)
31# define SPARROW_BUFFER_GROWTH_FACTOR 2
32#endif
33
34namespace sparrow
35{
43 template <class T>
45 {
46 protected:
47
49 using alloc_traits = std::allocator_traits<allocator_type>;
50 using pointer = typename alloc_traits::pointer;
51 using size_type = typename alloc_traits::size_type;
52
54 {
55 pointer p_begin = nullptr;
56 pointer p_end = nullptr;
58
59 buffer_data() = default;
60 constexpr buffer_data(buffer_data&&) noexcept;
61 constexpr buffer_data& operator=(buffer_data&&) noexcept;
62 };
63
64 buffer_base() = default;
65
66 template <allocator A>
67 constexpr buffer_base(const A& a) noexcept;
68
69 template <allocator A = allocator_type>
70 constexpr buffer_base(size_type n, const A& a = A());
71
72 template <allocator A = allocator_type>
73 constexpr buffer_base(pointer p, size_type n, const A& a = A());
74
76
77 buffer_base(buffer_base&&) noexcept = default;
78
79 template <allocator A>
80 constexpr buffer_base(buffer_base&& rhs, const A& a);
81
82 [[nodiscard]] constexpr allocator_type& get_allocator() noexcept;
83 [[nodiscard]] constexpr const allocator_type& get_allocator() const noexcept;
84 [[nodiscard]] constexpr buffer_data& get_data() noexcept;
85 [[nodiscard]] constexpr const buffer_data& get_data() const noexcept;
86
88 constexpr void deallocate(pointer p, size_type n);
89 constexpr void create_storage(size_type n);
90 constexpr void assign_storage(pointer p, size_type n, size_type cap);
91
92 private:
93
94 allocator_type m_alloc;
95 buffer_data m_data;
96 };
97
107 template <class T>
108 class buffer : private buffer_base<T>
109 {
110 using base_type = buffer_base<T>;
111 using alloc_traits = typename base_type::alloc_traits;
112
113 static_assert(
114 std::same_as<std::remove_cvref_t<T>, T>,
115 "buffer must have a non-const, non-volatile, non-reference value_type"
116 );
117
118 public:
119
121 using value_type = T;
124 using pointer = typename alloc_traits::pointer;
125 using const_pointer = typename alloc_traits::const_pointer;
126 using size_type = typename alloc_traits::size_type;
127 using difference_type = typename alloc_traits::difference_type;
130 using reverse_iterator = std::reverse_iterator<iterator>;
131 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
132
133 buffer() = default;
134
135 template <class A>
136 requires(not std::same_as<A, buffer<T>> and allocator<A>)
137 constexpr explicit buffer(const A& a)
138 : base_type(a)
139 {
140 }
141
142 template <allocator A = allocator_type>
143 constexpr explicit buffer(size_type n, const A& a = A());
144
145 template <allocator A = allocator_type>
146 constexpr buffer(size_type n, const value_type& v, const A& a = A());
147
148 template <allocator A = allocator_type>
149 constexpr buffer(pointer p, size_type n, const A& a = A());
150
151 template <allocator A = allocator_type>
152 constexpr buffer(std::initializer_list<value_type> init, const A& a = A());
153
154 template <class It, allocator A = allocator_type>
155 constexpr buffer(It first, It last, const A& a = A());
156
157 template <std::ranges::input_range Range, allocator A = allocator_type>
158 requires std::same_as<std::ranges::range_value_t<Range>, T>
159 constexpr buffer(const Range& range, const A& a = A());
160
162
163 buffer(const buffer& rhs);
164
165 template <allocator A>
166 buffer(const buffer& rhs, const A& a);
167
168 buffer(buffer&& rhs) noexcept = default;
169
170 template <allocator A>
171 buffer(buffer&& rhs, const A& a);
172
173 constexpr buffer& operator=(const buffer& rhs);
174 constexpr buffer& operator=(buffer&& rhs);
175 constexpr buffer& operator=(std::initializer_list<value_type> init);
176
177 // Element access
178
179 [[nodiscard]] constexpr reference operator[](size_type i);
180 [[nodiscard]] constexpr const_reference operator[](size_type i) const;
181
182 [[nodiscard]] constexpr reference front();
183 [[nodiscard]] constexpr const_reference front() const;
184
185 [[nodiscard]] constexpr reference back();
186 [[nodiscard]] constexpr const_reference back() const;
187
188 // TODO: make this non template and make a buffer_caster class
189 template <class U = T>
190 [[nodiscard]] constexpr U* data() noexcept;
191
192 // TODO: make this non template and make a buffer_caster class
193 template <class U = T>
194 [[nodiscard]] constexpr const U* data() const noexcept;
195
196 // Iterators
197
198 [[nodiscard]] constexpr iterator begin() noexcept;
199 [[nodiscard]] constexpr iterator end() noexcept;
200
201 [[nodiscard]] constexpr const_iterator begin() const noexcept;
202 [[nodiscard]] constexpr const_iterator end() const noexcept;
203
204 [[nodiscard]] constexpr const_iterator cbegin() const noexcept;
205 [[nodiscard]] constexpr const_iterator cend() const noexcept;
206
207 [[nodiscard]] constexpr reverse_iterator rbegin() noexcept;
208 [[nodiscard]] constexpr reverse_iterator rend() noexcept;
209
210 [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept;
211 [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept;
212
213 [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept;
214 [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept;
215 [[nodiscard]]
216 // Capacity
217
218 [[nodiscard]] constexpr bool
219 empty() const noexcept;
220 [[nodiscard]] constexpr size_type capacity() const noexcept;
221 [[nodiscard]] constexpr size_type size() const noexcept;
222 [[nodiscard]] constexpr size_type max_size() const noexcept;
223 constexpr void reserve(size_type new_cap);
224 constexpr void shrink_to_fit();
225
226 // Modifiers
227
228 constexpr void clear();
229
230 constexpr iterator insert(const_iterator pos, const T& value);
231 constexpr iterator insert(const_iterator pos, T&& value);
232 constexpr iterator insert(const_iterator pos, size_type count, const T& value);
233 template <mpl::iterator_of_type<T> InputIt>
234 constexpr iterator insert(const_iterator pos, InputIt first, InputIt last);
235 template <std::ranges::input_range R>
236 requires std::same_as<std::ranges::range_value_t<R>, T>
237 constexpr iterator insert(const_iterator pos, R&& range);
238 constexpr iterator insert(const_iterator pos, std::initializer_list<T> ilist);
239
240 template <class... Args>
241 constexpr iterator emplace(const_iterator pos, Args&&... args);
242
245
246 constexpr void push_back(const T& value);
247 constexpr void push_back(T&& value);
248
249 constexpr void pop_back();
250
251 constexpr void resize(size_type new_size);
252 constexpr void resize(size_type new_size, const value_type& value);
253 constexpr void swap(buffer& rhs) noexcept;
254
255 private:
256
257 using base_type::get_allocator;
258 using base_type::get_data;
259
260 template <class F>
261 constexpr void resize_impl(size_type new_size, F&& initializer);
262
263 template <class It>
264 constexpr void assign_range_impl(It first, It last, std::forward_iterator_tag);
265
266 constexpr void erase_at_end(pointer p);
267
268 template <class It>
269 constexpr pointer allocate_and_copy(size_type n, It first, It last);
270
271 constexpr void reserve_with_growth_factor(size_type new_cap);
272
273 // The following methods are static because:
274 // - they accept an allocator argument, and do not depend on
275 // the state of buffer
276 // - taking an allocator argument instead of relying on get_allocator
277 // will make it easier to support allocators that propagate
278 // on copy / move, as these methods will be called with allocators
279 // from different instances of buffers.
280
281 static constexpr size_type check_init_length(size_type n, const allocator_type& a);
282
283 [[nodiscard]] static constexpr size_type max_size_impl(const allocator_type& a) noexcept;
284
285 [[nodiscard]] static constexpr pointer
286 default_initialize(pointer begin, size_type n, allocator_type& a);
287
288 static constexpr pointer
289 fill_initialize(pointer begin, size_type n, const value_type& v, allocator_type& a);
290
291 template <class It>
292 static constexpr pointer copy_initialize(It first, It last, pointer begin, allocator_type& a);
293
294 static constexpr void destroy(pointer first, pointer last, allocator_type& a);
295 };
296
297 template <class T>
298 constexpr bool operator==(const buffer<T>& lhs, const buffer<T>& rhs) noexcept;
299
300 /******************************
301 * buffer_base implementation *
302 ******************************/
303
304 template <class T>
305 constexpr buffer_base<T>::buffer_data::buffer_data(buffer_data&& rhs) noexcept
306 : p_begin(rhs.p_begin)
307 , p_end(rhs.p_end)
309 {
310 rhs.p_begin = nullptr;
311 rhs.p_end = nullptr;
312 rhs.p_storage_end = nullptr;
313 }
314
315 template <class T>
317 {
318 std::swap(p_begin, rhs.p_begin);
319 std::swap(p_end, rhs.p_end);
320 std::swap(p_storage_end, rhs.p_storage_end);
321 return *this;
322 }
323
324 template <class T>
325 template <allocator A>
326 constexpr buffer_base<T>::buffer_base(const A& a) noexcept
327 : m_alloc(a)
328 {
329 }
330
331 template <class T>
332 template <allocator A>
333 constexpr buffer_base<T>::buffer_base(size_type n, const A& a)
334 : m_alloc(a)
335 {
337 }
338
339 template <class T>
340 template <allocator A>
342 : m_alloc(a)
343 {
344 SPARROW_ASSERT_TRUE((p != nullptr) || (p == nullptr && n == 0));
345 assign_storage(p, n, n);
346 }
347
348 template <class T>
350 {
351 deallocate(m_data.p_begin, static_cast<size_type>(m_data.p_storage_end - m_data.p_begin));
352 }
353
354 template <class T>
355 template <allocator A>
356 constexpr buffer_base<T>::buffer_base(buffer_base&& rhs, const A& a)
357 : m_alloc(a)
358 , m_data(std::move(rhs.m_data))
359 {
360 }
361
362 template <class T>
363 constexpr auto buffer_base<T>::get_allocator() noexcept -> allocator_type&
364 {
365 return m_alloc;
366 }
367
368 template <class T>
369 constexpr auto buffer_base<T>::get_allocator() const noexcept -> const allocator_type&
370 {
371 return m_alloc;
372 }
373
374 template <class T>
375 constexpr auto buffer_base<T>::get_data() noexcept -> buffer_data&
376 {
377 return m_data;
378 }
379
380 template <class T>
381 constexpr auto buffer_base<T>::get_data() const noexcept -> const buffer_data&
382 {
383 return m_data;
384 }
385
386 template <class T>
388 {
389 return alloc_traits::allocate(m_alloc, n);
390 }
391
392 template <class T>
394 {
395 alloc_traits::deallocate(m_alloc, p, n);
396 }
397
398 template <class T>
400 {
401 m_data.p_begin = allocate(n);
402 m_data.p_end = m_data.p_begin + n;
403 m_data.p_storage_end = m_data.p_end;
404 }
405
406 template <class T>
408 {
409 SPARROW_ASSERT_TRUE(n <= cap);
410 m_data.p_begin = p;
411 m_data.p_end = p + n;
412 m_data.p_storage_end = p + cap;
413 }
414
415 /*************************
416 * buffer implementation *
417 *************************/
418
419 template <class T>
420 template <allocator A>
421 constexpr buffer<T>::buffer(size_type n, const A& a)
422 : base_type(check_init_length(n, a), a)
423 {
424 get_data().p_end = default_initialize(get_data().p_begin, n, get_allocator());
425 }
426
427 template <class T>
428 template <allocator A>
429 constexpr buffer<T>::buffer(size_type n, const value_type& v, const A& a)
430 : base_type(check_init_length(n, a), a)
431 {
432 get_data().p_end = fill_initialize(get_data().p_begin, n, v, get_allocator());
433 }
434
435 template <class T>
436 template <allocator A>
437 constexpr buffer<T>::buffer(pointer p, size_type n, const A& a)
438 : base_type(p, check_init_length(n, a), a)
439 {
440 }
441
442 template <class T>
443 template <allocator A>
444 constexpr buffer<T>::buffer(std::initializer_list<value_type> init, const A& a)
445 : base_type(check_init_length(init.size(), a), a)
446 {
447 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin, get_allocator());
448 }
449
450 template <class T>
451 template <class It, allocator A>
452 constexpr buffer<T>::buffer(It first, It last, const A& a)
453 : base_type(check_init_length(static_cast<size_type>(std::distance(first, last)), a), a)
454 {
455 get_data().p_end = copy_initialize(first, last, get_data().p_begin, get_allocator());
456 }
457
458 template <class T>
459 template <std::ranges::input_range Range, allocator A>
460 requires std::same_as<std::ranges::range_value_t<Range>, T>
461 constexpr buffer<T>::buffer(const Range& range, const A& a)
462 : base_type(check_init_length(static_cast<size_type>(std::ranges::size(range)), a), a)
463 {
464 get_data().p_end = copy_initialize(
465 std::ranges::begin(range),
466 std::ranges::end(range),
467 get_data().p_begin,
468 get_allocator()
469 );
470 }
471
472 template <class T>
474 {
475 destroy(get_data().p_begin, get_data().p_end, get_allocator());
476 }
477
478 template <class T>
480 : base_type(rhs.get_allocator())
481 {
482 if (rhs.get_data().p_begin != nullptr)
483 {
484 this->create_storage(rhs.size());
485 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
486 }
487 }
488
489 template <class T>
490 template <allocator A>
491 buffer<T>::buffer(const buffer& rhs, const A& a)
492 : base_type(a)
493 {
494 if (rhs.get_data().p_begin != nullptr)
495 {
496 this->create_storage(rhs.size());
497 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
498 }
499 }
500
501 template <class T>
502 template <allocator A>
503 buffer<T>::buffer(buffer&& rhs, const A& a)
504 : base_type(a)
505 {
506 if (rhs.get_allocator() == get_allocator())
507 {
508 get_data() = std::move(rhs.m_data);
509 }
510 else if (!rhs.empty())
511 {
512 if (rhs.get_data().p_begin != nullptr)
513 {
514 this->create_storage(rhs.size());
515 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
516 }
517 rhs.clear();
518 }
519 }
520
521 template <class T>
522 constexpr buffer<T>& buffer<T>::operator=(const buffer& rhs)
523 {
524 if (std::addressof(rhs) != this)
525 {
526 if (rhs.get_data().p_begin != nullptr)
527 {
528 // We assume that any_allocator never propagates on assign
529 assign_range_impl(rhs.get_data().p_begin, rhs.get_data().p_end, std::random_access_iterator_tag());
530 }
531 else
532 {
533 clear();
534 this->deallocate(
535 get_data().p_begin,
536 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
537 );
538 this->assign_storage(nullptr, 0, 0);
539 }
540 }
541 return *this;
542 }
543
544 template <class T>
546 {
547 if (get_allocator() == rhs.get_allocator())
548 {
549 get_data() = std::move(rhs.get_data());
550 }
551 else
552 {
553 if (rhs.get_data().p_begin != nullptr)
554 {
555 assign_range_impl(
556 std::make_move_iterator(rhs.begin()),
557 std::make_move_iterator(rhs.end()),
558 std::random_access_iterator_tag()
559 );
560 }
561 else
562 {
563 clear();
564 }
565
566 rhs.clear();
567 }
568 return *this;
569 }
570
571 template <class T>
572 constexpr buffer<T>& buffer<T>::operator=(std::initializer_list<value_type> init)
573 {
574 assign_range_impl(
575 std::make_move_iterator(init.begin()),
576 std::make_move_iterator(init.end()),
577 std::random_access_iterator_tag()
578 );
579 return *this;
580 }
581
582 template <class T>
584 {
586 return get_data().p_begin[i];
587 }
588
589 template <class T>
591 {
593 return get_data().p_begin[i];
594 }
595
596 template <class T>
597 constexpr auto buffer<T>::front() -> reference
598 {
600 return *(get_data().p_begin);
601 }
602
603 template <class T>
604 constexpr auto buffer<T>::front() const -> const_reference
605 {
607 return *(get_data().p_begin);
608 }
609
610 template <class T>
611 constexpr auto buffer<T>::back() -> reference
612 {
614 return *(get_data().p_end - 1);
615 }
616
617 template <class T>
618 constexpr auto buffer<T>::back() const -> const_reference
619 {
621 return *(get_data().p_end - 1);
622 }
623
624 template <class T>
625 template <class U>
626 constexpr U* buffer<T>::data() noexcept
627 {
628#if defined(__GNUC__)
629# pragma GCC diagnostic push
630# pragma GCC diagnostic ignored "-Wcast-align"
631#endif
632 return reinterpret_cast<U*>(get_data().p_begin);
633#if defined(__GNUC__)
634# pragma GCC diagnostic pop
635#endif
636 }
637
638 template <class T>
639 template <class U>
640 constexpr const U* buffer<T>::data() const noexcept
641 {
642#if defined(__GNUC__)
643# pragma GCC diagnostic push
644# pragma GCC diagnostic ignored "-Wcast-align"
645#endif
646 return reinterpret_cast<U*>(get_data().p_begin);
647#if defined(__GNUC__)
648# pragma GCC diagnostic pop
649#endif
650 }
651
652 template <class T>
653 constexpr auto buffer<T>::begin() noexcept -> iterator
654 {
655 return make_pointer_iterator(get_data().p_begin);
656 }
657
658 template <class T>
659 constexpr auto buffer<T>::end() noexcept -> iterator
660 {
661 return make_pointer_iterator(get_data().p_end);
662 }
663
664 template <class T>
665 constexpr auto buffer<T>::begin() const noexcept -> const_iterator
666 {
667 return cbegin();
668 }
669
670 template <class T>
671 constexpr auto buffer<T>::end() const noexcept -> const_iterator
672 {
673 return cend();
674 }
675
676 template <class T>
677 constexpr auto buffer<T>::cbegin() const noexcept -> const_iterator
678 {
679 return make_pointer_iterator(const_pointer(get_data().p_begin));
680 }
681
682 template <class T>
683 constexpr auto buffer<T>::cend() const noexcept -> const_iterator
684 {
685 return make_pointer_iterator(const_pointer(get_data().p_end));
686 }
687
688 template <class T>
689 constexpr auto buffer<T>::rbegin() noexcept -> reverse_iterator
690 {
691 return reverse_iterator(end());
692 }
693
694 template <class T>
695 constexpr auto buffer<T>::rend() noexcept -> reverse_iterator
696 {
697 return reverse_iterator(begin());
698 }
699
700 template <class T>
701 constexpr auto buffer<T>::rbegin() const noexcept -> const_reverse_iterator
702 {
703 return crbegin();
704 }
705
706 template <class T>
707 constexpr auto buffer<T>::rend() const noexcept -> const_reverse_iterator
708 {
709 return crend();
710 }
711
712 template <class T>
713 constexpr auto buffer<T>::crbegin() const noexcept -> const_reverse_iterator
714 {
715 return const_reverse_iterator(end());
716 }
717
718 template <class T>
719 constexpr auto buffer<T>::crend() const noexcept -> const_reverse_iterator
720 {
722 }
723
724 template <class T>
725 constexpr bool buffer<T>::empty() const noexcept
726 {
727 return get_data().p_begin == get_data().p_end;
728 }
729
730 template <class T>
731 constexpr auto buffer<T>::capacity() const noexcept -> size_type
732 {
733 return static_cast<size_type>(get_data().p_storage_end - get_data().p_begin);
734 }
735
736 template <class T>
737 constexpr auto buffer<T>::size() const noexcept -> size_type
738 {
739 return static_cast<size_type>(get_data().p_end - get_data().p_begin);
740 }
741
742 template <class T>
743 constexpr auto buffer<T>::max_size() const noexcept -> size_type
744 {
745 return max_size_impl(get_allocator());
746 }
747
748 template <class T>
749 constexpr void buffer<T>::reserve(size_type new_cap)
750 {
751 if (new_cap > max_size())
752 {
753 throw std::length_error("buffer::reserve called with new_cap > max_size()");
754 }
755 if (new_cap > capacity())
756 {
757 if (data() == nullptr)
758 {
759 this->create_storage(new_cap);
760 get_data().p_end = get_data().p_begin;
761 }
762 else
763 {
764 const size_type old_size = size();
765 pointer tmp = allocate_and_copy(
766 new_cap,
767 std::make_move_iterator(get_data().p_begin),
768 std::make_move_iterator(get_data().p_end)
769 );
770 destroy(get_data().p_begin, get_data().p_end, get_allocator());
771 this->deallocate(
772 get_data().p_begin,
773 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
774 );
775 this->assign_storage(tmp, old_size, new_cap);
776 }
777 }
778 }
779
780 template <class T>
781 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
782 {
783 if (new_cap > capacity())
784 {
785 reserve(new_cap * SPARROW_BUFFER_GROWTH_FACTOR);
786 }
787 }
788
789 template <class T>
791 {
792 if (capacity() != size())
793 {
794 buffer(std::make_move_iterator(begin()), std::make_move_iterator(end()), get_allocator()).swap(*this);
795 }
796 }
797
798 template <class T>
799 constexpr void buffer<T>::clear()
800 {
801 if (get_data().p_begin != nullptr)
802 {
803 erase_at_end(get_data().p_begin);
804 }
805 }
806
807 template <class T>
808 constexpr auto buffer<T>::insert(const_iterator pos, const T& value) -> iterator
809 {
810 SPARROW_ASSERT_TRUE(cbegin() <= pos);
811 SPARROW_ASSERT_TRUE(pos <= cend());
812 return emplace(pos, value);
813 }
814
815 template <class T>
816 constexpr auto buffer<T>::insert(const_iterator pos, T&& value) -> iterator
817 {
818 SPARROW_ASSERT_TRUE(cbegin() <= pos);
819 SPARROW_ASSERT_TRUE(pos <= cend());
820 return emplace(pos, std::move(value));
821 }
822
823 template <class T>
824 constexpr auto buffer<T>::insert(const_iterator pos, size_type count, const T& value) -> iterator
825 {
826 SPARROW_ASSERT_TRUE(cbegin() <= pos);
827 SPARROW_ASSERT_TRUE(pos <= cend());
828
829 const difference_type offset = std::distance(cbegin(), pos);
830 if (count != 0)
831 {
832 reserve_with_growth_factor(size() + count);
833 const iterator it = std::next(begin(), offset);
834 std::move_backward(it, end(), std::next(end(), static_cast<difference_type>(count)));
835 std::fill_n(it, count, value);
836 get_data().p_end += count;
837 }
838 return std::next(begin(), offset);
839 }
840
841 template <typename T>
842 struct is_move_iterator : std::false_type
843 {
844 };
845
846 template <typename Iterator>
847 struct is_move_iterator<std::move_iterator<Iterator>> : std::true_type
848 {
849 };
850
851 template <typename T>
853
854 template <class T>
855 template <mpl::iterator_of_type<T> InputIt>
856 constexpr auto buffer<T>::insert(const_iterator pos, InputIt first, InputIt last) -> iterator
857 {
858 SPARROW_ASSERT_TRUE(cbegin() <= pos && pos <= cend());
859 const difference_type num_elements = std::distance(first, last);
860 const size_type new_size = size() + static_cast<size_type>(num_elements);
861 const difference_type offset = std::distance(cbegin(), pos);
862 const size_type old_size = size();
863 reserve_with_growth_factor(new_size);
864 resize(new_size);
865 const iterator new_pos = std::next(begin(), offset);
866 const iterator end_it = std::next(begin(), static_cast<difference_type>(old_size));
867 std::move_backward(new_pos, end_it, end());
868 if constexpr (is_move_iterator_v<InputIt>)
869 {
870 std::uninitialized_move(first, last, new_pos);
871 }
872 else
873 {
874 std::uninitialized_copy(first, last, new_pos);
875 }
876 return new_pos;
877 }
878
879 template <class T>
880 template <std::ranges::input_range R>
881 requires std::same_as<std::ranges::range_value_t<R>, T>
882 constexpr auto buffer<T>::insert(const_iterator pos, R&& range) -> iterator
883 {
884 SPARROW_ASSERT_TRUE(cbegin() <= pos);
885 SPARROW_ASSERT_TRUE(pos <= cend());
886 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
887 }
888
889 template <class T>
890 constexpr auto buffer<T>::insert(const_iterator pos, std::initializer_list<T> ilist) -> iterator
891 {
892 SPARROW_ASSERT_TRUE(cbegin() <= pos);
893 SPARROW_ASSERT_TRUE(pos <= cend());
894 return insert(pos, ilist.begin(), ilist.end());
895 }
896
897 template <class T>
898 template <class... Args>
900 {
901 SPARROW_ASSERT_TRUE(cbegin() <= pos);
902 SPARROW_ASSERT_TRUE(pos <= cend());
903 const difference_type offset = std::distance(cbegin(), pos);
904 reserve_with_growth_factor(size() + 1);
905 pointer p = get_data().p_begin + offset;
906 if (p != get_data().p_end)
907 {
908 alloc_traits::construct(get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
909 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
910 alloc_traits::construct(get_allocator(), p, std::forward<Args>(args)...);
911 }
912 else
913 {
914 alloc_traits::construct(get_allocator(), get_data().p_end, std::forward<Args>(args)...);
915 }
916 ++get_data().p_end;
917 return iterator(p);
918 }
919
920 template <class T>
922 {
923 SPARROW_ASSERT_TRUE(cbegin() <= pos);
924 SPARROW_ASSERT_TRUE(pos < cend());
925 return erase(pos, pos + 1);
926 }
927
928 template <class T>
930 {
931 SPARROW_ASSERT_TRUE(first < last);
932 SPARROW_ASSERT_TRUE(cbegin() <= first);
933 SPARROW_ASSERT_TRUE(last <= cend());
934 const difference_type offset = std::distance(cbegin(), first);
935 const difference_type len = std::distance(first, last);
936 pointer p = get_data().p_begin + offset;
937 erase_at_end(std::move(p + len, get_data().p_end, p));
938 return iterator(p);
939 }
940
941 template <class T>
942 constexpr void buffer<T>::push_back(const T& value)
943 {
944 emplace(cend(), value);
945 }
946
947 template <class T>
948 constexpr void buffer<T>::push_back(T&& value)
949 {
950 emplace(cend(), std::move(value));
951 }
952
953 template <class T>
954 constexpr void buffer<T>::pop_back()
955 {
957 destroy(get_allocator(), get_data().p_end - 1);
958 --get_data().p_end;
959 }
960
961 template <class T>
962 constexpr void buffer<T>::resize(size_type new_size)
963 {
964 resize_impl(
965 new_size,
966 [this](size_type nb_init)
967 {
968 get_data().p_end = default_initialize(get_data().p_end, nb_init, get_allocator());
969 }
970 );
971 }
972
973 template <class T>
974 constexpr void buffer<T>::resize(size_type new_size, const value_type& value)
975 {
976 resize_impl(
977 new_size,
978 [this, &value](size_type nb_init)
979 {
980 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value, get_allocator());
981 }
982 );
983 }
984
985 template <class T>
986 constexpr void buffer<T>::swap(buffer& rhs) noexcept
987 {
988 std::swap(this->get_data(), rhs.get_data());
989 }
990
991 template <class T>
992 template <class F>
993 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
994 {
995 if (new_size > size())
996 {
997 const std::size_t nb_init = new_size - size();
998 if (new_size <= capacity())
999 {
1000 initializer(nb_init);
1001 }
1002 else
1003 {
1004 reserve(new_size);
1005 initializer(nb_init);
1006 }
1007 }
1008 else if (new_size < size())
1009 {
1010 erase_at_end(get_data().p_begin + new_size);
1011 }
1012 }
1013
1014 template <class T>
1015 template <class It>
1016 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
1017 {
1018 const size_type sz = size();
1019 const size_type len = static_cast<size_type>(std::distance(first, last));
1020 if (len > capacity())
1021 {
1022 check_init_length(len, get_allocator());
1023 pointer p = allocate_and_copy(len, first, last);
1024 destroy(get_data().p_begin, get_data().p_end, get_allocator());
1025 this->deallocate(get_data().p_begin, capacity());
1026 this->assign_storage(p, len, len);
1027 }
1028 else if (sz >= len)
1029 {
1030 pointer p = std::copy(first, last, get_data().p_begin);
1031 erase_at_end(p);
1032 }
1033 else
1034 {
1035 It mid = first;
1036 std::advance(mid, sz);
1037 std::copy(first, mid, get_data().p_begin);
1038 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1039 }
1040 }
1041
1042 template <class T>
1043 constexpr void buffer<T>::erase_at_end(pointer p)
1044 {
1045 destroy(p, get_data().p_end, get_allocator());
1046 get_data().p_end = p;
1047 }
1048
1049 template <class T>
1050 template <class It>
1051 constexpr auto buffer<T>::allocate_and_copy(size_type n, It first, It last) -> pointer
1052 {
1053 pointer p = this->allocate(n);
1054 try
1055 {
1056 copy_initialize(first, last, p, get_allocator());
1057 }
1058 catch (...)
1059 {
1060 this->deallocate(p, n);
1061 throw;
1062 }
1063 return p;
1064 }
1065
1066 template <class T>
1067 constexpr auto buffer<T>::check_init_length(size_type n, const allocator_type& a) -> size_type
1068 {
1069 if (n > max_size_impl(a))
1070 {
1071 throw std::length_error("cannot create buffer larger than max_size()");
1072 }
1073 return n;
1074 }
1075
1076 template <class T>
1077 constexpr auto buffer<T>::max_size_impl(const allocator_type& a) noexcept -> size_type
1078 {
1079 const size_type diff_max = static_cast<size_type>(std::numeric_limits<difference_type>::max());
1080 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1081 return (std::min) (diff_max, alloc_max);
1082 }
1083
1084 template <class T>
1085 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1086 {
1087 pointer current = begin;
1088 for (; n > 0; --n, ++current)
1089 {
1090 alloc_traits::construct(a, current);
1091 }
1092 return current;
1093 }
1094
1095 template <class T>
1096 constexpr auto
1097 buffer<T>::fill_initialize(pointer begin, size_type n, const value_type& v, allocator_type& a) -> pointer
1098 {
1099 pointer current = begin;
1100 for (; n > 0; --n, ++current)
1101 {
1102 alloc_traits::construct(a, current, v);
1103 }
1104 return current;
1105 }
1106
1107 template <class T>
1108 template <class It>
1109 constexpr auto buffer<T>::copy_initialize(It first, It last, pointer begin, allocator_type& a) -> pointer
1110 {
1111 pointer current = begin;
1112 for (; first != last; ++first, ++current)
1113 {
1114 alloc_traits::construct(a, current, *first);
1115 }
1116 return current;
1117 }
1118
1119 template <class T>
1120 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1121 {
1122 SPARROW_ASSERT_TRUE(first <= last);
1123 for (; first != last; ++first)
1124 {
1125 alloc_traits::destroy(a, first);
1126 }
1127 }
1128
1129 template <class T>
1130 constexpr bool operator==(const buffer<T>& lhs, const buffer<T>& rhs) noexcept
1131 {
1132 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1133 }
1134}
#define SPARROW_BUFFER_GROWTH_FACTOR
Definition buffer.hpp:31
constexpr pointer allocate(size_type n)
Definition buffer.hpp:387
typename alloc_traits::pointer pointer
Definition buffer.hpp:50
any_allocator< T > allocator_type
Definition buffer.hpp:48
constexpr void create_storage(size_type n)
Definition buffer.hpp:399
constexpr void deallocate(pointer p, size_type n)
Definition buffer.hpp:393
constexpr buffer_data & get_data() noexcept
Definition buffer.hpp:375
constexpr void assign_storage(pointer p, size_type n, size_type cap)
Definition buffer.hpp:407
std::allocator_traits< allocator_type > alloc_traits
Definition buffer.hpp:49
typename alloc_traits::size_type size_type
Definition buffer.hpp:51
constexpr allocator_type & get_allocator() noexcept
Definition buffer.hpp:363
Object that owns a piece of contiguous memory.
Definition buffer.hpp:109
std::reverse_iterator< iterator > reverse_iterator
Definition buffer.hpp:130
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition buffer.hpp:131
constexpr buffer(size_type n, const A &a=A())
Definition buffer.hpp:421
typename alloc_traits::difference_type difference_type
Definition buffer.hpp:127
typename alloc_traits::size_type size_type
Definition buffer.hpp:126
constexpr void resize(size_type new_size)
Definition buffer.hpp:962
constexpr const_reference back() const
Definition buffer.hpp:618
constexpr size_type max_size() const noexcept
Definition buffer.hpp:743
typename alloc_traits::const_pointer const_pointer
Definition buffer.hpp:125
constexpr auto allocate_and_copy(size_type n, It first, It last) -> pointer
Definition buffer.hpp:1051
constexpr iterator insert(const_iterator pos, const T &value)
Definition buffer.hpp:808
constexpr void shrink_to_fit()
Definition buffer.hpp:790
constexpr const_reverse_iterator crend() const noexcept
Definition buffer.hpp:719
constexpr U * data() noexcept
Definition buffer.hpp:626
buffer(const buffer &rhs, const A &a)
Definition buffer.hpp:491
constexpr auto copy_initialize(It first, It last, pointer begin, allocator_type &a) -> pointer
Definition buffer.hpp:1109
pointer_iterator< const_pointer > const_iterator
Definition buffer.hpp:129
constexpr iterator erase(const_iterator pos)
Definition buffer.hpp:921
constexpr reference back()
Definition buffer.hpp:611
typename base_type::allocator_type allocator_type
Definition buffer.hpp:120
constexpr const_iterator cbegin() const noexcept
Definition buffer.hpp:677
const value_type & const_reference
Definition buffer.hpp:123
constexpr buffer & operator=(std::initializer_list< value_type > init)
Definition buffer.hpp:572
buffer(const buffer &rhs)
Definition buffer.hpp:479
typename alloc_traits::pointer pointer
Definition buffer.hpp:124
constexpr buffer(const A &a)
Definition buffer.hpp:137
constexpr const_reference operator[](size_type i) const
Definition buffer.hpp:590
pointer_iterator< pointer > iterator
Definition buffer.hpp:128
constexpr buffer(size_type n, const value_type &v, const A &a=A())
Definition buffer.hpp:429
constexpr buffer(pointer p, size_type n, const A &a=A())
Definition buffer.hpp:437
constexpr const_iterator cend() const noexcept
Definition buffer.hpp:683
constexpr iterator begin() noexcept
Definition buffer.hpp:653
constexpr iterator end() noexcept
Definition buffer.hpp:659
constexpr buffer(It first, It last, const A &a=A())
Definition buffer.hpp:452
constexpr auto insert(const_iterator pos, InputIt first, InputIt last) -> iterator
Definition buffer.hpp:856
constexpr reverse_iterator rend() noexcept
Definition buffer.hpp:695
buffer(buffer &&rhs, const A &a)
Definition buffer.hpp:503
buffer()=default
constexpr size_type size() const noexcept
Definition buffer.hpp:737
constexpr reference operator[](size_type i)
Definition buffer.hpp:583
constexpr void reserve(size_type new_cap)
Definition buffer.hpp:749
value_type & reference
Definition buffer.hpp:122
buffer(buffer &&rhs) noexcept=default
constexpr buffer & operator=(const buffer &rhs)
Definition buffer.hpp:522
constexpr void swap(buffer &rhs) noexcept
Definition buffer.hpp:986
constexpr void push_back(const std::uint8_t &value)
Definition buffer.hpp:942
constexpr const_reverse_iterator crbegin() const noexcept
Definition buffer.hpp:713
constexpr iterator emplace(const_iterator pos, Args &&... args)
Definition buffer.hpp:899
constexpr const_reference front() const
Definition buffer.hpp:604
constexpr buffer(std::initializer_list< value_type > init, const A &a=A())
Definition buffer.hpp:444
constexpr reference front()
Definition buffer.hpp:597
constexpr reverse_iterator rbegin() noexcept
Definition buffer.hpp:689
constexpr size_type capacity() const noexcept
Definition buffer.hpp:731
constexpr buffer(const Range &range, const A &a=A())
Definition buffer.hpp:461
constexpr buffer & operator=(buffer &&rhs)
Definition buffer.hpp:545
constexpr bool empty() const noexcept
Definition buffer.hpp:725
#define SPARROW_ASSERT_TRUE(expr__)
#define SPARROW_ASSERT_FALSE(expr__)
constexpr std::size_t size(typelist< T... >={})
Definition mp_utils.hpp:107
pointer_iterator< T * > make_pointer_iterator(T *t)
Definition iterator.hpp:497
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
constexpr bool is_move_iterator_v
Definition buffer.hpp:852
constexpr buffer_data & operator=(buffer_data &&) noexcept
Definition buffer.hpp:316