• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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