1//
2// Copyright (c) 2016-2019 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_HPP
11#define BOOST_BEAST_IMPL_MULTI_BUFFER_HPP
12
13#include <boost/beast/core/buffer_traits.hpp>
14#include <boost/config/workaround.hpp>
15#include <boost/core/exchange.hpp>
16#include <boost/assert.hpp>
17#include <boost/throw_exception.hpp>
18#include <algorithm>
19#include <exception>
20#include <iterator>
21#include <sstream>
22#include <string>
23#include <type_traits>
24#include <utility>
25
26namespace boost {
27namespace beast {
28
29/* These diagrams illustrate the layout and state variables.
30
311 Input and output contained entirely in one element:
32
33 0 out_
34 |<------+-----------+--------------------------------+----->|
35 in_pos_ out_pos_ out_end_
36
37
382 Output contained in first and second elements:
39
40 out_
41 |<------+-----------+------>| |<-------------------+----->|
42 in_pos_ out_pos_ out_end_
43
44
453 Output contained in the second element:
46
47 out_
48 |<------+------------------>| |<----+--------------+----->|
49 in_pos_ out_pos_ out_end_
50
51
524 Output contained in second and third elements:
53
54 out_
55 |<------+------->| |<-------+------>| |<---------+----->|
56 in_pos_ out_pos_ out_end_
57
58
595 Input sequence is empty:
60
61 out_
62 |<------+------------------>| |<-------------------+----->|
63 out_pos_ out_end_
64 in_pos_
65
666 Output sequence is empty:
67
68 out_
69 |<------+------------------>| |<------+------------------>|
70 in_pos_ out_pos_
71 out_end_
72
73
747 The end of output can point to the end of an element.
75 But out_pos_ should never point to the end:
76
77 out_
78 |<------+------------------>| |<------+------------------>|
79 in_pos_ out_pos_ out_end_
80
81
828 When the input sequence entirely fills the last element and
83 the output sequence is empty, out_ will point to the end of
84 the list of buffers, and out_pos_ and out_end_ will be 0:
85
86
87 |<------+------------------>| out_ == list_.end()
88 in_pos_ out_pos_ == 0
89 out_end_ == 0
90*/
91
92//------------------------------------------------------------------------------
93
94#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
95# pragma warning (push)
96# pragma warning (disable: 4521) // multiple copy constructors specified
97# pragma warning (disable: 4522) // multiple assignment operators specified
98#endif
99
100template<class Allocator>
101template<bool isMutable>
102class basic_multi_buffer<Allocator>::subrange
103{
104 basic_multi_buffer const* b_;
105 const_iter begin_;
106 const_iter end_;
107 size_type begin_pos_; // offset in begin_
108 size_type last_pos_; // offset in std::prev(end_)
109
110 friend class basic_multi_buffer;
111
112 subrange(
113 basic_multi_buffer const& b,
114 size_type pos,
115 size_type n) noexcept
116 : b_(&b)
117 {
118 auto const set_empty = [&]
119 {
120 begin_ = b_->list_.end();
121 end_ = b_->list_.end();
122 begin_pos_ = 0;
123 last_pos_ = 0;
124 };
125
126 // VFALCO Handle this trivial case of
127 // pos larger than total size, otherwise
128 // the addition to pos can overflow.
129 //if(pos >= b_->in_size_)
130 // skip unused prefix
131 pos = pos + b_->in_pos_;
132
133 // iterate the buffers
134 auto it = b_->list_.begin();
135
136 // is the list empty?
137 if(it == b_->list_.end())
138 {
139 set_empty();
140 return;
141 }
142
143 // is the requested size zero?
144 if(n == 0)
145 {
146 set_empty();
147 return;
148 }
149
150
151 // get last buffer and its size
152 auto const last =
153 std::prev(b_->list_.end());
154 auto const last_end =
155 [&]
156 {
157 if(b_->out_end_ == 0)
158 return last->size();
159 return b_->out_end_;
160 }();
161
162 // only one buffer in list?
163 if(it == last)
164 {
165 if(pos >= last_end)
166 {
167 set_empty();
168 return;
169 }
170
171 begin_ = it;
172 begin_pos_ = pos;
173 end_ = std::next(it);
174 if(n > last_end - pos)
175 last_pos_ = last_end;
176 else
177 last_pos_ = pos + n;
178 return;
179 }
180
181 for(;;)
182 {
183 // is pos in this buffer?
184 if(pos < it->size())
185 {
186 begin_ = it;
187 begin_pos_ = pos;
188
189 // does this buffer satisfy n?
190 auto const avail =
191 it->size() - pos;
192 if(n <= avail)
193 {
194 end_ = ++it;
195 last_pos_ = pos + n;
196 return;
197 }
198
199 n -= avail;
200 ++it;
201 break;
202 }
203
204 pos -= it->size();
205 ++it;
206
207 // did we reach the last buffer?
208 if(it == last)
209 {
210 // is pos past the end?
211 if(pos >= last_end)
212 {
213 set_empty();
214 return;
215 }
216
217 // satisfy the request
218 begin_ = it;
219 begin_pos_ = pos;
220 end_ = std::next(it);
221 if(n < last_end - pos)
222 last_pos_ = pos + n;
223 else
224 last_pos_ = last_end;
225 return;
226 }
227 }
228
229 // find pos+n
230 for(;;)
231 {
232 if(it == last)
233 {
234 end_ = ++it;
235 if(n >= last_end)
236 last_pos_ = last_end;
237 else
238 last_pos_ = n;
239 return;
240 }
241 if(n <= it->size())
242 {
243 end_ = ++it;
244 last_pos_ = n;
245 return;
246 }
247
248 n -= it->size();
249 ++it;
250 }
251 }
252
253public:
254 using value_type = typename
255 std::conditional<
256 isMutable,
257 net::mutable_buffer,
258 net::const_buffer>::type;
259
260 class const_iterator;
261
262 subrange() = delete;
263#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
264 subrange(subrange const& other)
265 : b_(other.b_)
266 , begin_(other.begin_)
267 , end_(other.end_)
268 , begin_pos_(other.begin_pos_)
269 , last_pos_(other.last_pos_)
270 {
271 }
272
273 subrange& operator=(subrange const& other)
274 {
275 b_ = other.b_;
276 begin_ = other.begin_;
277 end_ = other.end_;
278 begin_pos_ = other.begin_pos_;
279 last_pos_ = other.last_pos_;
280 return *this;
281 }
282#else
283 subrange(subrange const&) = default;
284 subrange& operator=(subrange const&) = default;
285#endif
286
287 template<
288 bool isMutable_ = isMutable,
289 class = typename std::enable_if<! isMutable_>::type>
290 subrange(
291 subrange<true> const& other) noexcept
292 : b_(other.b_)
293 , begin_(other.begin_)
294 , end_(other.end_)
295 , begin_pos_(other.begin_pos_)
296 , last_pos_(other.last_pos_)
297 {
298 }
299
300 template<
301 bool isMutable_ = isMutable,
302 class = typename std::enable_if<! isMutable_>::type>
303 subrange& operator=(
304 subrange<true> const& other) noexcept
305 {
306 b_ = other.b_;
307 begin_ = other.begin_;
308 end_ = other.end_;
309 begin_pos_ = other.begin_pos_;
310 last_pos_ = other.last_pos_;
311 return *this;
312 }
313
314 const_iterator begin() const noexcept;
315 const_iterator end() const noexcept;
316
317 std::size_t
318 buffer_bytes() const noexcept
319 {
320 return b_->size();
321 }
322};
323
324#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
325# pragma warning (pop)
326#endif
327
328//------------------------------------------------------------------------------
329
330template<class Allocator>
331template<bool isMutable>
332class
333 basic_multi_buffer<Allocator>::
334 subrange<isMutable>::
335 const_iterator
336{
337 friend class subrange;
338
339 subrange const* sr_ = nullptr;
340 typename list_type::const_iterator it_;
341
342 const_iterator(
343 subrange const& sr, typename
344 list_type::const_iterator const& it) noexcept
345 : sr_(&sr)
346 , it_(it)
347 {
348 }
349
350public:
351 using value_type =
352 typename subrange::value_type;
353 using pointer = value_type const*;
354 using reference = value_type;
355 using difference_type = std::ptrdiff_t;
356 using iterator_category =
357 std::bidirectional_iterator_tag;
358
359 const_iterator() = default;
360 const_iterator(
361 const_iterator const& other) = default;
362 const_iterator& operator=(
363 const_iterator const& other) = default;
364
365 bool
366 operator==(
367 const_iterator const& other) const noexcept
368 {
369 return sr_ == other.sr_ && it_ == other.it_;
370 }
371
372 bool
373 operator!=(
374 const_iterator const& other) const noexcept
375 {
376 return !(*this == other);
377 }
378
379 reference
380 operator*() const noexcept
381 {
382 value_type result;
383 BOOST_ASSERT(sr_->last_pos_ != 0);
384 if(it_ == std::prev(sr_->end_))
385 result = {
386 it_->data(), sr_->last_pos_ };
387 else
388 result = {
389 it_->data(), it_->size() };
390 if(it_ == sr_->begin_)
391 result += sr_->begin_pos_;
392 return result;
393 }
394
395 pointer
396 operator->() const = delete;
397
398 const_iterator&
399 operator++() noexcept
400 {
401 ++it_;
402 return *this;
403 }
404
405 const_iterator
406 operator++(int) noexcept
407 {
408 auto temp = *this;
409 ++(*this);
410 return temp;
411 }
412
413 const_iterator&
414 operator--() noexcept
415 {
416 --it_;
417 return *this;
418 }
419
420 const_iterator
421 operator--(int) noexcept
422 {
423 auto temp = *this;
424 --(*this);
425 return temp;
426 }
427};
428
429//------------------------------------------------------------------------------
430
431template<class Allocator>
432template<bool isMutable>
433auto
434basic_multi_buffer<Allocator>::
435subrange<isMutable>::
436begin() const noexcept ->
437 const_iterator
438{
439 return const_iterator(
440 *this, begin_);
441}
442
443template<class Allocator>
444template<bool isMutable>
445auto
446basic_multi_buffer<Allocator>::
447subrange<isMutable>::
448end() const noexcept ->
449 const_iterator
450{
451 return const_iterator(
452 *this, end_);
453}
454
455//------------------------------------------------------------------------------
456
457template<class Allocator>
458basic_multi_buffer<Allocator>::
459~basic_multi_buffer()
460{
461 destroy(list_);
462}
463
464template<class Allocator>
465basic_multi_buffer<Allocator>::
466basic_multi_buffer() noexcept(default_nothrow)
467 : max_(alloc_traits::max_size(this->get()))
468 , out_(list_.end())
469{
470}
471
472template<class Allocator>
473basic_multi_buffer<Allocator>::
474basic_multi_buffer(
475 std::size_t limit) noexcept(default_nothrow)
476 : max_(limit)
477 , out_(list_.end())
478{
479}
480
481template<class Allocator>
482basic_multi_buffer<Allocator>::
483basic_multi_buffer(
484 Allocator const& alloc) noexcept
485 : boost::empty_value<Allocator>(
486 boost::empty_init_t(), alloc)
487 , max_(alloc_traits::max_size(this->get()))
488 , out_(list_.end())
489{
490}
491
492template<class Allocator>
493basic_multi_buffer<Allocator>::
494basic_multi_buffer(
495 std::size_t limit,
496 Allocator const& alloc) noexcept
497 : boost::empty_value<Allocator>(
498 boost::empty_init_t(), alloc)
499 , max_(limit)
500 , out_(list_.end())
501{
502}
503
504template<class Allocator>
505basic_multi_buffer<Allocator>::
506basic_multi_buffer(
507 basic_multi_buffer&& other) noexcept
508 : boost::empty_value<Allocator>(
509 boost::empty_init_t(), std::move(other.get()))
510 , max_(other.max_)
511 , in_size_(boost::exchange(other.in_size_, 0))
512 , in_pos_(boost::exchange(other.in_pos_, 0))
513 , out_pos_(boost::exchange(other.out_pos_, 0))
514 , out_end_(boost::exchange(other.out_end_, 0))
515{
516 auto const at_end =
517 other.out_ == other.list_.end();
518 list_ = std::move(other.list_);
519 out_ = at_end ? list_.end() : other.out_;
520 other.out_ = other.list_.end();
521}
522
523template<class Allocator>
524basic_multi_buffer<Allocator>::
525basic_multi_buffer(
526 basic_multi_buffer&& other,
527 Allocator const& alloc)
528 : boost::empty_value<Allocator>(
529 boost::empty_init_t(), alloc)
530 , max_(other.max_)
531{
532 if(this->get() != other.get())
533 {
534 out_ = list_.end();
535 copy_from(other);
536 return;
537 }
538
539 auto const at_end =
540 other.out_ == other.list_.end();
541 list_ = std::move(other.list_);
542 out_ = at_end ? list_.end() : other.out_;
543 in_size_ = other.in_size_;
544 in_pos_ = other.in_pos_;
545 out_pos_ = other.out_pos_;
546 out_end_ = other.out_end_;
547 other.in_size_ = 0;
548 other.out_ = other.list_.end();
549 other.in_pos_ = 0;
550 other.out_pos_ = 0;
551 other.out_end_ = 0;
552}
553
554template<class Allocator>
555basic_multi_buffer<Allocator>::
556basic_multi_buffer(
557 basic_multi_buffer const& other)
558 : boost::empty_value<Allocator>(
559 boost::empty_init_t(), alloc_traits::
560 select_on_container_copy_construction(
561 other.get()))
562 , max_(other.max_)
563 , out_(list_.end())
564{
565 copy_from(other);
566}
567
568template<class Allocator>
569basic_multi_buffer<Allocator>::
570basic_multi_buffer(
571 basic_multi_buffer const& other,
572 Allocator const& alloc)
573 : boost::empty_value<Allocator>(
574 boost::empty_init_t(), alloc)
575 , max_(other.max_)
576 , out_(list_.end())
577{
578 copy_from(other);
579}
580
581template<class Allocator>
582template<class OtherAlloc>
583basic_multi_buffer<Allocator>::
584basic_multi_buffer(
585 basic_multi_buffer<OtherAlloc> const& other)
586 : out_(list_.end())
587{
588 copy_from(other);
589}
590
591template<class Allocator>
592template<class OtherAlloc>
593basic_multi_buffer<Allocator>::
594basic_multi_buffer(
595 basic_multi_buffer<OtherAlloc> const& other,
596 allocator_type const& alloc)
597 : boost::empty_value<Allocator>(
598 boost::empty_init_t(), alloc)
599 , max_(other.max_)
600 , out_(list_.end())
601{
602 copy_from(other);
603}
604
605template<class Allocator>
606auto
607basic_multi_buffer<Allocator>::
608operator=(basic_multi_buffer&& other) ->
609 basic_multi_buffer&
610{
611 if(this == &other)
612 return *this;
613 clear();
614 max_ = other.max_;
615 move_assign(other, pocma{});
616 return *this;
617}
618
619template<class Allocator>
620auto
621basic_multi_buffer<Allocator>::
622operator=(basic_multi_buffer const& other) ->
623basic_multi_buffer&
624{
625 if(this == &other)
626 return *this;
627 copy_assign(other, pocca{});
628 return *this;
629}
630
631template<class Allocator>
632template<class OtherAlloc>
633auto
634basic_multi_buffer<Allocator>::
635operator=(
636 basic_multi_buffer<OtherAlloc> const& other) ->
637 basic_multi_buffer&
638{
639 copy_from(other);
640 return *this;
641}
642
643//------------------------------------------------------------------------------
644
645template<class Allocator>
646std::size_t
647basic_multi_buffer<Allocator>::
648capacity() const noexcept
649{
650 auto pos = out_;
651 if(pos == list_.end())
652 return in_size_;
653 auto n = pos->size() - out_pos_;
654 while(++pos != list_.end())
655 n += pos->size();
656 return in_size_ + n;
657}
658
659template<class Allocator>
660auto
661basic_multi_buffer<Allocator>::
662data() const noexcept ->
663 const_buffers_type
664{
665 return const_buffers_type(
666 *this, 0, in_size_);
667}
668
669template<class Allocator>
670auto
671basic_multi_buffer<Allocator>::
672data() noexcept ->
673 mutable_buffers_type
674{
675 return mutable_buffers_type(
676 *this, 0, in_size_);
677}
678
679template<class Allocator>
680void
681basic_multi_buffer<Allocator>::
682reserve(std::size_t n)
683{
684 // VFALCO The amount needs to be adjusted for
685 // the sizeof(element) plus padding
686 if(n > alloc_traits::max_size(this->get()))
687 BOOST_THROW_EXCEPTION(std::length_error(
688 "A basic_multi_buffer exceeded the allocator's maximum size"));
689 std::size_t total = in_size_;
690 if(n <= total)
691 return;
692 if(out_ != list_.end())
693 {
694 total += out_->size() - out_pos_;
695 if(n <= total)
696 return;
697 for(auto it = out_;;)
698 {
699 if(++it == list_.end())
700 break;
701 total += it->size();
702 if(n <= total)
703 return;
704 }
705 }
706 BOOST_ASSERT(n > total);
707 (void)prepare(n: n - size());
708}
709
710template<class Allocator>
711void
712basic_multi_buffer<Allocator>::
713shrink_to_fit()
714{
715 // empty list
716 if(list_.empty())
717 return;
718
719 // zero readable bytes
720 if(in_size_ == 0)
721 {
722 destroy(list_);
723 list_.clear();
724 out_ = list_.end();
725 in_size_ = 0;
726 in_pos_ = 0;
727 out_pos_ = 0;
728 out_end_ = 0;
729 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
730 debug_check();
731 #endif
732 return;
733 }
734
735 // one or more unused output buffers
736 if(out_ != list_.end())
737 {
738 if(out_ != list_.iterator_to(list_.back()))
739 {
740 // unused list
741 list_type extra;
742 extra.splice(
743 extra.end(),
744 list_,
745 std::next(out_),
746 list_.end());
747 destroy(extra);
748 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
749 debug_check();
750 #endif
751 }
752
753 // unused out_
754 BOOST_ASSERT(out_ ==
755 list_.iterator_to(list_.back()));
756 if(out_pos_ == 0)
757 {
758 BOOST_ASSERT(out_ != list_.begin());
759 auto& e = *out_;
760 list_.erase(out_);
761 out_ = list_.end();
762 destroy(e);
763 out_end_ = 0;
764 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
765 debug_check();
766 #endif
767 }
768 }
769
770 auto const replace =
771 [&](iter pos, element& e)
772 {
773 auto it =
774 list_.insert(pos, e);
775 auto& e0 = *pos;
776 list_.erase(pos);
777 destroy(e0);
778 return it;
779 };
780
781 // partial last buffer
782 if(out_ != list_.begin() && out_ != list_.end())
783 {
784 BOOST_ASSERT(out_ ==
785 list_.iterator_to(list_.back()));
786 BOOST_ASSERT(out_pos_ != 0);
787 auto& e = alloc(size: out_pos_);
788 std::memcpy(
789 dest: e.data(),
790 src: out_->data(),
791 n: out_pos_);
792 replace(out_, e);
793 out_ = list_.end();
794 out_pos_ = 0;
795 out_end_ = 0;
796 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
797 debug_check();
798 #endif
799 }
800
801 // partial first buffer
802 if(in_pos_ != 0)
803 {
804 if(out_ != list_.begin())
805 {
806 auto const n =
807 list_.front().size() - in_pos_;
808 auto& e = alloc(size: n);
809 std::memcpy(
810 dest: e.data(),
811 src: list_.front().data() + in_pos_,
812 n: n);
813 replace(list_.begin(), e);
814 in_pos_ = 0;
815 }
816 else
817 {
818 BOOST_ASSERT(out_ ==
819 list_.iterator_to(list_.back()));
820 BOOST_ASSERT(out_pos_ > in_pos_);
821 auto const n = out_pos_ - in_pos_;
822 auto& e = alloc(size: n);
823 std::memcpy(
824 dest: e.data(),
825 src: list_.front().data() + in_pos_,
826 n: n);
827 replace(list_.begin(), e);
828 in_pos_ = 0;
829 out_ = list_.end();
830 }
831 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
832 debug_check();
833 #endif
834 }
835}
836
837template<class Allocator>
838void
839basic_multi_buffer<Allocator>::
840clear() noexcept
841{
842 out_ = list_.begin();
843 in_size_ = 0;
844 in_pos_ = 0;
845 out_pos_ = 0;
846 out_end_ = 0;
847}
848
849template<class Allocator>
850auto
851basic_multi_buffer<Allocator>::
852prepare(size_type n) ->
853 mutable_buffers_type
854{
855 auto const n0 = n;
856 if(in_size_ > max_ || n > (max_ - in_size_))
857 BOOST_THROW_EXCEPTION(std::length_error{
858 "basic_multi_buffer too long"});
859 list_type reuse;
860 std::size_t total = in_size_;
861 // put all empty buffers on reuse list
862 if(out_ != list_.end())
863 {
864 total += out_->size() - out_pos_;
865 if(out_ != list_.iterator_to(list_.back()))
866 {
867 out_end_ = out_->size();
868 reuse.splice(reuse.end(), list_,
869 std::next(out_), list_.end());
870 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
871 debug_check();
872 #endif
873 }
874 auto const avail = out_->size() - out_pos_;
875 if(n > avail)
876 {
877 out_end_ = out_->size();
878 n -= avail;
879 }
880 else
881 {
882 out_end_ = out_pos_ + n;
883 n = 0;
884 }
885 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
886 debug_check();
887 #endif
888 }
889 // get space from reuse buffers
890 while(n > 0 && ! reuse.empty())
891 {
892 auto& e = reuse.front();
893 reuse.erase(reuse.iterator_to(e));
894 list_.push_back(e);
895 total += e.size();
896 if(n > e.size())
897 {
898 out_end_ = e.size();
899 n -= e.size();
900 }
901 else
902 {
903 out_end_ = n;
904 n = 0;
905 }
906 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
907 debug_check();
908 #endif
909 }
910 BOOST_ASSERT(total <= max_);
911 if(! reuse.empty() || n > 0)
912 {
913 destroy(reuse);
914 if(n > 0)
915 {
916 std::size_t const growth_factor = 2;
917 std::size_t altn = in_size_ * growth_factor;
918 // Overflow detection:
919 if(in_size_ > altn)
920 altn = (std::numeric_limits<std::size_t>::max)();
921 else
922 altn = (std::max<std::size_t>)(a: 512, b: altn);
923 auto const size =
924 (std::min<std::size_t>)(
925 max_ - total,
926 (std::max<std::size_t>)(n, altn));
927 auto& e = alloc(size);
928 list_.push_back(e);
929 if(out_ == list_.end())
930 out_ = list_.iterator_to(e);
931 out_end_ = n;
932 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
933 debug_check();
934 #endif
935 }
936 }
937 auto const result =
938 mutable_buffers_type(
939 *this, in_size_, n0);
940 BOOST_ASSERT(
941 net::buffer_size(result) == n0);
942 return result;
943}
944
945template<class Allocator>
946void
947basic_multi_buffer<Allocator>::
948commit(size_type n) noexcept
949{
950 if(list_.empty())
951 return;
952 if(out_ == list_.end())
953 return;
954 auto const back =
955 list_.iterator_to(list_.back());
956 while(out_ != back)
957 {
958 auto const avail =
959 out_->size() - out_pos_;
960 if(n < avail)
961 {
962 out_pos_ += n;
963 in_size_ += n;
964 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
965 debug_check();
966 #endif
967 return;
968 }
969 ++out_;
970 n -= avail;
971 out_pos_ = 0;
972 in_size_ += avail;
973 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
974 debug_check();
975 #endif
976 }
977
978 n = (std::min)(n, out_end_ - out_pos_);
979 out_pos_ += n;
980 in_size_ += n;
981 if(out_pos_ == out_->size())
982 {
983 ++out_;
984 out_pos_ = 0;
985 out_end_ = 0;
986 }
987#if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
988 debug_check();
989#endif
990}
991
992template<class Allocator>
993void
994basic_multi_buffer<Allocator>::
995consume(size_type n) noexcept
996{
997 if(list_.empty())
998 return;
999 for(;;)
1000 {
1001 if(list_.begin() != out_)
1002 {
1003 auto const avail =
1004 list_.front().size() - in_pos_;
1005 if(n < avail)
1006 {
1007 in_size_ -= n;
1008 in_pos_ += n;
1009 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1010 debug_check();
1011 #endif
1012 break;
1013 }
1014 n -= avail;
1015 in_size_ -= avail;
1016 in_pos_ = 0;
1017 auto& e = list_.front();
1018 list_.erase(list_.iterator_to(e));
1019 destroy(e);
1020 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1021 debug_check();
1022 #endif
1023 }
1024 else
1025 {
1026 auto const avail = out_pos_ - in_pos_;
1027 if(n < avail)
1028 {
1029 in_size_ -= n;
1030 in_pos_ += n;
1031 }
1032 else
1033 {
1034 in_size_ = 0;
1035 if(out_ != list_.iterator_to(list_.back()) ||
1036 out_pos_ != out_end_)
1037 {
1038 in_pos_ = out_pos_;
1039 }
1040 else
1041 {
1042 // Input and output sequences are empty, reuse buffer.
1043 // Alternatively we could deallocate it.
1044 in_pos_ = 0;
1045 out_pos_ = 0;
1046 out_end_ = 0;
1047 }
1048 }
1049 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1050 debug_check();
1051 #endif
1052 break;
1053 }
1054 }
1055}
1056
1057template<class Allocator>
1058template<class OtherAlloc>
1059void
1060basic_multi_buffer<Allocator>::
1061copy_from(basic_multi_buffer<OtherAlloc> const& other)
1062{
1063 clear();
1064 max_ = other.max_;
1065 if(other.size() == 0)
1066 return;
1067 commit(n: net::buffer_copy(
1068 prepare(n: other.size()), other.data()));
1069}
1070
1071template<class Allocator>
1072void
1073basic_multi_buffer<Allocator>::
1074move_assign(basic_multi_buffer& other, std::true_type) noexcept
1075{
1076 this->get() = std::move(other.get());
1077 auto const at_end =
1078 other.out_ == other.list_.end();
1079 list_ = std::move(other.list_);
1080 out_ = at_end ? list_.end() : other.out_;
1081
1082 in_size_ = other.in_size_;
1083 in_pos_ = other.in_pos_;
1084 out_pos_ = other.out_pos_;
1085 out_end_ = other.out_end_;
1086 max_ = other.max_;
1087
1088 other.in_size_ = 0;
1089 other.out_ = other.list_.end();
1090 other.in_pos_ = 0;
1091 other.out_pos_ = 0;
1092 other.out_end_ = 0;
1093}
1094
1095template<class Allocator>
1096void
1097basic_multi_buffer<Allocator>::
1098move_assign(basic_multi_buffer& other, std::false_type)
1099{
1100 if(this->get() != other.get())
1101 {
1102 copy_from(other);
1103 }
1104 else
1105 {
1106 move_assign(other, std::true_type{});
1107 }
1108}
1109
1110template<class Allocator>
1111void
1112basic_multi_buffer<Allocator>::
1113copy_assign(
1114 basic_multi_buffer const& other, std::false_type)
1115{
1116 copy_from(other);
1117}
1118
1119template<class Allocator>
1120void
1121basic_multi_buffer<Allocator>::
1122copy_assign(
1123 basic_multi_buffer const& other, std::true_type)
1124{
1125 clear();
1126 this->get() = other.get();
1127 copy_from(other);
1128}
1129
1130template<class Allocator>
1131void
1132basic_multi_buffer<Allocator>::
1133swap(basic_multi_buffer& other) noexcept
1134{
1135 swap(other, typename
1136 alloc_traits::propagate_on_container_swap{});
1137}
1138
1139template<class Allocator>
1140void
1141basic_multi_buffer<Allocator>::
1142swap(basic_multi_buffer& other, std::true_type) noexcept
1143{
1144 using std::swap;
1145 auto const at_end0 =
1146 out_ == list_.end();
1147 auto const at_end1 =
1148 other.out_ == other.list_.end();
1149 swap(this->get(), other.get());
1150 swap(list_, other.list_);
1151 swap(out_, other.out_);
1152 if(at_end1)
1153 out_ = list_.end();
1154 if(at_end0)
1155 other.out_ = other.list_.end();
1156 swap(in_size_, other.in_size_);
1157 swap(in_pos_, other.in_pos_);
1158 swap(out_pos_, other.out_pos_);
1159 swap(out_end_, other.out_end_);
1160}
1161
1162template<class Allocator>
1163void
1164basic_multi_buffer<Allocator>::
1165swap(basic_multi_buffer& other, std::false_type) noexcept
1166{
1167 BOOST_ASSERT(this->get() == other.get());
1168 using std::swap;
1169 auto const at_end0 =
1170 out_ == list_.end();
1171 auto const at_end1 =
1172 other.out_ == other.list_.end();
1173 swap(list_, other.list_);
1174 swap(out_, other.out_);
1175 if(at_end1)
1176 out_ = list_.end();
1177 if(at_end0)
1178 other.out_ = other.list_.end();
1179 swap(in_size_, other.in_size_);
1180 swap(in_pos_, other.in_pos_);
1181 swap(out_pos_, other.out_pos_);
1182 swap(out_end_, other.out_end_);
1183}
1184
1185template<class Allocator>
1186void
1187swap(
1188 basic_multi_buffer<Allocator>& lhs,
1189 basic_multi_buffer<Allocator>& rhs) noexcept
1190{
1191 lhs.swap(rhs);
1192}
1193
1194template<class Allocator>
1195void
1196basic_multi_buffer<Allocator>::
1197destroy(list_type& list) noexcept
1198{
1199 for(auto it = list.begin();
1200 it != list.end();)
1201 destroy(*it++);
1202}
1203
1204template<class Allocator>
1205void
1206basic_multi_buffer<Allocator>::
1207destroy(element& e)
1208{
1209 auto a = rebind_type{this->get()};
1210 auto const n =
1211 (sizeof(element) + e.size() +
1212 sizeof(align_type) - 1) /
1213 sizeof(align_type);
1214 e.~element();
1215 alloc_traits::deallocate(a,
1216 reinterpret_cast<align_type*>(&e), n);
1217}
1218
1219template<class Allocator>
1220auto
1221basic_multi_buffer<Allocator>::
1222alloc(std::size_t size) ->
1223 element&
1224{
1225 if(size > alloc_traits::max_size(this->get()))
1226 BOOST_THROW_EXCEPTION(std::length_error(
1227 "A basic_multi_buffer exceeded the allocator's maximum size"));
1228 auto a = rebind_type{this->get()};
1229 auto const p = alloc_traits::allocate(a,
1230 (sizeof(element) + size + sizeof(align_type) - 1) /
1231 sizeof(align_type));
1232 return *(::new(p) element(size));
1233}
1234
1235template<class Allocator>
1236void
1237basic_multi_buffer<Allocator>::
1238debug_check() const
1239{
1240#ifndef NDEBUG
1241 BOOST_ASSERT(buffer_bytes(data()) == in_size_);
1242 if(list_.empty())
1243 {
1244 BOOST_ASSERT(in_pos_ == 0);
1245 BOOST_ASSERT(in_size_ == 0);
1246 BOOST_ASSERT(out_pos_ == 0);
1247 BOOST_ASSERT(out_end_ == 0);
1248 BOOST_ASSERT(out_ == list_.end());
1249 return;
1250 }
1251
1252 auto const& front = list_.front();
1253
1254 BOOST_ASSERT(in_pos_ < front.size());
1255
1256 if(out_ == list_.end())
1257 {
1258 BOOST_ASSERT(out_pos_ == 0);
1259 BOOST_ASSERT(out_end_ == 0);
1260 }
1261 else
1262 {
1263 auto const& out = *out_;
1264 auto const& back = list_.back();
1265
1266 BOOST_ASSERT(out_end_ <= back.size());
1267 BOOST_ASSERT(out_pos_ < out.size());
1268 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
1269 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
1270 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
1271 }
1272#endif
1273}
1274
1275} // beast
1276} // boost
1277
1278#endif
1279

source code of boost/libs/beast/include/boost/beast/core/impl/multi_buffer.hpp