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
26 namespace boost {
27 namespace beast {
28
29 /* These diagrams illustrate the layout and state variables.
30
31 1 Input and output contained entirely in one element:
32
33 0 out_
34 |<------+-----------+--------------------------------+----->|
35 in_pos_ out_pos_ out_end_
36
37
38 2 Output contained in first and second elements:
39
40 out_
41 |<------+-----------+------>| |<-------------------+----->|
42 in_pos_ out_pos_ out_end_
43
44
45 3 Output contained in the second element:
46
47 out_
48 |<------+------------------>| |<----+--------------+----->|
49 in_pos_ out_pos_ out_end_
50
51
52 4 Output contained in second and third elements:
53
54 out_
55 |<------+------->| |<-------+------>| |<---------+----->|
56 in_pos_ out_pos_ out_end_
57
58
59 5 Input sequence is empty:
60
61 out_
62 |<------+------------------>| |<-------------------+----->|
63 out_pos_ out_end_
64 in_pos_
65
66 6 Output sequence is empty:
67
68 out_
69 |<------+------------------>| |<------+------------------>|
70 in_pos_ out_pos_
71 out_end_
72
73
74 7 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
82 8 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
100 template<class Allocator>
101 template<bool isMutable>
102 class 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
subrange(basic_multi_buffer const & b,size_type pos,size_type n)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
253 public:
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)
subrange(subrange const & other)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
operator =(subrange const & other)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>
subrange(subrange<true> const & other)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>
operator =(subrange<true> const & other)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
buffer_bytes() const318 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
330 template<class Allocator>
331 template<bool isMutable>
332 class
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
const_iterator(subrange const & sr,typename list_type::const_iterator const & it)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
350 public:
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
operator ==(const_iterator const & other) const366 operator==(
367 const_iterator const& other) const noexcept
368 {
369 return sr_ == other.sr_ && it_ == other.it_;
370 }
371
372 bool
operator !=(const_iterator const & other) const373 operator!=(
374 const_iterator const& other) const noexcept
375 {
376 return !(*this == other);
377 }
378
379 reference
operator *() const380 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&
operator ++()399 operator++() noexcept
400 {
401 ++it_;
402 return *this;
403 }
404
405 const_iterator
operator ++(int)406 operator++(int) noexcept
407 {
408 auto temp = *this;
409 ++(*this);
410 return temp;
411 }
412
413 const_iterator&
operator --()414 operator--() noexcept
415 {
416 --it_;
417 return *this;
418 }
419
420 const_iterator
operator --(int)421 operator--(int) noexcept
422 {
423 auto temp = *this;
424 --(*this);
425 return temp;
426 }
427 };
428
429 //------------------------------------------------------------------------------
430
431 template<class Allocator>
432 template<bool isMutable>
433 auto
434 basic_multi_buffer<Allocator>::
435 subrange<isMutable>::
begin() const436 begin() const noexcept ->
437 const_iterator
438 {
439 return const_iterator(
440 *this, begin_);
441 }
442
443 template<class Allocator>
444 template<bool isMutable>
445 auto
446 basic_multi_buffer<Allocator>::
447 subrange<isMutable>::
end() const448 end() const noexcept ->
449 const_iterator
450 {
451 return const_iterator(
452 *this, end_);
453 }
454
455 //------------------------------------------------------------------------------
456
457 template<class Allocator>
458 basic_multi_buffer<Allocator>::
~basic_multi_buffer()459 ~basic_multi_buffer()
460 {
461 destroy(list_);
462 }
463
464 template<class Allocator>
465 basic_multi_buffer<Allocator>::
basic_multi_buffer()466 basic_multi_buffer() noexcept(default_nothrow)
467 : max_(alloc_traits::max_size(this->get()))
468 , out_(list_.end())
469 {
470 }
471
472 template<class Allocator>
473 basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit)474 basic_multi_buffer(
475 std::size_t limit) noexcept(default_nothrow)
476 : max_(limit)
477 , out_(list_.end())
478 {
479 }
480
481 template<class Allocator>
482 basic_multi_buffer<Allocator>::
basic_multi_buffer(Allocator const & alloc)483 basic_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
492 template<class Allocator>
493 basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit,Allocator const & alloc)494 basic_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
504 template<class Allocator>
505 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer && other)506 basic_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
523 template<class Allocator>
524 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer && other,Allocator const & alloc)525 basic_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
554 template<class Allocator>
555 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const & other)556 basic_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
568 template<class Allocator>
569 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const & other,Allocator const & alloc)570 basic_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
581 template<class Allocator>
582 template<class OtherAlloc>
583 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer<OtherAlloc> const & other)584 basic_multi_buffer(
585 basic_multi_buffer<OtherAlloc> const& other)
586 : out_(list_.end())
587 {
588 copy_from(other);
589 }
590
591 template<class Allocator>
592 template<class OtherAlloc>
593 basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer<OtherAlloc> const & other,allocator_type const & alloc)594 basic_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
605 template<class Allocator>
606 auto
607 basic_multi_buffer<Allocator>::
operator =(basic_multi_buffer && other)608 operator=(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
619 template<class Allocator>
620 auto
621 basic_multi_buffer<Allocator>::
operator =(basic_multi_buffer const & other)622 operator=(basic_multi_buffer const& other) ->
623 basic_multi_buffer&
624 {
625 if(this == &other)
626 return *this;
627 copy_assign(other, pocca{});
628 return *this;
629 }
630
631 template<class Allocator>
632 template<class OtherAlloc>
633 auto
634 basic_multi_buffer<Allocator>::
operator =(basic_multi_buffer<OtherAlloc> const & other)635 operator=(
636 basic_multi_buffer<OtherAlloc> const& other) ->
637 basic_multi_buffer&
638 {
639 copy_from(other);
640 return *this;
641 }
642
643 //------------------------------------------------------------------------------
644
645 template<class Allocator>
646 std::size_t
647 basic_multi_buffer<Allocator>::
capacity() const648 capacity() 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
659 template<class Allocator>
660 auto
661 basic_multi_buffer<Allocator>::
data() const662 data() const noexcept ->
663 const_buffers_type
664 {
665 return const_buffers_type(
666 *this, 0, in_size_);
667 }
668
669 template<class Allocator>
670 auto
671 basic_multi_buffer<Allocator>::
data()672 data() noexcept ->
673 mutable_buffers_type
674 {
675 return mutable_buffers_type(
676 *this, 0, in_size_);
677 }
678
679 template<class Allocator>
680 void
681 basic_multi_buffer<Allocator>::
reserve(std::size_t n)682 reserve(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 - size());
708 }
709
710 template<class Allocator>
711 void
712 basic_multi_buffer<Allocator>::
shrink_to_fit()713 shrink_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(list_.size() > 1 && out_ != list_.end())
783 {
784 BOOST_ASSERT(out_ ==
785 list_.iterator_to(list_.back()));
786 BOOST_ASSERT(out_pos_ != 0);
787 auto& e = alloc(out_pos_);
788 std::memcpy(
789 e.data(),
790 out_->data(),
791 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(n);
809 std::memcpy(
810 e.data(),
811 list_.front().data() + in_pos_,
812 n);
813 replace(list_.begin(), e);
814 in_pos_ = 0;
815 }
816 else
817 {
818 BOOST_ASSERT(list_.size() == 1);
819 BOOST_ASSERT(out_pos_ > in_pos_);
820 auto const n = out_pos_ - in_pos_;
821 auto& e = alloc(n);
822 std::memcpy(
823 e.data(),
824 list_.front().data() + in_pos_,
825 n);
826 replace(list_.begin(), e);
827 in_pos_ = 0;
828 out_ = list_.end();
829 }
830 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
831 debug_check();
832 #endif
833 }
834 }
835
836 template<class Allocator>
837 void
838 basic_multi_buffer<Allocator>::
clear()839 clear() noexcept
840 {
841 out_ = list_.begin();
842 in_size_ = 0;
843 in_pos_ = 0;
844 out_pos_ = 0;
845 out_end_ = 0;
846 }
847
848 template<class Allocator>
849 auto
850 basic_multi_buffer<Allocator>::
prepare(size_type n)851 prepare(size_type n) ->
852 mutable_buffers_type
853 {
854 auto const n0 = n;
855 if(in_size_ > max_ || n > (max_ - in_size_))
856 BOOST_THROW_EXCEPTION(std::length_error{
857 "basic_multi_buffer too long"});
858 list_type reuse;
859 std::size_t total = in_size_;
860 // put all empty buffers on reuse list
861 if(out_ != list_.end())
862 {
863 total += out_->size() - out_pos_;
864 if(out_ != list_.iterator_to(list_.back()))
865 {
866 out_end_ = out_->size();
867 reuse.splice(reuse.end(), list_,
868 std::next(out_), list_.end());
869 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
870 debug_check();
871 #endif
872 }
873 auto const avail = out_->size() - out_pos_;
874 if(n > avail)
875 {
876 out_end_ = out_->size();
877 n -= avail;
878 }
879 else
880 {
881 out_end_ = out_pos_ + n;
882 n = 0;
883 }
884 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
885 debug_check();
886 #endif
887 }
888 // get space from reuse buffers
889 while(n > 0 && ! reuse.empty())
890 {
891 auto& e = reuse.front();
892 reuse.erase(reuse.iterator_to(e));
893 list_.push_back(e);
894 total += e.size();
895 if(n > e.size())
896 {
897 out_end_ = e.size();
898 n -= e.size();
899 }
900 else
901 {
902 out_end_ = n;
903 n = 0;
904 }
905 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
906 debug_check();
907 #endif
908 }
909 BOOST_ASSERT(total <= max_);
910 if(! reuse.empty() || n > 0)
911 {
912 destroy(reuse);
913 if(n > 0)
914 {
915 auto const growth_factor = 2.0f;
916 auto const size =
917 (std::min<std::size_t>)(
918 max_ - total,
919 (std::max<std::size_t>)({
920 static_cast<std::size_t>(
921 in_size_ * growth_factor - in_size_),
922 512,
923 n}));
924 auto& e = alloc(size);
925 list_.push_back(e);
926 if(out_ == list_.end())
927 out_ = list_.iterator_to(e);
928 out_end_ = n;
929 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
930 debug_check();
931 #endif
932 }
933 }
934 auto const result =
935 mutable_buffers_type(
936 *this, in_size_, n0);
937 BOOST_ASSERT(
938 net::buffer_size(result) == n0);
939 return result;
940 }
941
942 template<class Allocator>
943 void
944 basic_multi_buffer<Allocator>::
commit(size_type n)945 commit(size_type n) noexcept
946 {
947 if(list_.empty())
948 return;
949 if(out_ == list_.end())
950 return;
951 auto const back =
952 list_.iterator_to(list_.back());
953 while(out_ != back)
954 {
955 auto const avail =
956 out_->size() - out_pos_;
957 if(n < avail)
958 {
959 out_pos_ += n;
960 in_size_ += n;
961 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
962 debug_check();
963 #endif
964 return;
965 }
966 ++out_;
967 n -= avail;
968 out_pos_ = 0;
969 in_size_ += avail;
970 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
971 debug_check();
972 #endif
973 }
974
975 n = (std::min)(n, out_end_ - out_pos_);
976 out_pos_ += n;
977 in_size_ += n;
978 if(out_pos_ == out_->size())
979 {
980 ++out_;
981 out_pos_ = 0;
982 out_end_ = 0;
983 }
984 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
985 debug_check();
986 #endif
987 }
988
989 template<class Allocator>
990 void
991 basic_multi_buffer<Allocator>::
consume(size_type n)992 consume(size_type n) noexcept
993 {
994 if(list_.empty())
995 return;
996 for(;;)
997 {
998 if(list_.begin() != out_)
999 {
1000 auto const avail =
1001 list_.front().size() - in_pos_;
1002 if(n < avail)
1003 {
1004 in_size_ -= n;
1005 in_pos_ += n;
1006 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1007 debug_check();
1008 #endif
1009 break;
1010 }
1011 n -= avail;
1012 in_size_ -= avail;
1013 in_pos_ = 0;
1014 auto& e = list_.front();
1015 list_.erase(list_.iterator_to(e));
1016 destroy(e);
1017 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1018 debug_check();
1019 #endif
1020 }
1021 else
1022 {
1023 auto const avail = out_pos_ - in_pos_;
1024 if(n < avail)
1025 {
1026 in_size_ -= n;
1027 in_pos_ += n;
1028 }
1029 else
1030 {
1031 in_size_ = 0;
1032 if(out_ != list_.iterator_to(list_.back()) ||
1033 out_pos_ != out_end_)
1034 {
1035 in_pos_ = out_pos_;
1036 }
1037 else
1038 {
1039 // Input and output sequences are empty, reuse buffer.
1040 // Alternatively we could deallocate it.
1041 in_pos_ = 0;
1042 out_pos_ = 0;
1043 out_end_ = 0;
1044 }
1045 }
1046 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1047 debug_check();
1048 #endif
1049 break;
1050 }
1051 }
1052 }
1053
1054 template<class Allocator>
1055 template<class OtherAlloc>
1056 void
1057 basic_multi_buffer<Allocator>::
copy_from(basic_multi_buffer<OtherAlloc> const & other)1058 copy_from(basic_multi_buffer<OtherAlloc> const& other)
1059 {
1060 clear();
1061 max_ = other.max_;
1062 if(other.size() == 0)
1063 return;
1064 commit(net::buffer_copy(
1065 prepare(other.size()), other.data()));
1066 }
1067
1068 template<class Allocator>
1069 void
1070 basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer & other,std::true_type)1071 move_assign(basic_multi_buffer& other, std::true_type) noexcept
1072 {
1073 this->get() = std::move(other.get());
1074 auto const at_end =
1075 other.out_ == other.list_.end();
1076 list_ = std::move(other.list_);
1077 out_ = at_end ? list_.end() : other.out_;
1078
1079 in_size_ = other.in_size_;
1080 in_pos_ = other.in_pos_;
1081 out_pos_ = other.out_pos_;
1082 out_end_ = other.out_end_;
1083 max_ = other.max_;
1084
1085 other.in_size_ = 0;
1086 other.out_ = other.list_.end();
1087 other.in_pos_ = 0;
1088 other.out_pos_ = 0;
1089 other.out_end_ = 0;
1090 }
1091
1092 template<class Allocator>
1093 void
1094 basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer & other,std::false_type)1095 move_assign(basic_multi_buffer& other, std::false_type)
1096 {
1097 if(this->get() != other.get())
1098 {
1099 copy_from(other);
1100 }
1101 else
1102 {
1103 move_assign(other, std::true_type{});
1104 }
1105 }
1106
1107 template<class Allocator>
1108 void
1109 basic_multi_buffer<Allocator>::
copy_assign(basic_multi_buffer const & other,std::false_type)1110 copy_assign(
1111 basic_multi_buffer const& other, std::false_type)
1112 {
1113 copy_from(other);
1114 }
1115
1116 template<class Allocator>
1117 void
1118 basic_multi_buffer<Allocator>::
copy_assign(basic_multi_buffer const & other,std::true_type)1119 copy_assign(
1120 basic_multi_buffer const& other, std::true_type)
1121 {
1122 clear();
1123 this->get() = other.get();
1124 copy_from(other);
1125 }
1126
1127 template<class Allocator>
1128 void
1129 basic_multi_buffer<Allocator>::
swap(basic_multi_buffer & other)1130 swap(basic_multi_buffer& other) noexcept
1131 {
1132 swap(other, typename
1133 alloc_traits::propagate_on_container_swap{});
1134 }
1135
1136 template<class Allocator>
1137 void
1138 basic_multi_buffer<Allocator>::
swap(basic_multi_buffer & other,std::true_type)1139 swap(basic_multi_buffer& other, std::true_type) noexcept
1140 {
1141 using std::swap;
1142 auto const at_end0 =
1143 out_ == list_.end();
1144 auto const at_end1 =
1145 other.out_ == other.list_.end();
1146 swap(this->get(), other.get());
1147 swap(list_, other.list_);
1148 swap(out_, other.out_);
1149 if(at_end1)
1150 out_ = list_.end();
1151 if(at_end0)
1152 other.out_ = other.list_.end();
1153 swap(in_size_, other.in_size_);
1154 swap(in_pos_, other.in_pos_);
1155 swap(out_pos_, other.out_pos_);
1156 swap(out_end_, other.out_end_);
1157 }
1158
1159 template<class Allocator>
1160 void
1161 basic_multi_buffer<Allocator>::
swap(basic_multi_buffer & other,std::false_type)1162 swap(basic_multi_buffer& other, std::false_type) noexcept
1163 {
1164 BOOST_ASSERT(this->get() == other.get());
1165 using std::swap;
1166 auto const at_end0 =
1167 out_ == list_.end();
1168 auto const at_end1 =
1169 other.out_ == other.list_.end();
1170 swap(list_, other.list_);
1171 swap(out_, other.out_);
1172 if(at_end1)
1173 out_ = list_.end();
1174 if(at_end0)
1175 other.out_ = other.list_.end();
1176 swap(in_size_, other.in_size_);
1177 swap(in_pos_, other.in_pos_);
1178 swap(out_pos_, other.out_pos_);
1179 swap(out_end_, other.out_end_);
1180 }
1181
1182 template<class Allocator>
1183 void
swap(basic_multi_buffer<Allocator> & lhs,basic_multi_buffer<Allocator> & rhs)1184 swap(
1185 basic_multi_buffer<Allocator>& lhs,
1186 basic_multi_buffer<Allocator>& rhs) noexcept
1187 {
1188 lhs.swap(rhs);
1189 }
1190
1191 template<class Allocator>
1192 void
1193 basic_multi_buffer<Allocator>::
destroy(list_type & list)1194 destroy(list_type& list) noexcept
1195 {
1196 for(auto it = list.begin();
1197 it != list.end();)
1198 destroy(*it++);
1199 }
1200
1201 template<class Allocator>
1202 void
1203 basic_multi_buffer<Allocator>::
destroy(const_iter it)1204 destroy(const_iter it)
1205 {
1206 auto& e = list_.erase(it);
1207 destroy(e);
1208 }
1209
1210 template<class Allocator>
1211 void
1212 basic_multi_buffer<Allocator>::
destroy(element & e)1213 destroy(element& e)
1214 {
1215 auto a = rebind_type{this->get()};
1216 auto const n =
1217 (sizeof(element) + e.size() +
1218 sizeof(align_type) - 1) /
1219 sizeof(align_type);
1220 e.~element();
1221 alloc_traits::deallocate(a,
1222 reinterpret_cast<align_type*>(&e), n);
1223 }
1224
1225 template<class Allocator>
1226 auto
1227 basic_multi_buffer<Allocator>::
alloc(std::size_t size)1228 alloc(std::size_t size) ->
1229 element&
1230 {
1231 if(size > alloc_traits::max_size(this->get()))
1232 BOOST_THROW_EXCEPTION(std::length_error(
1233 "A basic_multi_buffer exceeded the allocator's maximum size"));
1234 auto a = rebind_type{this->get()};
1235 auto const p = alloc_traits::allocate(a,
1236 (sizeof(element) + size + sizeof(align_type) - 1) /
1237 sizeof(align_type));
1238 return *(::new(p) element(size));
1239 }
1240
1241 template<class Allocator>
1242 void
1243 basic_multi_buffer<Allocator>::
debug_check() const1244 debug_check() const
1245 {
1246 #ifndef NDEBUG
1247 BOOST_ASSERT(buffer_bytes(data()) == in_size_);
1248 if(list_.empty())
1249 {
1250 BOOST_ASSERT(in_pos_ == 0);
1251 BOOST_ASSERT(in_size_ == 0);
1252 BOOST_ASSERT(out_pos_ == 0);
1253 BOOST_ASSERT(out_end_ == 0);
1254 BOOST_ASSERT(out_ == list_.end());
1255 return;
1256 }
1257
1258 auto const& front = list_.front();
1259
1260 BOOST_ASSERT(in_pos_ < front.size());
1261
1262 if(out_ == list_.end())
1263 {
1264 BOOST_ASSERT(out_pos_ == 0);
1265 BOOST_ASSERT(out_end_ == 0);
1266 }
1267 else
1268 {
1269 auto const& out = *out_;
1270 auto const& back = list_.back();
1271
1272 BOOST_ASSERT(out_end_ <= back.size());
1273 BOOST_ASSERT(out_pos_ < out.size());
1274 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
1275 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
1276 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
1277 }
1278 #endif
1279 }
1280
1281 } // beast
1282 } // boost
1283
1284 #endif
1285