]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/core/impl/multi_buffer.ipp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / beast / core / impl / multi_buffer.ipp
1 //
2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_IMPL_MULTI_BUFFER_IPP
11 #define BOOST_BEAST_IMPL_MULTI_BUFFER_IPP
12
13 #include <boost/beast/core/detail/type_traits.hpp>
14 #include <boost/assert.hpp>
15 #include <boost/throw_exception.hpp>
16 #include <algorithm>
17 #include <exception>
18 #include <sstream>
19 #include <string>
20 #include <utility>
21
22 namespace boost {
23 namespace beast {
24
25 /* These diagrams illustrate the layout and state variables.
26
27 1 Input and output contained entirely in one element:
28
29 0 out_
30 |<-------------+------------------------------------------->|
31 in_pos_ out_pos_ out_end_
32
33
34 2 Output contained in first and second elements:
35
36 out_
37 |<------+----------+------->| |<----------+-------------->|
38 in_pos_ out_pos_ out_end_
39
40
41 3 Output contained in the second element:
42
43 out_
44 |<------------+------------>| |<----+-------------------->|
45 in_pos_ out_pos_ out_end_
46
47
48 4 Output contained in second and third elements:
49
50 out_
51 |<-----+-------->| |<-------+------>| |<--------------->|
52 in_pos_ out_pos_ out_end_
53
54
55 5 Input sequence is empty:
56
57 out_
58 |<------+------------------>| |<-----------+------------->|
59 out_pos_ out_end_
60 in_pos_
61
62
63 6 Output sequence is empty:
64
65 out_
66 |<------+------------------>| |<------+------------------>|
67 in_pos_ out_pos_
68 out_end_
69
70
71 7 The end of output can point to the end of an element.
72 But out_pos_ should never point to the end:
73
74 out_
75 |<------+------------------>| |<------+------------------>|
76 in_pos_ out_pos_ out_end_
77
78
79 8 When the input sequence entirely fills the last element and
80 the output sequence is empty, out_ will point to the end of
81 the list of buffers, and out_pos_ and out_end_ will be 0:
82
83
84 |<------+------------------>| out_ == list_.end()
85 in_pos_ out_pos_ == 0
86 out_end_ == 0
87 */
88
89 template<class Allocator>
90 class basic_multi_buffer<Allocator>::element
91 : public boost::intrusive::list_base_hook<
92 boost::intrusive::link_mode<
93 boost::intrusive::normal_link>>
94 {
95 using size_type =
96 typename detail::allocator_traits<Allocator>::size_type;
97
98 size_type const size_;
99
100 public:
101 element(element const&) = delete;
102 element& operator=(element const&) = delete;
103
104 explicit
105 element(size_type n)
106 : size_(n)
107 {
108 }
109
110 size_type
111 size() const
112 {
113 return size_;
114 }
115
116 char*
117 data() const
118 {
119 return const_cast<char*>(
120 reinterpret_cast<char const*>(this+1));
121 }
122 };
123
124 template<class Allocator>
125 class basic_multi_buffer<Allocator>::const_buffers_type
126 {
127 basic_multi_buffer const* b_;
128
129 friend class basic_multi_buffer;
130
131 explicit
132 const_buffers_type(basic_multi_buffer const& b);
133
134 public:
135 using value_type = boost::asio::mutable_buffer;
136
137 class const_iterator;
138
139 const_buffers_type() = delete;
140 const_buffers_type(const_buffers_type const&) = default;
141 const_buffers_type& operator=(const_buffers_type const&) = default;
142
143 const_iterator
144 begin() const;
145
146 const_iterator
147 end() const;
148
149 friend
150 std::size_t
151 buffer_size(const_buffers_type const& buffers)
152 {
153 return buffers.b_->size();
154 }
155 };
156
157 template<class Allocator>
158 class basic_multi_buffer<Allocator>::mutable_buffers_type
159 {
160 basic_multi_buffer const* b_;
161
162 friend class basic_multi_buffer;
163
164 explicit
165 mutable_buffers_type(basic_multi_buffer const& b);
166
167 public:
168 using value_type = mutable_buffer;
169
170 class const_iterator;
171
172 mutable_buffers_type() = delete;
173 mutable_buffers_type(mutable_buffers_type const&) = default;
174 mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
175
176 const_iterator
177 begin() const;
178
179 const_iterator
180 end() const;
181 };
182
183 //------------------------------------------------------------------------------
184
185 template<class Allocator>
186 class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator
187 {
188 basic_multi_buffer const* b_ = nullptr;
189 typename list_type::const_iterator it_;
190
191 public:
192 using value_type =
193 typename const_buffers_type::value_type;
194 using pointer = value_type const*;
195 using reference = value_type;
196 using difference_type = std::ptrdiff_t;
197 using iterator_category =
198 std::bidirectional_iterator_tag;
199
200 const_iterator() = default;
201 const_iterator(const_iterator&& other) = default;
202 const_iterator(const_iterator const& other) = default;
203 const_iterator& operator=(const_iterator&& other) = default;
204 const_iterator& operator=(const_iterator const& other) = default;
205
206 const_iterator(basic_multi_buffer const& b,
207 typename list_type::const_iterator const& it)
208 : b_(&b)
209 , it_(it)
210 {
211 }
212
213 bool
214 operator==(const_iterator const& other) const
215 {
216 return b_ == other.b_ && it_ == other.it_;
217 }
218
219 bool
220 operator!=(const_iterator const& other) const
221 {
222 return !(*this == other);
223 }
224
225 reference
226 operator*() const
227 {
228 auto const& e = *it_;
229 return value_type{e.data(),
230 (b_->out_ == b_->list_.end() ||
231 &e != &*b_->out_) ? e.size() : b_->out_pos_} +
232 (&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
233 }
234
235 pointer
236 operator->() const = delete;
237
238 const_iterator&
239 operator++()
240 {
241 ++it_;
242 return *this;
243 }
244
245 const_iterator
246 operator++(int)
247 {
248 auto temp = *this;
249 ++(*this);
250 return temp;
251 }
252
253 const_iterator&
254 operator--()
255 {
256 --it_;
257 return *this;
258 }
259
260 const_iterator
261 operator--(int)
262 {
263 auto temp = *this;
264 --(*this);
265 return temp;
266 }
267 };
268
269 template<class Allocator>
270 basic_multi_buffer<Allocator>::
271 const_buffers_type::
272 const_buffers_type(
273 basic_multi_buffer const& b)
274 : b_(&b)
275 {
276 }
277
278 template<class Allocator>
279 auto
280 basic_multi_buffer<Allocator>::
281 const_buffers_type::
282 begin() const ->
283 const_iterator
284 {
285 return const_iterator{*b_, b_->list_.begin()};
286 }
287
288 template<class Allocator>
289 auto
290 basic_multi_buffer<Allocator>::
291 const_buffers_type::
292 end() const ->
293 const_iterator
294 {
295 return const_iterator{*b_, b_->out_ ==
296 b_->list_.end() ? b_->list_.end() :
297 std::next(b_->out_)};
298 }
299
300 //------------------------------------------------------------------------------
301
302 template<class Allocator>
303 class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
304 {
305 basic_multi_buffer const* b_ = nullptr;
306 typename list_type::const_iterator it_;
307
308 public:
309 using value_type =
310 typename mutable_buffers_type::value_type;
311 using pointer = value_type const*;
312 using reference = value_type;
313 using difference_type = std::ptrdiff_t;
314 using iterator_category =
315 std::bidirectional_iterator_tag;
316
317 const_iterator() = default;
318 const_iterator(const_iterator&& other) = default;
319 const_iterator(const_iterator const& other) = default;
320 const_iterator& operator=(const_iterator&& other) = default;
321 const_iterator& operator=(const_iterator const& other) = default;
322
323 const_iterator(basic_multi_buffer const& b,
324 typename list_type::const_iterator const& it)
325 : b_(&b)
326 , it_(it)
327 {
328 }
329
330 bool
331 operator==(const_iterator const& other) const
332 {
333 return b_ == other.b_ && it_ == other.it_;
334 }
335
336 bool
337 operator!=(const_iterator const& other) const
338 {
339 return !(*this == other);
340 }
341
342 reference
343 operator*() const
344 {
345 auto const& e = *it_;
346 return value_type{e.data(),
347 &e == &*std::prev(b_->list_.end()) ?
348 b_->out_end_ : e.size()} +
349 (&e == &*b_->out_ ? b_->out_pos_ : 0);
350 }
351
352 pointer
353 operator->() const = delete;
354
355 const_iterator&
356 operator++()
357 {
358 ++it_;
359 return *this;
360 }
361
362 const_iterator
363 operator++(int)
364 {
365 auto temp = *this;
366 ++(*this);
367 return temp;
368 }
369
370 const_iterator&
371 operator--()
372 {
373 --it_;
374 return *this;
375 }
376
377 const_iterator
378 operator--(int)
379 {
380 auto temp = *this;
381 --(*this);
382 return temp;
383 }
384 };
385
386 template<class Allocator>
387 basic_multi_buffer<Allocator>::
388 mutable_buffers_type::
389 mutable_buffers_type(
390 basic_multi_buffer const& b)
391 : b_(&b)
392 {
393 }
394
395 template<class Allocator>
396 auto
397 basic_multi_buffer<Allocator>::
398 mutable_buffers_type::
399 begin() const ->
400 const_iterator
401 {
402 return const_iterator{*b_, b_->out_};
403 }
404
405 template<class Allocator>
406 auto
407 basic_multi_buffer<Allocator>::
408 mutable_buffers_type::
409 end() const ->
410 const_iterator
411 {
412 return const_iterator{*b_, b_->list_.end()};
413 }
414
415 //------------------------------------------------------------------------------
416
417 template<class Allocator>
418 basic_multi_buffer<Allocator>::
419 ~basic_multi_buffer()
420 {
421 delete_list();
422 }
423
424 template<class Allocator>
425 basic_multi_buffer<Allocator>::
426 basic_multi_buffer()
427 : out_(list_.end())
428 {
429 }
430
431 template<class Allocator>
432 basic_multi_buffer<Allocator>::
433 basic_multi_buffer(std::size_t limit)
434 : max_(limit)
435 , out_(list_.end())
436 {
437 }
438
439 template<class Allocator>
440 basic_multi_buffer<Allocator>::
441 basic_multi_buffer(Allocator const& alloc)
442 : detail::empty_base_optimization<
443 base_alloc_type>(alloc)
444 , out_(list_.end())
445 {
446 }
447
448 template<class Allocator>
449 basic_multi_buffer<Allocator>::
450 basic_multi_buffer(std::size_t limit,
451 Allocator const& alloc)
452 : detail::empty_base_optimization<
453 base_alloc_type>(alloc)
454 , max_(limit)
455 , out_(list_.end())
456 {
457 }
458
459 template<class Allocator>
460 basic_multi_buffer<Allocator>::
461 basic_multi_buffer(basic_multi_buffer&& other)
462 : detail::empty_base_optimization<
463 base_alloc_type>(std::move(other.member()))
464 , max_(other.max_)
465 , in_size_(other.in_size_)
466 , in_pos_(other.in_pos_)
467 , out_pos_(other.out_pos_)
468 , out_end_(other.out_end_)
469 {
470 auto const at_end =
471 other.out_ == other.list_.end();
472 list_ = std::move(other.list_);
473 out_ = at_end ? list_.end() : other.out_;
474 other.in_size_ = 0;
475 other.out_ = other.list_.end();
476 other.in_pos_ = 0;
477 other.out_pos_ = 0;
478 other.out_end_ = 0;
479 }
480
481 template<class Allocator>
482 basic_multi_buffer<Allocator>::
483 basic_multi_buffer(basic_multi_buffer&& other,
484 Allocator const& alloc)
485 : detail::empty_base_optimization<
486 base_alloc_type>(alloc)
487 , max_(other.max_)
488 {
489 if(this->member() != other.member())
490 {
491 out_ = list_.end();
492 copy_from(other);
493 other.reset();
494 }
495 else
496 {
497 auto const at_end =
498 other.out_ == other.list_.end();
499 list_ = std::move(other.list_);
500 out_ = at_end ? list_.end() : other.out_;
501 in_size_ = other.in_size_;
502 in_pos_ = other.in_pos_;
503 out_pos_ = other.out_pos_;
504 out_end_ = other.out_end_;
505 other.in_size_ = 0;
506 other.out_ = other.list_.end();
507 other.in_pos_ = 0;
508 other.out_pos_ = 0;
509 other.out_end_ = 0;
510 }
511 }
512
513 template<class Allocator>
514 basic_multi_buffer<Allocator>::
515 basic_multi_buffer(basic_multi_buffer const& other)
516 : detail::empty_base_optimization<
517 base_alloc_type>(alloc_traits::
518 select_on_container_copy_construction(
519 other.member()))
520 , max_(other.max_)
521 , out_(list_.end())
522 {
523 copy_from(other);
524 }
525
526 template<class Allocator>
527 basic_multi_buffer<Allocator>::
528 basic_multi_buffer(basic_multi_buffer const& other,
529 Allocator const& alloc)
530 : detail::empty_base_optimization<
531 base_alloc_type>(alloc)
532 , max_(other.max_)
533 , out_(list_.end())
534 {
535 copy_from(other);
536 }
537
538 template<class Allocator>
539 template<class OtherAlloc>
540 basic_multi_buffer<Allocator>::
541 basic_multi_buffer(
542 basic_multi_buffer<OtherAlloc> const& other)
543 : out_(list_.end())
544 {
545 copy_from(other);
546 }
547
548 template<class Allocator>
549 template<class OtherAlloc>
550 basic_multi_buffer<Allocator>::
551 basic_multi_buffer(
552 basic_multi_buffer<OtherAlloc> const& other,
553 allocator_type const& alloc)
554 : detail::empty_base_optimization<
555 base_alloc_type>(alloc)
556 , max_(other.max_)
557 , out_(list_.end())
558 {
559 copy_from(other);
560 }
561
562 template<class Allocator>
563 auto
564 basic_multi_buffer<Allocator>::
565 operator=(basic_multi_buffer&& other) ->
566 basic_multi_buffer&
567 {
568 if(this == &other)
569 return *this;
570 reset();
571 max_ = other.max_;
572 move_assign(other, std::integral_constant<bool,
573 alloc_traits::propagate_on_container_move_assignment::value>{});
574 return *this;
575 }
576
577 template<class Allocator>
578 auto
579 basic_multi_buffer<Allocator>::
580 operator=(basic_multi_buffer const& other) ->
581 basic_multi_buffer&
582 {
583 if(this == &other)
584 return *this;
585 copy_assign(other, std::integral_constant<bool,
586 alloc_traits::propagate_on_container_copy_assignment::value>{});
587 return *this;
588 }
589
590 template<class Allocator>
591 template<class OtherAlloc>
592 auto
593 basic_multi_buffer<Allocator>::
594 operator=(
595 basic_multi_buffer<OtherAlloc> const& other) ->
596 basic_multi_buffer&
597 {
598 reset();
599 max_ = other.max_;
600 copy_from(other);
601 return *this;
602 }
603
604 template<class Allocator>
605 std::size_t
606 basic_multi_buffer<Allocator>::
607 capacity() const
608 {
609 auto pos = out_;
610 if(pos == list_.end())
611 return in_size_;
612 auto n = pos->size() - out_pos_;
613 while(++pos != list_.end())
614 n += pos->size();
615 return in_size_ + n;
616 }
617
618 template<class Allocator>
619 auto
620 basic_multi_buffer<Allocator>::
621 data() const ->
622 const_buffers_type
623 {
624 return const_buffers_type(*this);
625 }
626
627 template<class Allocator>
628 auto
629 basic_multi_buffer<Allocator>::
630 prepare(size_type n) ->
631 mutable_buffers_type
632 {
633 if(in_size_ + n > max_)
634 BOOST_THROW_EXCEPTION(std::length_error{
635 "dynamic buffer overflow"});
636 list_type reuse;
637 std::size_t total = in_size_;
638 // put all empty buffers on reuse list
639 if(out_ != list_.end())
640 {
641 total += out_->size() - out_pos_;
642 if(out_ != list_.iterator_to(list_.back()))
643 {
644 out_end_ = out_->size();
645 reuse.splice(reuse.end(), list_,
646 std::next(out_), list_.end());
647 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
648 debug_check();
649 #endif
650 }
651 auto const avail = out_->size() - out_pos_;
652 if(n > avail)
653 {
654 out_end_ = out_->size();
655 n -= avail;
656 }
657 else
658 {
659 out_end_ = out_pos_ + n;
660 n = 0;
661 }
662 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
663 debug_check();
664 #endif
665 }
666 // get space from reuse buffers
667 while(n > 0 && ! reuse.empty())
668 {
669 auto& e = reuse.front();
670 reuse.erase(reuse.iterator_to(e));
671 list_.push_back(e);
672 total += e.size();
673 if(n > e.size())
674 {
675 out_end_ = e.size();
676 n -= e.size();
677 }
678 else
679 {
680 out_end_ = n;
681 n = 0;
682 }
683 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
684 debug_check();
685 #endif
686 }
687 BOOST_ASSERT(total <= max_);
688 if(! reuse.empty() || n > 0)
689 {
690 for(auto it = reuse.begin(); it != reuse.end();)
691 {
692 auto& e = *it++;
693 reuse.erase(list_.iterator_to(e));
694 auto const len = sizeof(e) + e.size();
695 alloc_traits::destroy(this->member(), &e);
696 alloc_traits::deallocate(this->member(),
697 reinterpret_cast<char*>(&e), len);
698 }
699 if(n > 0)
700 {
701 static auto const growth_factor = 2.0f;
702 auto const size =
703 (std::min<std::size_t>)(
704 max_ - total,
705 (std::max<std::size_t>)({
706 static_cast<std::size_t>(
707 in_size_ * growth_factor - in_size_),
708 512,
709 n}));
710 auto& e = *reinterpret_cast<element*>(static_cast<
711 void*>(alloc_traits::allocate(this->member(),
712 sizeof(element) + size)));
713 alloc_traits::construct(this->member(), &e, size);
714 list_.push_back(e);
715 if(out_ == list_.end())
716 out_ = list_.iterator_to(e);
717 out_end_ = n;
718 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
719 debug_check();
720 #endif
721 }
722 }
723 return mutable_buffers_type(*this);
724 }
725
726 template<class Allocator>
727 void
728 basic_multi_buffer<Allocator>::
729 commit(size_type n)
730 {
731 if(list_.empty())
732 return;
733 if(out_ == list_.end())
734 return;
735 auto const back =
736 list_.iterator_to(list_.back());
737 while(out_ != back)
738 {
739 auto const avail =
740 out_->size() - out_pos_;
741 if(n < avail)
742 {
743 out_pos_ += n;
744 in_size_ += n;
745 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
746 debug_check();
747 #endif
748 return;
749 }
750 ++out_;
751 n -= avail;
752 out_pos_ = 0;
753 in_size_ += avail;
754 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
755 debug_check();
756 #endif
757 }
758
759 n = (std::min)(n, out_end_ - out_pos_);
760 out_pos_ += n;
761 in_size_ += n;
762 if(out_pos_ == out_->size())
763 {
764 ++out_;
765 out_pos_ = 0;
766 out_end_ = 0;
767 }
768 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
769 debug_check();
770 #endif
771 }
772
773 template<class Allocator>
774 void
775 basic_multi_buffer<Allocator>::
776 consume(size_type n)
777 {
778 if(list_.empty())
779 return;
780 for(;;)
781 {
782 if(list_.begin() != out_)
783 {
784 auto const avail =
785 list_.front().size() - in_pos_;
786 if(n < avail)
787 {
788 in_size_ -= n;
789 in_pos_ += n;
790 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
791 debug_check();
792 #endif
793 break;
794 }
795 n -= avail;
796 in_size_ -= avail;
797 in_pos_ = 0;
798 auto& e = list_.front();
799 list_.erase(list_.iterator_to(e));
800 auto const len = sizeof(e) + e.size();
801 alloc_traits::destroy(this->member(), &e);
802 alloc_traits::deallocate(this->member(),
803 reinterpret_cast<char*>(&e), len);
804 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
805 debug_check();
806 #endif
807 }
808 else
809 {
810 auto const avail = out_pos_ - in_pos_;
811 if(n < avail)
812 {
813 in_size_ -= n;
814 in_pos_ += n;
815 }
816 else
817 {
818 in_size_ = 0;
819 if(out_ != list_.iterator_to(list_.back()) ||
820 out_pos_ != out_end_)
821 {
822 in_pos_ = out_pos_;
823 }
824 else
825 {
826 // Input and output sequences are empty, reuse buffer.
827 // Alternatively we could deallocate it.
828 in_pos_ = 0;
829 out_pos_ = 0;
830 out_end_ = 0;
831 }
832 }
833 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
834 debug_check();
835 #endif
836 break;
837 }
838 }
839 }
840
841 template<class Allocator>
842 inline
843 void
844 basic_multi_buffer<Allocator>::
845 delete_list()
846 {
847 for(auto iter = list_.begin(); iter != list_.end();)
848 {
849 auto& e = *iter++;
850 auto const len = sizeof(e) + e.size();
851 alloc_traits::destroy(this->member(), &e);
852 alloc_traits::deallocate(this->member(),
853 reinterpret_cast<char*>(&e), len);
854 }
855 }
856
857 template<class Allocator>
858 inline
859 void
860 basic_multi_buffer<Allocator>::
861 reset()
862 {
863 delete_list();
864 list_.clear();
865 out_ = list_.end();
866 in_size_ = 0;
867 in_pos_ = 0;
868 out_pos_ = 0;
869 out_end_ = 0;
870 }
871
872 template<class Allocator>
873 template<class DynamicBuffer>
874 inline
875 void
876 basic_multi_buffer<Allocator>::
877 copy_from(DynamicBuffer const& buffer)
878 {
879 if(buffer.size() == 0)
880 return;
881 using boost::asio::buffer_copy;
882 commit(buffer_copy(
883 prepare(buffer.size()), buffer.data()));
884 }
885
886 template<class Allocator>
887 inline
888 void
889 basic_multi_buffer<Allocator>::
890 move_assign(basic_multi_buffer& other, std::false_type)
891 {
892 if(this->member() != other.member())
893 {
894 copy_from(other);
895 other.reset();
896 }
897 else
898 {
899 move_assign(other, std::true_type{});
900 }
901 }
902
903 template<class Allocator>
904 inline
905 void
906 basic_multi_buffer<Allocator>::
907 move_assign(basic_multi_buffer& other, std::true_type)
908 {
909 this->member() = std::move(other.member());
910 auto const at_end =
911 other.out_ == other.list_.end();
912 list_ = std::move(other.list_);
913 out_ = at_end ? list_.end() : other.out_;
914
915 in_size_ = other.in_size_;
916 in_pos_ = other.in_pos_;
917 out_pos_ = other.out_pos_;
918 out_end_ = other.out_end_;
919
920 other.in_size_ = 0;
921 other.out_ = other.list_.end();
922 other.in_pos_ = 0;
923 other.out_pos_ = 0;
924 other.out_end_ = 0;
925 }
926
927 template<class Allocator>
928 inline
929 void
930 basic_multi_buffer<Allocator>::
931 copy_assign(
932 basic_multi_buffer const& other, std::false_type)
933 {
934 reset();
935 max_ = other.max_;
936 copy_from(other);
937 }
938
939 template<class Allocator>
940 inline
941 void
942 basic_multi_buffer<Allocator>::
943 copy_assign(
944 basic_multi_buffer const& other, std::true_type)
945 {
946 reset();
947 max_ = other.max_;
948 this->member() = other.member();
949 copy_from(other);
950 }
951
952 template<class Allocator>
953 inline
954 void
955 basic_multi_buffer<Allocator>::
956 swap(basic_multi_buffer& other)
957 {
958 swap(other, typename
959 alloc_traits::propagate_on_container_swap{});
960 }
961
962 template<class Allocator>
963 inline
964 void
965 basic_multi_buffer<Allocator>::
966 swap(basic_multi_buffer& other, std::true_type)
967 {
968 using std::swap;
969 auto const at_end0 =
970 out_ == list_.end();
971 auto const at_end1 =
972 other.out_ == other.list_.end();
973 swap(this->member(), other.member());
974 swap(list_, other.list_);
975 swap(out_, other.out_);
976 if(at_end1)
977 out_ = list_.end();
978 if(at_end0)
979 other.out_ = other.list_.end();
980 swap(in_size_, other.in_size_);
981 swap(in_pos_, other.in_pos_);
982 swap(out_pos_, other.out_pos_);
983 swap(out_end_, other.out_end_);
984 }
985
986 template<class Allocator>
987 inline
988 void
989 basic_multi_buffer<Allocator>::
990 swap(basic_multi_buffer& other, std::false_type)
991 {
992 BOOST_ASSERT(this->member() == other.member());
993 using std::swap;
994 auto const at_end0 =
995 out_ == list_.end();
996 auto const at_end1 =
997 other.out_ == other.list_.end();
998 swap(list_, other.list_);
999 swap(out_, other.out_);
1000 if(at_end1)
1001 out_ = list_.end();
1002 if(at_end0)
1003 other.out_ = other.list_.end();
1004 swap(in_size_, other.in_size_);
1005 swap(in_pos_, other.in_pos_);
1006 swap(out_pos_, other.out_pos_);
1007 swap(out_end_, other.out_end_);
1008 }
1009
1010 template<class Allocator>
1011 void
1012 swap(
1013 basic_multi_buffer<Allocator>& lhs,
1014 basic_multi_buffer<Allocator>& rhs)
1015 {
1016 lhs.swap(rhs);
1017 }
1018
1019 template<class Allocator>
1020 void
1021 basic_multi_buffer<Allocator>::
1022 debug_check() const
1023 {
1024 #ifndef NDEBUG
1025 using boost::asio::buffer_size;
1026 BOOST_ASSERT(buffer_size(data()) == in_size_);
1027 if(list_.empty())
1028 {
1029 BOOST_ASSERT(in_pos_ == 0);
1030 BOOST_ASSERT(in_size_ == 0);
1031 BOOST_ASSERT(out_pos_ == 0);
1032 BOOST_ASSERT(out_end_ == 0);
1033 BOOST_ASSERT(out_ == list_.end());
1034 return;
1035 }
1036
1037 auto const& front = list_.front();
1038
1039 BOOST_ASSERT(in_pos_ < front.size());
1040
1041 if(out_ == list_.end())
1042 {
1043 BOOST_ASSERT(out_pos_ == 0);
1044 BOOST_ASSERT(out_end_ == 0);
1045 }
1046 else
1047 {
1048 auto const& out = *out_;
1049 auto const& back = list_.back();
1050
1051 BOOST_ASSERT(out_end_ <= back.size());
1052 BOOST_ASSERT(out_pos_ < out.size());
1053 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
1054 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
1055 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
1056 }
1057 #endif
1058 }
1059
1060 } // beast
1061 } // boost
1062
1063 #endif