plugify 1.2.8
Loading...
Searching...
No Matches
split_buffer.hpp
1#pragma once
2
3#include "plg/config.hpp"
4#include "plg/concepts.hpp"
5
6namespace plg {
7 template <class T, class Allocator, template <class, class, class> class Layout>
8 class split_buffer;
9
10 template <class SplitBuffer, class T, class Allocator>
12 protected:
13 using value_type = T;
14 using allocator_type = 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;
26
27 public:
29 : back_cap_(nullptr) {
30 }
31
32 constexpr explicit split_buffer_pointer_layout(const allocator_type& alloc)
33 : back_cap_(nullptr)
34 , alloc_(alloc) {
35 }
36
37 constexpr pointer front_cap() noexcept {
38 return front_cap_;
39 }
40
41 constexpr const_pointer front_cap() const noexcept {
42 return front_cap_;
43 }
44
45 constexpr pointer begin() noexcept {
46 return begin_;
47 }
48
49 constexpr const_pointer begin() const noexcept {
50 return begin_;
51 }
52
53 constexpr pointer end() noexcept {
54 return end_;
55 }
56
57 constexpr pointer end() const noexcept {
58 return end_;
59 }
60
61 constexpr size_type size() const noexcept {
62 return static_cast<size_type>(end_ - begin_);
63 }
64
65 constexpr bool empty() const noexcept {
66 return begin_ == end_;
67 }
68
69 constexpr size_type capacity() const noexcept {
70 return static_cast<size_type>(back_cap_ - front_cap_);
71 }
72
73 constexpr allocator_type& get_allocator() noexcept {
74 return alloc_;
75 }
76
77 constexpr const allocator_type& get_allocator() const noexcept {
78 return alloc_;
79 }
80
81 // Returns the sentinel object directly. Should be used in conjunction with automatic type
82 // deduction, not explicit types.
83 constexpr sentinel_type raw_sentinel() const noexcept {
84 return end_;
85 }
86
87 constexpr sentinel_type raw_capacity() const noexcept {
88 return back_cap_;
89 }
90
91 constexpr void set_data(pointer new_first) noexcept {
92 front_cap_ = new_first;
93 }
94
95 constexpr void
96 set_valid_range(pointer new_begin, pointer new_end) noexcept {
97 begin_ = new_begin;
98 end_ = new_end;
99 }
100
101 constexpr void
102 set_valid_range(pointer new_begin, size_type new_size) noexcept {
103 begin_ = new_begin;
104 end_ = begin_ + new_size;
105 }
106
107 constexpr void set_sentinel(pointer new_end) noexcept {
108 PLUGIFY_ASSERT(front_cap_ <= new_end, "new_end cannot precede front_cap_");
109 end_ = new_end;
110 }
111
112 constexpr void set_sentinel(size_type new_size) noexcept {
113 end_ = begin_ + new_size;
114 }
115
116 constexpr void set_capacity(size_type new_capacity) noexcept {
117 back_cap_ = front_cap_ + new_capacity;
118 }
119
120 constexpr void set_capacity(pointer new_capacity) noexcept {
121 back_cap_ = new_capacity;
122 }
123
124 constexpr size_type front_spare() const noexcept {
125 return static_cast<size_type>(begin_ - front_cap_);
126 }
127
128 constexpr size_type back_spare() const noexcept {
129 return static_cast<size_type>(back_cap_ - end_);
130 }
131
132 constexpr reference back() noexcept {
133 return *(end_ - 1);
134 }
135
136 constexpr const_reference back() const noexcept {
137 return *(end_ - 1);
138 }
139
140 constexpr void swap_without_allocator(
143 value_type,
144 alloc_rr&>& other
145 ) noexcept {
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_);
150 }
151
152 constexpr void swap(split_buffer_pointer_layout& other) noexcept {
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_);
158 }
159
160 constexpr void reset() noexcept {
161 front_cap_ = nullptr;
162 begin_ = nullptr;
163 end_ = nullptr;
164 back_cap_ = nullptr;
165 }
166
167 constexpr void copy_without_alloc(
168 const split_buffer_pointer_layout& other
169 ) noexcept(std::is_nothrow_copy_assignable<pointer>::value) {
170 front_cap_ = other.front_cap_;
171 begin_ = other.begin_;
172 end_ = other.end_;
173 back_cap_ = other.back_cap_;
174 }
175
176 private:
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_;
182
183 template <class, class, class>
184 friend class split_buffer_pointer_layout;
185 };
186
187 template <class SplitBuffer, class T, class Allocator>
189 protected:
190 using value_type = T;
191 using allocator_type = Allocator;
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;
203
204 public:
205 constexpr split_buffer_size_layout() = default;
206
207 constexpr explicit split_buffer_size_layout(const allocator_type& alloc)
208 : alloc_(alloc) {
209 }
210
211 constexpr pointer front_cap() noexcept {
212 return front_cap_;
213 }
214
215 constexpr const_pointer front_cap() const noexcept {
216 return front_cap_;
217 }
218
219 constexpr pointer begin() noexcept {
220 return begin_;
221 }
222
223 constexpr const_pointer begin() const noexcept {
224 return begin_;
225 }
226
227 constexpr pointer end() noexcept {
228 return begin_ + size_;
229 }
230
231 constexpr pointer end() const noexcept {
232 return begin_ + size_;
233 }
234
235 constexpr size_type size() const noexcept {
236 return size_;
237 }
238
239 constexpr bool empty() const noexcept {
240 return size_ == 0;
241 }
242
243 constexpr size_type capacity() const noexcept {
244 return cap_;
245 }
246
247 constexpr allocator_type& get_allocator() noexcept {
248 return alloc_;
249 }
250
251 constexpr const allocator_type& get_allocator() const noexcept {
252 return alloc_;
253 }
254
255 // Returns the sentinel object directly. Should be used in conjunction with automatic type
256 // deduction, not explicit types.
257 constexpr sentinel_type raw_sentinel() const noexcept {
258 return size_;
259 }
260
261 constexpr sentinel_type raw_capacity() const noexcept {
262 return cap_;
263 }
264
265 constexpr void set_data(pointer new_first) noexcept {
266 front_cap_ = new_first;
267 }
268
269 constexpr void
270 set_valid_range(pointer new_begin, pointer new_end) noexcept {
271 // Size-based split_buffers track their size directly: we need to explicitly update the size
272 // when the front is adjusted.
273 size_ -= new_begin - begin_;
274 begin_ = new_begin;
275 set_sentinel(new_end);
276 }
277
278 constexpr void
279 set_valid_range(pointer new_begin, size_type new_size) noexcept {
280 // Size-based split_buffers track their size directly: we need to explicitly update the size
281 // when the front is adjusted.
282 size_ -= new_begin - begin_;
283 begin_ = new_begin;
284 set_sentinel(new_size);
285 }
286
287 constexpr void set_sentinel(pointer new_end) noexcept {
288 _LIBCPP_ASSERT_INTERNAL(front_cap_ <= new_end, "new_end cannot precede front_cap_");
289 size_ += new_end - end();
290 }
291
292 constexpr void set_sentinel(size_type new_size) noexcept {
293 size_ = new_size;
294 }
295
296 constexpr void set_capacity(size_type new_capacity) noexcept {
297 cap_ = new_capacity;
298 }
299
300 constexpr void set_capacity(pointer new_capacity) noexcept {
301 cap_ = new_capacity - begin_;
302 }
303
304 constexpr size_type front_spare() const noexcept {
305 return static_cast<size_type>(begin_ - front_cap_);
306 }
307
308 constexpr size_type back_spare() const noexcept {
309 // `cap_ - end_` tells us the total number of spares when in size-mode. We need to remove
310 // the front_spare from the count.
311 return cap_ - size_ - front_spare();
312 }
313
314 constexpr reference back() noexcept {
315 return begin_[size_ - 1];
316 }
317
318 constexpr const_reference back() const noexcept {
319 return begin_[size_ - 1];
320 }
321
322 constexpr void swap_without_allocator(
325 value_type,
326 alloc_rr&>& other
327 ) noexcept {
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_);
332 }
333
334 constexpr void swap(split_buffer_size_layout& other) noexcept {
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_);
340 }
341
342 constexpr void reset() noexcept {
343 front_cap_ = nullptr;
344 begin_ = nullptr;
345 size_ = 0;
346 cap_ = 0;
347 }
348
349 constexpr void copy_without_alloc(
350 const split_buffer_size_layout& other
351 ) noexcept(std::is_nothrow_copy_assignable<pointer>::value) {
352 front_cap_ = other.front_cap_;
353 begin_ = other.begin_;
354 cap_ = other.cap_;
355 size_ = other.size_;
356 }
357
358 private:
359 pointer front_cap_ = nullptr;
360 pointer begin_ = nullptr;
361 size_type size_ = 0;
362 size_type cap_ = 0;
363 PLUGIFY_NO_UNIQUE_ADDRESS allocator_type alloc_;
364
365 template <class, class, class>
366 friend class split_buffer_size_layout;
367 };
368
369 // `split_buffer` is a contiguous array data structure. It may hold spare capacity at both ends of
370 // the sequence. This allows for a `split_buffer` to grow from both the front and the back without
371 // relocating its contents until it runs out of room. This characteristic sets it apart from
372 // `std::vector`, which only holds spare capacity at its end. As such, `split_buffer` is useful
373 // for implementing both `std::vector` and `std::deque`.
374 //
375 // The sequence is stored as a contiguous chunk of memory delimited by the following "pointers" (`o`
376 // denotes uninitialized memory and `x` denotes a valid object):
377 //
378 // |oooooooooooooooooooxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxoooooooooooooooooooooooo|
379 // ^ ^ ^ ^
380 // front_cap_ begin_ end_ back_cap_
381 //
382 // The range [front_cap_, begin_) contains uninitialized memory. It is referred to as the "front
383 // spare capacity". The range [begin_, end_) contains valid objects. It is referred to as the "valid
384 // range". The range [end_, back_cap_) contains uninitialized memory. It is referred to as the "back
385 // spare capacity".
386 //
387 // The layout of `split_buffer` is determined by the `Layout` template template parameter. This
388 // `Layout` allows the above pointers to be stored as different representations, such as integer
389 // offsets. A layout class template must provide the following interface:
390 //
391 // template<class T, class Allocator, class Layout>
392 // class layout {
393 // protected:
394 // using value_type = T;
395 // using allocator_type = Allocator;
396 // using alloc_rr = std::remove_reference_t<allocator_type>;
397 // using alloc_traits = allocator_traits<alloc_rr>;
398 // using reference = value_type&;
399 // using const_reference = const value_type&;
400 // using size_type = typename alloc_traits::size_type;
401 // using difference_type = typename alloc_traits::difference_type;
402 // using pointer = typename alloc_traits::pointer;
403 // using const_pointer = typename alloc_traits::const_pointer;
404 // using iterator = pointer;
405 // using constIterator = const_pointer;
406 // using sentinel_type = /* type that represents the layout's sentinel */;
407 //
408 // public:
409 // layout() = default;
410 // explicit layout(const allocator_type&);
411 //
412 // pointer front_cap();
413 // const_pointer front_cap() const;
414 //
415 // pointer begin();
416 // const_pointer begin() const;
417 //
418 // pointer end();
419 // pointer end() const;
420 //
421 // size_type size() const;
422 // bool empty() const;
423 // size_type capacity() const;
424 //
425 // allocator_type& get_allocator();
426 // allocator_type const& get_allocator() const;
427 //
428 // sentinel_type raw_sentinel() const;
429 // sentinel_type raw_capacity() const;
430 //
431 // void set_data(pointer);
432 // void set_valid_range(pointer begin, pointer end);
433 // void set_valid_range(pointer begin, size_type size);
434 // void set_sentinel(pointer end);
435 // void set_sentinel(size_type size);
436 //
437 // void set_capacity(size_type capacity);
438 // void set_capacity(pointer capacity);
439 //
440 // size_type front_spare() const;
441 // size_type back_spare() const;
442 //
443 // reference back();
444 // const_reference back() const;
445 //
446 // template<class _OtherLayout>
447 // void swap_without_allocator(_OtherLayout&);
448 // void swap(layout&);
449 //
450 // void reset();
451 // void copy_without_alloc(layout const&);
452 // };
453 //
454 template <class T, class Allocator, template <class, class, class> class Layout>
455 class split_buffer : Layout<split_buffer<T, Allocator, Layout>, T, Allocator> {
457
458 public:
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;
471
472 using typename base_type::alloc_rr;
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;
479 using typename base_type::iterator;
480 using typename base_type::pointer;
481 using typename base_type::reference;
482 using typename base_type::size_type;
483 using typename base_type::value_type;
484
485 // A split_buffer contains the following members which may be trivially relocatable:
486 // - pointer: may be trivially relocatable, so it's checked
487 // - allocator_type: may be trivially relocatable, so it's checked
488 // split_buffer doesn't have any self-references, so it's trivially relocatable if its members
489 // are.
490 using trivially_relocatable = std::conditional_t<
494 void>;
495
496 split_buffer(const split_buffer&) = delete;
497 split_buffer& operator=(const split_buffer&) = delete;
498
499 split_buffer() = default;
500
501 constexpr explicit split_buffer(alloc_rr& a)
502 : base_type(a) {
503 }
504
505 constexpr explicit split_buffer(const alloc_rr& a)
506 : base_type(a) {
507 }
508
509 constexpr split_buffer(size_type cap, size_type start, alloc_rr& a);
510
511 constexpr
512 split_buffer(split_buffer&& c) noexcept(std::is_nothrow_move_constructible_v<allocator_type>);
513
514 constexpr split_buffer(split_buffer&& c, const alloc_rr& a);
515
516
517 constexpr split_buffer& operator=(split_buffer&& c) noexcept(
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
521 );
522
523 constexpr ~split_buffer();
524
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;
531
532 constexpr void clear() noexcept {
533 destruct_at_end(begin());
534 }
535
536 constexpr reference front() {
537 return *begin();
538 }
539
540 constexpr const_reference front() const {
541 return *begin();
542 }
543
544 constexpr void shrink_to_fit() noexcept;
545
546 template <class... Args>
547 constexpr void emplace_front(Args&&... args);
548 template <class... Args>
549 constexpr void emplace_back(Args&&... args);
550
551 constexpr void pop_front() {
552 destruct_at_begin(begin() + 1);
553 }
554
555 constexpr void pop_back() {
556 destruct_at_end(end() - 1);
557 }
558
559 constexpr void construct_at_end(size_type n);
560 constexpr void construct_at_end(size_type n, const_reference x);
561
562 template <std::forward_iterator ForwardIterator>
563 constexpr void
564 construct_at_end(ForwardIterator first, ForwardIterator last);
565
566 template <class Iterator, class Sentinel>
567 constexpr void
568 construct_at_end_with_sentinel(Iterator first, Sentinel last);
569
570 template <class Iterator>
571 constexpr void construct_at_end_with_size(Iterator first, size_type n);
572
573 constexpr void destruct_at_begin(pointer new_begin) {
574 destruct_at_begin(new_begin, std::is_trivially_destructible<value_type>());
575 }
576
577 constexpr void destruct_at_begin(pointer new_begin, std::false_type);
578 constexpr void destruct_at_begin(pointer new_begin, std::true_type);
579
580 constexpr void destruct_at_end(pointer new_last) noexcept {
581 destruct_at_end(new_last, std::false_type());
582 }
583
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;
586
587 constexpr void swap(
588 split_buffer& x
589 ) noexcept(!alloc_traits::propagate_on_container_swap::value || std::is_nothrow_swappable_v<alloc_rr>);
590
591 constexpr bool invariants() const {
592 if (front_cap() == nullptr) {
593 if (begin() != nullptr) {
594 return false;
595 }
596
597 if (!empty()) {
598 return false;
599 }
600
601 if (capacity() != 0) {
602 return false;
603 }
604
605 return true;
606 } else {
607 if (begin() < front_cap()) {
608 return false;
609 }
610
611 if (capacity() < size()) {
612 return false;
613 }
614
615 if (end() < begin()) {
616 return false;
617 }
618
619 return true;
620 }
621 }
622
623 constexpr void
624 swap_without_allocator(split_buffer<value_type, alloc_rr&, Layout>& other) noexcept {
625 base_type::swap_without_allocator(other);
626 }
627
628 private:
629 constexpr void move_assign_alloc(split_buffer& c, std::true_type) noexcept(
630 std::is_nothrow_move_assignable_v<allocator_type>
631 ) {
632 get_allocator() = std::move(c.get_allocator());
633 }
634
635 constexpr void move_assign_alloc(split_buffer&, std::false_type) noexcept {
636 }
637
638 struct ConstructTransaction {
639 constexpr explicit ConstructTransaction(
641 pointer p,
642 size_type n
643 ) noexcept
644 : pos_(p)
645 , end_(p + n)
646 , parent_(parent) {
647 }
648
649 constexpr ~ConstructTransaction() {
650 parent_->set_sentinel(pos_);
651 }
652
653 pointer pos_;
654 const pointer end_;
655
656 private:
657 split_buffer* parent_;
658 };
659
660 template <class T2, class A2, template <class, class, class> class L2>
661 friend class split_buffer;
662 };
663
664 // Default constructs n objects starting at `end()`
665 // throws if construction throws
666 // Precondition: n > 0
667 // Precondition: size() + n <= capacity()
668 // Postcondition: size() == size() + n
669 template <class T, class Allocator, template <class, class, class> class Layout>
670 constexpr void split_buffer<T, Allocator, Layout>::construct_at_end(size_type n) {
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_));
674 }
675 }
676
677 // Copy constructs n objects starting at `end()` from x
678 // throws if construction throws
679 // Precondition: n > 0
680 // Precondition: size() + n <= capacity()
681 // Postcondition: size() == old size() + n
682 // Postcondition: [i] == x for all i in [size() - n, n)
683 template <class T, class Allocator, template <class, class, class> class Layout>
684 constexpr void
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);
689 }
690 }
691
692 template <class T, class Allocator, template <class, class, class> class Layout>
693 template <class Iterator, class Sentinel>
694 constexpr void
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);
707 }
708 swap(buf);
709 }
710
711 alloc_traits::construct(a, std::to_address(end()), *first);
712 set_sentinel(size() + 1);
713 }
714 }
715
716 template <class T, class Allocator, template <class, class, class> class Layout>
717 template <std::forward_iterator ForwardIterator>
718 constexpr void
719 split_buffer<T, Allocator, Layout>::construct_at_end(ForwardIterator first, ForwardIterator last) {
720 construct_at_end_with_size(first, std::distance(first, last));
721 }
722
723 template <class T, class Allocator, template <class, class, class> class Layout>
724 template <class ForwardIterator>
725 constexpr void
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);
730 }
731 }
732
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();
737 // Updating begin at every iteration is unnecessary because destruction can't throw.
738 while (pos != new_begin) {
739 alloc_traits::destroy(get_allocator(), std::to_address(pos++));
740 }
741 set_valid_range(pos, end());
742 }
743
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());
748 }
749
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();
754 // Updating begin at every iteration is unnecessary because destruction can't throw.
755 while (new_last != cur_end) {
756 alloc_traits::destroy(get_allocator(), std::to_address(--cur_end));
757 }
758 set_sentinel(cur_end);
759 }
760
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)
763 : base_type(a) {
764 PLUGIFY_ASSERT(cap >= start, "can't have a start point outside the capacity");
765 if (cap > 0) {
766 auto allocation = allocate_at_least(get_allocator(), cap);
767 set_data(allocation.ptr);
768 cap = allocation.count;
769 }
770
771 pointer pos = front_cap() + start;
772 set_valid_range(pos, pos);
773 set_capacity(cap);
774 }
775
776 template <class T, class Allocator, template <class, class, class> class Layout>
777 constexpr split_buffer<T, Allocator, Layout>::~split_buffer() {
778 clear();
779 if (front_cap()) {
780 alloc_traits::deallocate(get_allocator(), front_cap(), capacity());
781 }
782 }
783
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>
787 )
788 : base_type(std::move(c)) {
789 c.reset();
790 }
791
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)
794 : base_type(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());
799 c.reset();
800 } else {
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()));
807 }
808 }
809
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
816 ) {
817 clear();
818 shrink_to_fit();
819 copy_without_alloc(c);
820 move_assign_alloc(
821 c,
822 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
823 );
824 c.reset();
825 return *this;
826 }
827
828 template <class T, class Allocator, template <class, class, class> class Layout>
829 constexpr void split_buffer<T, Allocator, Layout>::swap(
830 split_buffer& x
831 ) noexcept(!alloc_traits::propagate_on_container_swap::value || std::is_nothrow_swappable_v<alloc_rr>) {
832 base_type::swap(x);
833 }
834
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
839 try {
840#endif // 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);
846 }
847#if PLUGIFY_HAS_EXCEPTIONS
848 } catch (...) {
849 }
850#endif // PLUGIFY_HAS_EXCEPTIONS
851 }
852 }
853
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) {
860 // The elements are pressed up against the front of the buffer: we need to move them
861 // back a little bit to make `emplace_front` have amortised O(1) complexity.
862 difference_type d = back_spare();
863 d = (d + 1) / 2;
864 auto new_end = cur_end + d;
865 set_valid_range(std::move_backward(begin(), cur_end, new_end), new_end);
866 } else {
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);
871 }
872 }
873
874 alloc_traits::construct(get_allocator(), std::to_address(begin() - 1), std::forward<Args>(args)...);
875 set_valid_range(begin() - 1, size() + 1);
876 }
877
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();
885 d = (d + 1) / 2;
886 cur_end = std::move(begin(), cur_end, begin() - d);
887 set_valid_range(begin() - d, cur_end);
888 } else {
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);
893 }
894 }
895
896 alloc_traits::construct(get_allocator(), std::to_address(cur_end), std::forward<Args>(args)...);
897 set_sentinel(++cur_end);
898 }
899
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(
903 noexcept(x.swap(y))
904 ) {
905 x.swap(y);
906 }
907} // namespace plg