3#include "plg/config.hpp"
4#include "plg/concepts.hpp"
7 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
10 template <
class SplitBuffer,
class T,
class Allocator>
15 using alloc_rr = std::remove_reference_t<allocator_type>;
16 using alloc_traits = std::allocator_traits<alloc_rr>;
17 using reference = value_type&;
18 using const_reference =
const value_type&;
19 using size_type =
typename alloc_traits::size_type;
20 using difference_type =
typename alloc_traits::difference_type;
21 using pointer =
typename alloc_traits::pointer;
22 using const_pointer =
typename alloc_traits::const_pointer;
23 using iterator = pointer;
24 using constIterator = const_pointer;
25 using sentinel_type = pointer;
29 : back_cap_(
nullptr) {
37 constexpr pointer front_cap()
noexcept {
41 constexpr const_pointer front_cap()
const noexcept {
45 constexpr pointer begin()
noexcept {
49 constexpr const_pointer begin()
const noexcept {
53 constexpr pointer end()
noexcept {
57 constexpr pointer end()
const noexcept {
61 constexpr size_type size()
const noexcept {
62 return static_cast<size_type
>(end_ - begin_);
65 constexpr bool empty()
const noexcept {
66 return begin_ == end_;
69 constexpr size_type capacity()
const noexcept {
70 return static_cast<size_type
>(back_cap_ - front_cap_);
73 constexpr allocator_type& get_allocator()
noexcept {
77 constexpr const allocator_type& get_allocator()
const noexcept {
83 constexpr sentinel_type raw_sentinel()
const noexcept {
87 constexpr sentinel_type raw_capacity()
const noexcept {
91 constexpr void set_data(pointer
new_first)
noexcept {
107 constexpr void set_sentinel(pointer
new_end)
noexcept {
112 constexpr void set_sentinel(size_type
new_size)
noexcept {
116 constexpr void set_capacity(size_type
new_capacity)
noexcept {
120 constexpr void set_capacity(pointer
new_capacity)
noexcept {
124 constexpr size_type front_spare()
const noexcept {
125 return static_cast<size_type
>(begin_ - front_cap_);
128 constexpr size_type back_spare()
const noexcept {
129 return static_cast<size_type
>(back_cap_ - end_);
132 constexpr reference back()
noexcept {
136 constexpr const_reference back()
const noexcept {
140 constexpr void swap_without_allocator(
146 std::swap(front_cap_, other.front_cap_);
147 std::swap(begin_, other.begin_);
148 std::swap(back_cap_, other.back_cap_);
149 std::swap(end_, other.end_);
153 std::swap(front_cap_, other.front_cap_);
154 std::swap(begin_, other.begin_);
155 std::swap(back_cap_, other.back_cap_);
156 std::swap(end_, other.end_);
157 swap_allocator(alloc_, other.alloc_);
160 constexpr void reset()
noexcept {
161 front_cap_ =
nullptr;
167 constexpr void copy_without_alloc(
169 )
noexcept(std::is_nothrow_copy_assignable<pointer>::value) {
170 front_cap_ = other.front_cap_;
171 begin_ = other.begin_;
173 back_cap_ = other.back_cap_;
177 pointer front_cap_ =
nullptr;
178 pointer begin_ =
nullptr;
179 pointer end_ =
nullptr;
180 pointer back_cap_ =
nullptr;
181 PLUGIFY_NO_UNIQUE_ADDRESS allocator_type alloc_;
183 template <
class,
class,
class>
187 template <
class SplitBuffer,
class T,
class Allocator>
190 using value_type = T;
192 using alloc_rr = std::remove_reference_t<allocator_type>;
193 using alloc_traits = std::allocator_traits<alloc_rr>;
194 using reference = value_type&;
195 using const_reference =
const value_type&;
196 using size_type =
typename alloc_traits::size_type;
197 using difference_type =
typename alloc_traits::difference_type;
198 using pointer =
typename alloc_traits::pointer;
199 using const_pointer =
typename alloc_traits::const_pointer;
200 using iterator = pointer;
201 using constIterator = const_pointer;
202 using sentinel_type = size_type;
211 constexpr pointer front_cap()
noexcept {
215 constexpr const_pointer front_cap()
const noexcept {
219 constexpr pointer begin()
noexcept {
223 constexpr const_pointer begin()
const noexcept {
227 constexpr pointer end()
noexcept {
228 return begin_ + size_;
231 constexpr pointer end()
const noexcept {
232 return begin_ + size_;
235 constexpr size_type size()
const noexcept {
239 constexpr bool empty()
const noexcept {
243 constexpr size_type capacity()
const noexcept {
247 constexpr allocator_type& get_allocator()
noexcept {
251 constexpr const allocator_type& get_allocator()
const noexcept {
257 constexpr sentinel_type raw_sentinel()
const noexcept {
261 constexpr sentinel_type raw_capacity()
const noexcept {
265 constexpr void set_data(pointer
new_first)
noexcept {
287 constexpr void set_sentinel(pointer
new_end)
noexcept {
292 constexpr void set_sentinel(size_type
new_size)
noexcept {
296 constexpr void set_capacity(size_type
new_capacity)
noexcept {
300 constexpr void set_capacity(pointer
new_capacity)
noexcept {
304 constexpr size_type front_spare()
const noexcept {
305 return static_cast<size_type
>(begin_ - front_cap_);
308 constexpr size_type back_spare()
const noexcept {
311 return cap_ - size_ - front_spare();
314 constexpr reference back()
noexcept {
315 return begin_[size_ - 1];
318 constexpr const_reference back()
const noexcept {
319 return begin_[size_ - 1];
322 constexpr void swap_without_allocator(
328 std::swap(front_cap_, other.front_cap_);
329 std::swap(begin_, other.begin_);
330 std::swap(cap_, other.cap_);
331 std::swap(size_, other.size_);
335 std::swap(front_cap_, other.front_cap_);
336 std::swap(begin_, other.begin_);
337 std::swap(cap_, other.cap_);
338 std::swap(size_, other.size_);
339 swap_allocator(alloc_, other.alloc_);
342 constexpr void reset()
noexcept {
343 front_cap_ =
nullptr;
349 constexpr void copy_without_alloc(
351 )
noexcept(std::is_nothrow_copy_assignable<pointer>::value) {
352 front_cap_ = other.front_cap_;
353 begin_ = other.begin_;
359 pointer front_cap_ =
nullptr;
360 pointer begin_ =
nullptr;
363 PLUGIFY_NO_UNIQUE_ADDRESS allocator_type alloc_;
365 template <
class,
class,
class>
454 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
455 class split_buffer : Layout<split_buffer<T, Allocator, Layout>, T, Allocator> {
459 using base_type::back_spare;
460 using base_type::copy_without_alloc;
461 using base_type::front_cap;
462 using base_type::front_spare;
463 using base_type::get_allocator;
464 using base_type::raw_capacity;
465 using base_type::raw_sentinel;
466 using base_type::reset;
467 using base_type::set_capacity;
468 using base_type::set_data;
469 using base_type::set_sentinel;
470 using base_type::set_valid_range;
473 using typename base_type::alloc_traits;
474 using typename base_type::allocator_type;
475 using typename base_type::constIterator;
476 using typename base_type::const_pointer;
477 using typename base_type::const_reference;
478 using typename base_type::difference_type;
481 using typename base_type::reference;
482 using typename base_type::size_type;
483 using typename base_type::value_type;
490 using trivially_relocatable = std::conditional_t<
518 (alloc_traits::propagate_on_container_move_assignment::value
519 && std::is_nothrow_move_assignable_v<allocator_type>)
520 || !alloc_traits::propagate_on_container_move_assignment::value
525 using base_type::back;
526 using base_type::begin;
527 using base_type::capacity;
528 using base_type::empty;
529 using base_type::end;
530 using base_type::size;
532 constexpr void clear()
noexcept {
533 destruct_at_end(begin());
536 constexpr reference front() {
540 constexpr const_reference front()
const {
544 constexpr void shrink_to_fit()
noexcept;
546 template <
class...
Args>
547 constexpr void emplace_front(
Args&&...
args);
548 template <
class...
Args>
549 constexpr void emplace_back(
Args&&...
args);
551 constexpr void pop_front() {
552 destruct_at_begin(begin() + 1);
555 constexpr void pop_back() {
556 destruct_at_end(end() - 1);
559 constexpr void construct_at_end(size_type n);
560 constexpr void construct_at_end(size_type n, const_reference x);
562 template <std::forward_iterator ForwardIterator>
566 template <
class Iterator,
class Sentinel>
570 template <
class Iterator>
571 constexpr void construct_at_end_with_size(
Iterator first, size_type n);
573 constexpr void destruct_at_begin(pointer
new_begin) {
574 destruct_at_begin(
new_begin, std::is_trivially_destructible<value_type>());
577 constexpr void destruct_at_begin(pointer
new_begin, std::false_type);
578 constexpr void destruct_at_begin(pointer
new_begin, std::true_type);
580 constexpr void destruct_at_end(pointer
new_last)
noexcept {
581 destruct_at_end(
new_last, std::false_type());
584 constexpr void destruct_at_end(pointer
new_last, std::false_type)
noexcept;
585 constexpr void destruct_at_end(pointer
new_last, std::true_type)
noexcept;
589 )
noexcept(!alloc_traits::propagate_on_container_swap::value || std::is_nothrow_swappable_v<alloc_rr>);
591 constexpr bool invariants()
const {
592 if (front_cap() ==
nullptr) {
593 if (begin() !=
nullptr) {
601 if (capacity() != 0) {
607 if (begin() < front_cap()) {
611 if (capacity() < size()) {
615 if (end() < begin()) {
625 base_type::swap_without_allocator(other);
629 constexpr void move_assign_alloc(
split_buffer&
c, std::true_type)
noexcept(
630 std::is_nothrow_move_assignable_v<allocator_type>
632 get_allocator() = std::move(
c.get_allocator());
635 constexpr void move_assign_alloc(
split_buffer&, std::false_type)
noexcept {
638 struct ConstructTransaction {
639 constexpr explicit ConstructTransaction(
649 constexpr ~ConstructTransaction() {
650 parent_->set_sentinel(pos_);
660 template <
class T2,
class A2,
template <
class,
class,
class>
class L2>
669 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
671 ConstructTransaction
tx(
this, end(), n);
672 for (;
tx.pos_ !=
tx.end_; ++
tx.pos_) {
673 alloc_traits::construct(get_allocator(), std::to_address(
tx.pos_));
683 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
685 split_buffer<T, Allocator, Layout>::construct_at_end(size_type n, const_reference x) {
686 ConstructTransaction tx(
this, end(), n);
687 for (; tx.pos_ != tx.end_; ++tx.pos_) {
688 alloc_traits::construct(get_allocator(), std::to_address(tx.pos_), x);
692 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
693 template <
class Iterator,
class Sentinel>
695 split_buffer<T, Allocator, Layout>::construct_at_end_with_sentinel(Iterator first, Sentinel last) {
696 alloc_rr& a = get_allocator();
697 for (; first != last; ++first) {
698 if (back_spare() == 0) {
699 size_type old_cap = capacity();
700 size_type new_cap = std::max<size_type>(2 * old_cap, 8);
701 split_buffer buf(new_cap, 0, a);
702 pointer buf_end = buf.end();
703 pointer cur_end = end();
704 for (pointer p = begin(); p != cur_end; ++p) {
705 alloc_traits::construct(buf.get_allocator(), std::to_address(buf_end), std::move(*p));
706 buf.set_sentinel(++buf_end);
711 alloc_traits::construct(a, std::to_address(end()), *first);
712 set_sentinel(size() + 1);
716 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
717 template <std::forward_iterator ForwardIterator>
719 split_buffer<T, Allocator, Layout>::construct_at_end(ForwardIterator first, ForwardIterator last) {
720 construct_at_end_with_size(first, std::distance(first, last));
723 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
724 template <
class ForwardIterator>
726 split_buffer<T, Allocator, Layout>::construct_at_end_with_size(ForwardIterator first, size_type n) {
727 ConstructTransaction tx(
this, end(), n);
728 for (; tx.pos_ != tx.end_; ++tx.pos_, (void) ++first) {
729 alloc_traits::construct(get_allocator(), std::to_address(tx.pos_), *first);
733 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
734 inline constexpr void
735 split_buffer<T, Allocator, Layout>::destruct_at_begin(pointer new_begin, std::false_type) {
736 pointer pos = begin();
738 while (pos != new_begin) {
739 alloc_traits::destroy(get_allocator(), std::to_address(pos++));
741 set_valid_range(pos, end());
744 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
745 inline constexpr void
746 split_buffer<T, Allocator, Layout>::destruct_at_begin(pointer new_begin, std::true_type) {
747 set_valid_range(new_begin, end());
750 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
751 inline constexpr void
752 split_buffer<T, Allocator, Layout>::destruct_at_end(pointer new_last, std::false_type)
noexcept {
753 pointer cur_end = end();
755 while (new_last != cur_end) {
756 alloc_traits::destroy(get_allocator(), std::to_address(--cur_end));
758 set_sentinel(cur_end);
761 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
762 constexpr split_buffer<T, Allocator, Layout>::split_buffer(size_type cap, size_type start, alloc_rr& a)
764 PLUGIFY_ASSERT(cap >= start,
"can't have a start point outside the capacity");
766 auto allocation = allocate_at_least(get_allocator(), cap);
767 set_data(allocation.ptr);
768 cap = allocation.count;
771 pointer pos = front_cap() + start;
772 set_valid_range(pos, pos);
776 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
777 constexpr split_buffer<T, Allocator, Layout>::~split_buffer() {
780 alloc_traits::deallocate(get_allocator(), front_cap(), capacity());
784 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
785 constexpr split_buffer<T, Allocator, Layout>::split_buffer(split_buffer&& c)
noexcept(
786 std::is_nothrow_move_constructible_v<allocator_type>
788 : base_type(std::move(c)) {
792 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
793 constexpr split_buffer<T, Allocator, Layout>::split_buffer(split_buffer&& c,
const alloc_rr& a)
795 if (a == c.get_allocator()) {
796 set_data(c.front_cap());
797 set_valid_range(c.begin(), c.end());
798 set_capacity(c.capacity());
801 auto allocation = allocate_at_least(get_allocator(), c.size());
802 set_data(allocation.ptr);
803 set_valid_range(front_cap(), front_cap());
804 set_capacity(allocation.count);
805 using Ip = std::move_iterator<iterator>;
806 construct_at_end(Ip(c.begin()), Ip(c.end()));
810 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
811 constexpr split_buffer<T, Allocator, Layout>&
812 split_buffer<T, Allocator, Layout>::operator=(split_buffer&& c)
noexcept(
813 (alloc_traits::propagate_on_container_move_assignment::value
814 && std::is_nothrow_move_assignable_v<allocator_type>)
815 || !alloc_traits::propagate_on_container_move_assignment::value
819 copy_without_alloc(c);
822 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
828 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
829 constexpr void split_buffer<T, Allocator, Layout>::swap(
831 )
noexcept(!alloc_traits::propagate_on_container_swap::value || std::is_nothrow_swappable_v<alloc_rr>) {
835 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
836 constexpr void split_buffer<T, Allocator, Layout>::shrink_to_fit() noexcept {
837 if (capacity() > size()) {
838#if PLUGIFY_HAS_EXCEPTIONS
841 split_buffer<value_type, alloc_rr&, Layout> t(size(), 0, get_allocator());
842 if (t.capacity() < capacity()) {
843 t.construct_at_end(std::move_iterator<pointer>(begin()), std::move_iterator<pointer>(end()));
844 t.set_sentinel(size());
845 swap_without_allocator(t);
847#if PLUGIFY_HAS_EXCEPTIONS
854 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
855 template <
class... Args>
856 constexpr void split_buffer<T, Allocator, Layout>::emplace_front(Args&&... args) {
857 if (front_spare() == 0) {
858 pointer cur_end = end();
859 if (back_spare() > 0) {
862 difference_type d = back_spare();
864 auto new_end = cur_end + d;
865 set_valid_range(std::move_backward(begin(), cur_end, new_end), new_end);
867 size_type c = std::max<size_type>(2 * capacity(), 1);
868 split_buffer<value_type, alloc_rr&, Layout> t(c, (c + 3) / 4, get_allocator());
869 t.construct_at_end(std::move_iterator<pointer>(begin()), std::move_iterator<pointer>(cur_end));
870 base_type::swap_without_allocator(t);
874 alloc_traits::construct(get_allocator(), std::to_address(begin() - 1), std::forward<Args>(args)...);
875 set_valid_range(begin() - 1, size() + 1);
878 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
879 template <
class... Args>
880 constexpr void split_buffer<T, Allocator, Layout>::emplace_back(Args&&... args) {
881 pointer cur_end = end();
882 if (back_spare() == 0) {
883 if (front_spare() > 0) {
884 difference_type d = front_spare();
886 cur_end = std::move(begin(), cur_end, begin() - d);
887 set_valid_range(begin() - d, cur_end);
889 size_type c = std::max<size_type>(2 * capacity(), 1);
890 split_buffer<value_type, alloc_rr&, Layout> t(c, c / 4, get_allocator());
891 t.construct_at_end(std::move_iterator<pointer>(begin()), std::move_iterator<pointer>(cur_end));
892 base_type::swap_without_allocator(t);
896 alloc_traits::construct(get_allocator(), std::to_address(cur_end), std::forward<Args>(args)...);
897 set_sentinel(++cur_end);
900 template <
class T,
class Allocator,
template <
class,
class,
class>
class Layout>
901 inline constexpr void
902 swap(split_buffer<T, Allocator, Layout>& x, split_buffer<T, Allocator, Layout>& y)
noexcept(