Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:27

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
0008 //
0009 
0010 #ifndef BOOST_BEAST_IMPL_MULTI_BUFFER_HPP
0011 #define BOOST_BEAST_IMPL_MULTI_BUFFER_HPP
0012 
0013 #include <boost/beast/core/buffer_traits.hpp>
0014 #include <boost/config/workaround.hpp>
0015 #include <boost/core/exchange.hpp>
0016 #include <boost/assert.hpp>
0017 #include <boost/throw_exception.hpp>
0018 #include <algorithm>
0019 #include <exception>
0020 #include <iterator>
0021 #include <sstream>
0022 #include <string>
0023 #include <type_traits>
0024 #include <utility>
0025 
0026 namespace boost {
0027 namespace beast {
0028 
0029 /*  These diagrams illustrate the layout and state variables.
0030 
0031 1   Input and output contained entirely in one element:
0032 
0033     0                           out_
0034     |<------+-----------+--------------------------------+----->|
0035           in_pos_    out_pos_                         out_end_
0036 
0037 
0038 2   Output contained in first and second elements:
0039 
0040                  out_
0041     |<------+-----------+------>|   |<-------------------+----->|
0042           in_pos_    out_pos_                         out_end_
0043 
0044 
0045 3   Output contained in the second element:
0046 
0047                                                   out_
0048     |<------+------------------>|   |<----+--------------+----->|
0049           in_pos_                      out_pos_       out_end_
0050 
0051 
0052 4   Output contained in second and third elements:
0053 
0054                                  out_
0055     |<------+------->|   |<-------+------>|   |<---------+----->|
0056           in_pos_               out_pos_              out_end_
0057 
0058 
0059 5   Input sequence is empty:
0060 
0061                  out_
0062     |<------+------------------>|   |<-------------------+----->|
0063          out_pos_                                     out_end_
0064           in_pos_
0065 
0066 6   Output sequence is empty:
0067 
0068                                                     out_
0069     |<------+------------------>|   |<------+------------------>|
0070           in_pos_                        out_pos_
0071                                          out_end_
0072 
0073 
0074 7   The end of output can point to the end of an element.
0075     But out_pos_ should never point to the end:
0076 
0077                                                     out_
0078     |<------+------------------>|   |<------+------------------>|
0079           in_pos_                        out_pos_            out_end_
0080 
0081 
0082 8   When the input sequence entirely fills the last element and
0083     the output sequence is empty, out_ will point to the end of
0084     the list of buffers, and out_pos_ and out_end_ will be 0:
0085 
0086 
0087     |<------+------------------>|   out_     == list_.end()
0088           in_pos_                   out_pos_ == 0
0089                                     out_end_ == 0
0090 */
0091 
0092 //------------------------------------------------------------------------------
0093 
0094 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
0095 # pragma warning (push)
0096 # pragma warning (disable: 4521) // multiple copy constructors specified
0097 # pragma warning (disable: 4522) // multiple assignment operators specified
0098 #endif
0099 
0100 template<class Allocator>
0101 template<bool isMutable>
0102 class basic_multi_buffer<Allocator>::subrange
0103 {
0104     basic_multi_buffer const* b_;
0105     const_iter begin_;
0106     const_iter end_;
0107     size_type begin_pos_;   // offset in begin_
0108     size_type last_pos_;    // offset in std::prev(end_)
0109 
0110     friend class basic_multi_buffer;
0111 
0112     subrange(
0113         basic_multi_buffer const& b,
0114         size_type pos,
0115         size_type n) noexcept
0116         : b_(&b)
0117     {
0118         auto const set_empty = [&]
0119         {
0120             begin_ = b_->list_.end();
0121             end_ = b_->list_.end();
0122             begin_pos_ = 0;
0123             last_pos_ = 0;
0124         };
0125 
0126         // VFALCO Handle this trivial case of
0127         // pos larger than total size, otherwise
0128         // the addition to pos can overflow.
0129         //if(pos >= b_->in_size_)
0130         // skip unused prefix
0131         pos = pos + b_->in_pos_;
0132 
0133         // iterate the buffers
0134         auto it = b_->list_.begin();
0135 
0136         // is the list empty?
0137         if(it == b_->list_.end())
0138         {
0139             set_empty();
0140             return;
0141         }
0142         
0143         // is the requested size zero?
0144         if(n == 0)
0145         {
0146             set_empty();
0147             return;
0148         }
0149 
0150 
0151         // get last buffer and its size
0152         auto const last =
0153             std::prev(b_->list_.end());
0154         auto const last_end =
0155             [&]
0156             {
0157                 if(b_->out_end_ == 0)
0158                     return last->size();
0159                 return b_->out_end_;
0160             }();
0161 
0162         // only one buffer in list?
0163         if(it == last)
0164         {
0165             if(pos >= last_end)
0166             {
0167                 set_empty();
0168                 return;
0169             }
0170 
0171             begin_ = it;
0172             begin_pos_ = pos;
0173             end_ = std::next(it);
0174             if(n > last_end - pos)
0175                 last_pos_ = last_end;
0176             else
0177                 last_pos_ = pos + n;
0178             return;
0179         }
0180 
0181         for(;;)
0182         {
0183             // is pos in this buffer?
0184             if(pos < it->size())
0185             {
0186                 begin_ = it;
0187                 begin_pos_ = pos;
0188 
0189                 // does this buffer satisfy n?
0190                 auto const avail =
0191                     it->size() - pos;
0192                 if(n <= avail)
0193                 {
0194                     end_ = ++it;
0195                     last_pos_ = pos + n;
0196                     return;
0197                 }
0198 
0199                 n -= avail;
0200                 ++it;
0201                 break;
0202             }
0203 
0204             pos -= it->size();
0205             ++it;
0206 
0207             // did we reach the last buffer?
0208             if(it == last)
0209             {
0210                 // is pos past the end?
0211                 if(pos >= last_end)
0212                 {
0213                     set_empty();
0214                     return;
0215                 }
0216 
0217                 // satisfy the request
0218                 begin_ = it;
0219                 begin_pos_ = pos;
0220                 end_ = std::next(it);
0221                 if(n < last_end - pos)
0222                     last_pos_ = pos + n;
0223                 else
0224                     last_pos_ = last_end;
0225                 return;
0226             }
0227         }
0228 
0229         // find pos+n
0230         for(;;)
0231         {
0232             if(it == last)
0233             {
0234                 end_ = ++it;
0235                 if(n >= last_end)
0236                     last_pos_ = last_end;
0237                 else
0238                     last_pos_ = n;
0239                 return;
0240             }
0241             if(n <= it->size())
0242             {
0243                 end_ = ++it;
0244                 last_pos_ = n;
0245                 return;
0246             }
0247             
0248             n -= it->size();
0249             ++it;
0250         }
0251     }
0252 
0253 public:
0254     using value_type = typename
0255         std::conditional<
0256             isMutable,
0257             net::mutable_buffer,
0258             net::const_buffer>::type;
0259 
0260     class const_iterator;
0261 
0262     subrange() = delete;
0263 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
0264     subrange(subrange const& other)
0265         : b_(other.b_)
0266         , begin_(other.begin_)
0267         , end_(other.end_)
0268         , begin_pos_(other.begin_pos_)
0269         , last_pos_(other.last_pos_)
0270     {
0271     }
0272 
0273     subrange& operator=(subrange const& other)
0274     {
0275         b_ = other.b_;
0276         begin_ = other.begin_;
0277         end_ = other.end_;
0278         begin_pos_ = other.begin_pos_;
0279         last_pos_ = other.last_pos_;
0280         return *this;
0281     }
0282 #else
0283     subrange(subrange const&) = default;
0284     subrange& operator=(subrange const&) = default;
0285 #endif
0286 
0287     template<
0288         bool isMutable_ = isMutable,
0289         class = typename std::enable_if<! isMutable_>::type>
0290     subrange(
0291         subrange<true> const& other) noexcept
0292         : b_(other.b_)
0293         , begin_(other.begin_)
0294         , end_(other.end_)
0295         , begin_pos_(other.begin_pos_)
0296         , last_pos_(other.last_pos_)
0297    {
0298     }
0299 
0300     template<
0301         bool isMutable_ = isMutable,
0302         class = typename std::enable_if<! isMutable_>::type>
0303     subrange& operator=(
0304         subrange<true> const& other) noexcept
0305     {
0306         b_ = other.b_;
0307         begin_ = other.begin_;
0308         end_ = other.end_;
0309         begin_pos_ = other.begin_pos_;
0310         last_pos_ = other.last_pos_;
0311         return *this;
0312     }
0313 
0314     const_iterator begin() const noexcept;
0315     const_iterator end() const noexcept;
0316 
0317     std::size_t
0318     buffer_bytes() const noexcept
0319     {
0320         return b_->size();
0321     }
0322 };
0323 
0324 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
0325 # pragma warning (pop)
0326 #endif
0327 
0328 //------------------------------------------------------------------------------
0329 
0330 template<class Allocator>
0331 template<bool isMutable>
0332 class
0333     basic_multi_buffer<Allocator>::
0334     subrange<isMutable>::
0335     const_iterator
0336 {
0337     friend class subrange;
0338 
0339     subrange const* sr_ = nullptr;
0340     typename list_type::const_iterator it_;
0341 
0342     const_iterator(
0343         subrange const& sr, typename
0344         list_type::const_iterator const& it) noexcept
0345         : sr_(&sr)
0346         , it_(it)
0347     {
0348     }
0349 
0350 public:
0351     using value_type =
0352         typename subrange::value_type;
0353     using pointer = value_type const*;
0354     using reference = value_type;
0355     using difference_type = std::ptrdiff_t;
0356     using iterator_category =
0357         std::bidirectional_iterator_tag;
0358 
0359     const_iterator() = default;
0360     const_iterator(
0361         const_iterator const& other) = default;
0362     const_iterator& operator=(
0363         const_iterator const& other) = default;
0364 
0365     bool
0366     operator==(
0367         const_iterator const& other) const noexcept
0368     {
0369         return sr_ == other.sr_ && it_ == other.it_;
0370     }
0371 
0372     bool
0373     operator!=(
0374         const_iterator const& other) const noexcept
0375     {
0376         return !(*this == other);
0377     }
0378 
0379     reference
0380     operator*() const noexcept
0381     {
0382         value_type result;
0383         BOOST_ASSERT(sr_->last_pos_ != 0);
0384         if(it_ == std::prev(sr_->end_))
0385             result = {
0386                 it_->data(), sr_->last_pos_ };
0387         else
0388             result = {
0389                 it_->data(), it_->size() };
0390         if(it_ == sr_->begin_)
0391             result += sr_->begin_pos_;
0392         return result;
0393     }
0394 
0395     pointer
0396     operator->() const = delete;
0397 
0398     const_iterator&
0399     operator++() noexcept
0400     {
0401         ++it_;
0402         return *this;
0403     }
0404 
0405     const_iterator
0406     operator++(int) noexcept
0407     {
0408         auto temp = *this;
0409         ++(*this);
0410         return temp;
0411     }
0412 
0413     const_iterator&
0414     operator--() noexcept
0415     {
0416         --it_;
0417         return *this;
0418     }
0419 
0420     const_iterator
0421     operator--(int) noexcept
0422     {
0423         auto temp = *this;
0424         --(*this);
0425         return temp;
0426     }
0427 };
0428 
0429 //------------------------------------------------------------------------------
0430 
0431 template<class Allocator>
0432 template<bool isMutable>
0433 auto
0434 basic_multi_buffer<Allocator>::
0435 subrange<isMutable>::
0436 begin() const noexcept ->
0437     const_iterator
0438 {
0439     return const_iterator(
0440         *this, begin_);
0441 }
0442 
0443 template<class Allocator>
0444 template<bool isMutable>
0445 auto
0446 basic_multi_buffer<Allocator>::
0447 subrange<isMutable>::
0448 end() const noexcept ->
0449     const_iterator
0450 {
0451     return const_iterator(
0452         *this, end_);
0453 }
0454 
0455 //------------------------------------------------------------------------------
0456 
0457 template<class Allocator>
0458 basic_multi_buffer<Allocator>::
0459 ~basic_multi_buffer()
0460 {
0461     destroy(list_);
0462 }
0463 
0464 template<class Allocator>
0465 basic_multi_buffer<Allocator>::
0466 basic_multi_buffer() noexcept(default_nothrow)
0467     : max_(alloc_traits::max_size(this->get()))
0468     , out_(list_.end())
0469 {
0470 }
0471 
0472 template<class Allocator>
0473 basic_multi_buffer<Allocator>::
0474 basic_multi_buffer(
0475     std::size_t limit) noexcept(default_nothrow)
0476     : max_(limit)
0477     , out_(list_.end())
0478 {
0479 }
0480 
0481 template<class Allocator>
0482 basic_multi_buffer<Allocator>::
0483 basic_multi_buffer(
0484     Allocator const& alloc) noexcept
0485     : boost::empty_value<Allocator>(
0486         boost::empty_init_t(), alloc)
0487     , max_(alloc_traits::max_size(this->get()))
0488     , out_(list_.end())
0489 {
0490 }
0491 
0492 template<class Allocator>
0493 basic_multi_buffer<Allocator>::
0494 basic_multi_buffer(
0495     std::size_t limit,
0496     Allocator const& alloc) noexcept
0497     : boost::empty_value<Allocator>(
0498         boost::empty_init_t(), alloc)
0499     , max_(limit)
0500     , out_(list_.end())
0501 {
0502 }
0503 
0504 template<class Allocator>
0505 basic_multi_buffer<Allocator>::
0506 basic_multi_buffer(
0507     basic_multi_buffer&& other) noexcept
0508     : boost::empty_value<Allocator>(
0509         boost::empty_init_t(), std::move(other.get()))
0510     , max_(other.max_)
0511     , in_size_(boost::exchange(other.in_size_, 0))
0512     , in_pos_(boost::exchange(other.in_pos_, 0))
0513     , out_pos_(boost::exchange(other.out_pos_, 0))
0514     , out_end_(boost::exchange(other.out_end_, 0))
0515 {
0516     auto const at_end =
0517         other.out_ == other.list_.end();
0518     list_ = std::move(other.list_);
0519     out_ = at_end ? list_.end() : other.out_;
0520     other.out_ = other.list_.end();
0521 }
0522 
0523 template<class Allocator>
0524 basic_multi_buffer<Allocator>::
0525 basic_multi_buffer(
0526     basic_multi_buffer&& other,
0527     Allocator const& alloc)
0528     : boost::empty_value<Allocator>(
0529         boost::empty_init_t(), alloc)
0530     , max_(other.max_)
0531 {
0532     if(this->get() != other.get())
0533     {
0534         out_ = list_.end();
0535         copy_from(other);
0536         return;
0537     }
0538 
0539     auto const at_end =
0540         other.out_ == other.list_.end();
0541     list_ = std::move(other.list_);
0542     out_ = at_end ? list_.end() : other.out_;
0543     in_size_ = other.in_size_;
0544     in_pos_ = other.in_pos_;
0545     out_pos_ = other.out_pos_;
0546     out_end_ = other.out_end_;
0547     other.in_size_ = 0;
0548     other.out_ = other.list_.end();
0549     other.in_pos_ = 0;
0550     other.out_pos_ = 0;
0551     other.out_end_ = 0;
0552 }
0553 
0554 template<class Allocator>
0555 basic_multi_buffer<Allocator>::
0556 basic_multi_buffer(
0557     basic_multi_buffer const& other)
0558     : boost::empty_value<Allocator>(
0559         boost::empty_init_t(), alloc_traits::
0560             select_on_container_copy_construction(
0561                 other.get()))
0562     , max_(other.max_)
0563     , out_(list_.end())
0564 {
0565     copy_from(other);
0566 }
0567 
0568 template<class Allocator>
0569 basic_multi_buffer<Allocator>::
0570 basic_multi_buffer(
0571     basic_multi_buffer const& other,
0572     Allocator const& alloc)
0573     : boost::empty_value<Allocator>(
0574         boost::empty_init_t(), alloc)
0575     , max_(other.max_)
0576     , out_(list_.end())
0577 {
0578     copy_from(other);
0579 }
0580 
0581 template<class Allocator>
0582 template<class OtherAlloc>
0583 basic_multi_buffer<Allocator>::
0584 basic_multi_buffer(
0585         basic_multi_buffer<OtherAlloc> const& other)
0586     : out_(list_.end())
0587 {
0588     copy_from(other);
0589 }
0590 
0591 template<class Allocator>
0592 template<class OtherAlloc>
0593 basic_multi_buffer<Allocator>::
0594 basic_multi_buffer(
0595     basic_multi_buffer<OtherAlloc> const& other,
0596         allocator_type const& alloc)
0597     : boost::empty_value<Allocator>(
0598         boost::empty_init_t(), alloc)
0599     , max_(other.max_)
0600     , out_(list_.end())
0601 {
0602     copy_from(other);
0603 }
0604 
0605 template<class Allocator>
0606 auto
0607 basic_multi_buffer<Allocator>::
0608 operator=(basic_multi_buffer&& other) ->
0609     basic_multi_buffer&
0610 {
0611     if(this == &other)
0612         return *this;
0613     clear();
0614     max_ = other.max_;
0615     move_assign(other, pocma{});
0616     return *this;
0617 }
0618 
0619 template<class Allocator>
0620 auto
0621 basic_multi_buffer<Allocator>::
0622 operator=(basic_multi_buffer const& other) ->
0623 basic_multi_buffer&
0624 {
0625     if(this == &other)
0626         return *this;
0627     copy_assign(other, pocca{});
0628     return *this;
0629 }
0630 
0631 template<class Allocator>
0632 template<class OtherAlloc>
0633 auto
0634 basic_multi_buffer<Allocator>::
0635 operator=(
0636     basic_multi_buffer<OtherAlloc> const& other) ->
0637         basic_multi_buffer&
0638 {
0639     copy_from(other);
0640     return *this;
0641 }
0642 
0643 //------------------------------------------------------------------------------
0644 
0645 template<class Allocator>
0646 std::size_t
0647 basic_multi_buffer<Allocator>::
0648 capacity() const noexcept
0649 {
0650     auto pos = out_;
0651     if(pos == list_.end())
0652         return in_size_;
0653     auto n = pos->size() - out_pos_;
0654     while(++pos != list_.end())
0655         n += pos->size();
0656     return in_size_ + n;
0657 }
0658 
0659 template<class Allocator>
0660 auto
0661 basic_multi_buffer<Allocator>::
0662 data() const noexcept ->
0663     const_buffers_type
0664 {
0665     return const_buffers_type(
0666         *this, 0, in_size_);
0667 }
0668 
0669 template<class Allocator>
0670 auto
0671 basic_multi_buffer<Allocator>::
0672 data() noexcept ->
0673     mutable_buffers_type
0674 {
0675     return mutable_buffers_type(
0676         *this, 0, in_size_);
0677 }
0678 
0679 template<class Allocator>
0680 void
0681 basic_multi_buffer<Allocator>::
0682 reserve(std::size_t n)
0683 {
0684     // VFALCO The amount needs to be adjusted for
0685     //        the sizeof(element) plus padding
0686     if(n > alloc_traits::max_size(this->get()))
0687         BOOST_THROW_EXCEPTION(std::length_error(
0688         "A basic_multi_buffer exceeded the allocator's maximum size"));
0689     std::size_t total = in_size_;
0690     if(n <= total)
0691         return;
0692     if(out_ != list_.end())
0693     {
0694         total += out_->size() - out_pos_;
0695         if(n <= total)
0696             return;
0697         for(auto it = out_;;)
0698         {
0699             if(++it == list_.end())
0700                 break;
0701             total += it->size();
0702             if(n <= total)
0703                 return;
0704         }
0705     }
0706     BOOST_ASSERT(n > total);
0707     (void)prepare(n - size());
0708 }
0709 
0710 template<class Allocator>
0711 void
0712 basic_multi_buffer<Allocator>::
0713 shrink_to_fit()
0714 {
0715     // empty list
0716     if(list_.empty())
0717         return;
0718 
0719     // zero readable bytes
0720     if(in_size_ == 0)
0721     {
0722         destroy(list_);
0723         list_.clear();
0724         out_ = list_.end();
0725         in_size_ = 0;
0726         in_pos_ = 0;
0727         out_pos_ = 0;
0728         out_end_ = 0;
0729     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0730         debug_check();
0731     #endif
0732         return;
0733     }
0734 
0735     // one or more unused output buffers
0736     if(out_ != list_.end())
0737     {
0738         if(out_ != list_.iterator_to(list_.back()))
0739         {
0740             // unused list
0741             list_type extra;
0742             extra.splice(
0743                 extra.end(),
0744                 list_,
0745                 std::next(out_),
0746                 list_.end());
0747             destroy(extra);
0748         #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0749             debug_check();
0750         #endif
0751         }
0752 
0753         // unused out_
0754         BOOST_ASSERT(out_ ==
0755             list_.iterator_to(list_.back()));
0756         if(out_pos_ == 0)
0757         {
0758             BOOST_ASSERT(out_ != list_.begin());
0759             auto& e = *out_;
0760             list_.erase(out_);
0761             out_ = list_.end();
0762             destroy(e);
0763             out_end_ = 0;
0764         #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0765             debug_check();
0766         #endif
0767         }
0768     }
0769 
0770     auto const replace =
0771         [&](iter pos, element& e)
0772         {
0773             auto it =
0774                 list_.insert(pos, e);
0775             auto& e0 = *pos;
0776             list_.erase(pos);
0777             destroy(e0);
0778             return it;
0779         };
0780 
0781     // partial last buffer
0782     if(out_ != list_.begin() && out_ != list_.end())
0783     {
0784         BOOST_ASSERT(out_ ==
0785             list_.iterator_to(list_.back()));
0786         BOOST_ASSERT(out_pos_ != 0);
0787         auto& e = alloc(out_pos_);
0788         std::memcpy(
0789             e.data(),
0790             out_->data(),
0791             out_pos_);
0792         replace(out_, e);
0793         out_ = list_.end();
0794         out_pos_ = 0;
0795         out_end_ = 0;
0796     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0797         debug_check();
0798     #endif
0799     }
0800 
0801     // partial first buffer
0802     if(in_pos_ != 0)
0803     {
0804         if(out_ != list_.begin())
0805         {
0806             auto const n =
0807                 list_.front().size() - in_pos_;
0808             auto& e = alloc(n);
0809             std::memcpy(
0810                 e.data(),
0811                 list_.front().data() + in_pos_,
0812                 n);
0813             replace(list_.begin(), e);
0814             in_pos_ = 0;
0815         }
0816         else
0817         {
0818             BOOST_ASSERT(out_ ==
0819                 list_.iterator_to(list_.back()));
0820             BOOST_ASSERT(out_pos_ > in_pos_);
0821             auto const n = out_pos_ - in_pos_;
0822             auto& e = alloc(n);
0823             std::memcpy(
0824                 e.data(),
0825                 list_.front().data() + in_pos_,
0826                 n);
0827             replace(list_.begin(), e);
0828             in_pos_ = 0;
0829             out_ = list_.end();
0830         }
0831     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0832         debug_check();
0833     #endif
0834     }
0835 }
0836 
0837 template<class Allocator>
0838 void
0839 basic_multi_buffer<Allocator>::
0840 clear() noexcept
0841 {
0842     out_ = list_.begin();
0843     in_size_ = 0;
0844     in_pos_ = 0;
0845     out_pos_ = 0;
0846     out_end_ = 0;
0847 }
0848 
0849 template<class Allocator>
0850 auto
0851 basic_multi_buffer<Allocator>::
0852 prepare(size_type n) ->
0853     mutable_buffers_type
0854 {
0855     auto const n0 = n;
0856     if(in_size_ > max_ || n > (max_ - in_size_))
0857         BOOST_THROW_EXCEPTION(std::length_error{
0858             "basic_multi_buffer too long"});
0859     list_type reuse;
0860     std::size_t total = in_size_;
0861     // put all empty buffers on reuse list
0862     if(out_ != list_.end())
0863     {
0864         total += out_->size() - out_pos_;
0865         if(out_ != list_.iterator_to(list_.back()))
0866         {
0867             out_end_ = out_->size();
0868             reuse.splice(reuse.end(), list_,
0869                 std::next(out_), list_.end());
0870         #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0871             debug_check();
0872         #endif
0873         }
0874         auto const avail = out_->size() - out_pos_;
0875         if(n > avail)
0876         {
0877             out_end_ = out_->size();
0878             n -= avail;
0879         }
0880         else
0881         {
0882             out_end_ = out_pos_ + n;
0883             n = 0;
0884         }
0885     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0886         debug_check();
0887     #endif
0888     }
0889     // get space from reuse buffers
0890     while(n > 0 && ! reuse.empty())
0891     {
0892         auto& e = reuse.front();
0893         reuse.erase(reuse.iterator_to(e));
0894         list_.push_back(e);
0895         total += e.size();
0896         if(n > e.size())
0897         {
0898             out_end_ = e.size();
0899             n -= e.size();
0900         }
0901         else
0902         {
0903             out_end_ = n;
0904             n = 0;
0905         }
0906     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0907         debug_check();
0908     #endif
0909     }
0910     BOOST_ASSERT(total <= max_);
0911     if(! reuse.empty() || n > 0)
0912     {
0913         destroy(reuse);
0914         if(n > 0)
0915         {
0916             std::size_t const growth_factor = 2;
0917             std::size_t altn = in_size_ * growth_factor;
0918         // Overflow detection:
0919             if(in_size_ > altn)
0920                 altn = (std::numeric_limits<std::size_t>::max)();
0921             else
0922                 altn = (std::max<std::size_t>)(512, altn);
0923             auto const size =
0924                 (std::min<std::size_t>)(
0925                     max_ - total,
0926                     (std::max<std::size_t>)(n, altn));
0927             auto& e = alloc(size);
0928             list_.push_back(e);
0929             if(out_ == list_.end())
0930                 out_ = list_.iterator_to(e);
0931             out_end_ = n;
0932         #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0933             debug_check();
0934         #endif
0935         }
0936     }
0937     auto const result =
0938         mutable_buffers_type(
0939             *this, in_size_, n0);
0940     BOOST_ASSERT(
0941         net::buffer_size(result) == n0);
0942     return result;
0943 }
0944 
0945 template<class Allocator>
0946 void
0947 basic_multi_buffer<Allocator>::
0948 commit(size_type n) noexcept
0949 {
0950     if(list_.empty())
0951         return;
0952     if(out_ == list_.end())
0953         return;
0954     auto const back =
0955         list_.iterator_to(list_.back());
0956     while(out_ != back)
0957     {
0958         auto const avail =
0959             out_->size() - out_pos_;
0960         if(n < avail)
0961         {
0962             out_pos_ += n;
0963             in_size_ += n;
0964         #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0965             debug_check();
0966         #endif
0967             return;
0968         }
0969         ++out_;
0970         n -= avail;
0971         out_pos_ = 0;
0972         in_size_ += avail;
0973     #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0974         debug_check();
0975     #endif
0976     }
0977 
0978     n = (std::min)(n, out_end_ - out_pos_);
0979     out_pos_ += n;
0980     in_size_ += n;
0981     if(out_pos_ == out_->size())
0982     {
0983         ++out_;
0984         out_pos_ = 0;
0985         out_end_ = 0;
0986     }
0987 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
0988     debug_check();
0989 #endif
0990 }
0991 
0992 template<class Allocator>
0993 void
0994 basic_multi_buffer<Allocator>::
0995 consume(size_type n) noexcept
0996 {
0997     if(list_.empty())
0998         return;
0999     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 
1057 template<class Allocator>
1058 template<class OtherAlloc>
1059 void
1060 basic_multi_buffer<Allocator>::
1061 copy_from(basic_multi_buffer<OtherAlloc> const& other)
1062 {
1063     clear();
1064     max_ = other.max_;
1065     if(other.size() == 0)
1066         return;
1067     commit(net::buffer_copy(
1068         prepare(other.size()), other.data()));
1069 }
1070 
1071 template<class Allocator>
1072 void
1073 basic_multi_buffer<Allocator>::
1074 move_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 
1095 template<class Allocator>
1096 void
1097 basic_multi_buffer<Allocator>::
1098 move_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 
1110 template<class Allocator>
1111 void
1112 basic_multi_buffer<Allocator>::
1113 copy_assign(
1114     basic_multi_buffer const& other, std::false_type)
1115 {
1116     copy_from(other);
1117 }
1118 
1119 template<class Allocator>
1120 void
1121 basic_multi_buffer<Allocator>::
1122 copy_assign(
1123     basic_multi_buffer const& other, std::true_type)
1124 {
1125     clear();
1126     this->get() = other.get();
1127     copy_from(other);
1128 }
1129 
1130 template<class Allocator>
1131 void
1132 basic_multi_buffer<Allocator>::
1133 swap(basic_multi_buffer& other) noexcept
1134 {
1135     swap(other, typename
1136         alloc_traits::propagate_on_container_swap{});
1137 }
1138 
1139 template<class Allocator>
1140 void
1141 basic_multi_buffer<Allocator>::
1142 swap(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 
1162 template<class Allocator>
1163 void
1164 basic_multi_buffer<Allocator>::
1165 swap(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 
1185 template<class Allocator>
1186 void
1187 swap(
1188     basic_multi_buffer<Allocator>& lhs,
1189     basic_multi_buffer<Allocator>& rhs) noexcept
1190 {
1191     lhs.swap(rhs);
1192 }
1193 
1194 template<class Allocator>
1195 void
1196 basic_multi_buffer<Allocator>::
1197 destroy(list_type& list) noexcept
1198 {
1199     for(auto it = list.begin();
1200             it != list.end();)
1201         destroy(*it++);
1202 }
1203 
1204 template<class Allocator>
1205 void
1206 basic_multi_buffer<Allocator>::
1207 destroy(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 
1219 template<class Allocator>
1220 auto
1221 basic_multi_buffer<Allocator>::
1222 alloc(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 
1235 template<class Allocator>
1236 void
1237 basic_multi_buffer<Allocator>::
1238 debug_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