File indexing completed on 2025-01-18 09:29:27
0001
0002
0003
0004
0005
0006
0007
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
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
0095 # pragma warning (push)
0096 # pragma warning (disable: 4521)
0097 # pragma warning (disable: 4522)
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_;
0108 size_type last_pos_;
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
0127
0128
0129
0130
0131 pos = pos + b_->in_pos_;
0132
0133
0134 auto it = b_->list_.begin();
0135
0136
0137 if(it == b_->list_.end())
0138 {
0139 set_empty();
0140 return;
0141 }
0142
0143
0144 if(n == 0)
0145 {
0146 set_empty();
0147 return;
0148 }
0149
0150
0151
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
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
0184 if(pos < it->size())
0185 {
0186 begin_ = it;
0187 begin_pos_ = pos;
0188
0189
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
0208 if(it == last)
0209 {
0210
0211 if(pos >= last_end)
0212 {
0213 set_empty();
0214 return;
0215 }
0216
0217
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
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
0685
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
0716 if(list_.empty())
0717 return;
0718
0719
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
0736 if(out_ != list_.end())
0737 {
0738 if(out_ != list_.iterator_to(list_.back()))
0739 {
0740
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
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
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
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
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
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
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
1043
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 }
1276 }
1277
1278 #endif