2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_IMPL_MULTI_BUFFER_IPP
11 #define BOOST_BEAST_IMPL_MULTI_BUFFER_IPP
13 #include <boost/beast/core/detail/type_traits.hpp>
14 #include <boost/assert.hpp>
15 #include <boost/throw_exception.hpp>
25 /* These diagrams illustrate the layout and state variables.
27 1 Input and output contained entirely in one element:
30 |<-------------+------------------------------------------->|
31 in_pos_ out_pos_ out_end_
34 2 Output contained in first and second elements:
37 |<------+----------+------->| |<----------+-------------->|
38 in_pos_ out_pos_ out_end_
41 3 Output contained in the second element:
44 |<------------+------------>| |<----+-------------------->|
45 in_pos_ out_pos_ out_end_
48 4 Output contained in second and third elements:
51 |<-----+-------->| |<-------+------>| |<--------------->|
52 in_pos_ out_pos_ out_end_
55 5 Input sequence is empty:
58 |<------+------------------>| |<-----------+------------->|
63 6 Output sequence is empty:
66 |<------+------------------>| |<------+------------------>|
71 7 The end of output can point to the end of an element.
72 But out_pos_ should never point to the end:
75 |<------+------------------>| |<------+------------------>|
76 in_pos_ out_pos_ out_end_
79 8 When the input sequence entirely fills the last element and
80 the output sequence is empty, out_ will point to the end of
81 the list of buffers, and out_pos_ and out_end_ will be 0:
84 |<------+------------------>| out_ == list_.end()
89 template<class Allocator>
90 class basic_multi_buffer<Allocator>::element
91 : public boost::intrusive::list_base_hook<
92 boost::intrusive::link_mode<
93 boost::intrusive::normal_link>>
96 typename detail::allocator_traits<Allocator>::size_type;
98 size_type const size_;
101 element(element const&) = delete;
102 element& operator=(element const&) = delete;
119 return const_cast<char*>(
120 reinterpret_cast<char const*>(this+1));
124 template<class Allocator>
125 class basic_multi_buffer<Allocator>::const_buffers_type
127 basic_multi_buffer const* b_;
129 friend class basic_multi_buffer;
132 const_buffers_type(basic_multi_buffer const& b);
135 using value_type = boost::asio::mutable_buffer;
137 class const_iterator;
139 const_buffers_type() = delete;
140 const_buffers_type(const_buffers_type const&) = default;
141 const_buffers_type& operator=(const_buffers_type const&) = default;
151 buffer_size(const_buffers_type const& buffers)
153 return buffers.b_->size();
157 template<class Allocator>
158 class basic_multi_buffer<Allocator>::mutable_buffers_type
160 basic_multi_buffer const* b_;
162 friend class basic_multi_buffer;
165 mutable_buffers_type(basic_multi_buffer const& b);
168 using value_type = mutable_buffer;
170 class const_iterator;
172 mutable_buffers_type() = delete;
173 mutable_buffers_type(mutable_buffers_type const&) = default;
174 mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
183 //------------------------------------------------------------------------------
185 template<class Allocator>
186 class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator
188 basic_multi_buffer const* b_ = nullptr;
189 typename list_type::const_iterator it_;
193 typename const_buffers_type::value_type;
194 using pointer = value_type const*;
195 using reference = value_type;
196 using difference_type = std::ptrdiff_t;
197 using iterator_category =
198 std::bidirectional_iterator_tag;
200 const_iterator() = default;
201 const_iterator(const_iterator&& other) = default;
202 const_iterator(const_iterator const& other) = default;
203 const_iterator& operator=(const_iterator&& other) = default;
204 const_iterator& operator=(const_iterator const& other) = default;
206 const_iterator(basic_multi_buffer const& b,
207 typename list_type::const_iterator const& it)
214 operator==(const_iterator const& other) const
216 return b_ == other.b_ && it_ == other.it_;
220 operator!=(const_iterator const& other) const
222 return !(*this == other);
228 auto const& e = *it_;
229 return value_type{e.data(),
230 (b_->out_ == b_->list_.end() ||
231 &e != &*b_->out_) ? e.size() : b_->out_pos_} +
232 (&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
236 operator->() const = delete;
269 template<class Allocator>
270 basic_multi_buffer<Allocator>::
273 basic_multi_buffer const& b)
278 template<class Allocator>
280 basic_multi_buffer<Allocator>::
285 return const_iterator{*b_, b_->list_.begin()};
288 template<class Allocator>
290 basic_multi_buffer<Allocator>::
295 return const_iterator{*b_, b_->out_ ==
296 b_->list_.end() ? b_->list_.end() :
297 std::next(b_->out_)};
300 //------------------------------------------------------------------------------
302 template<class Allocator>
303 class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
305 basic_multi_buffer const* b_ = nullptr;
306 typename list_type::const_iterator it_;
310 typename mutable_buffers_type::value_type;
311 using pointer = value_type const*;
312 using reference = value_type;
313 using difference_type = std::ptrdiff_t;
314 using iterator_category =
315 std::bidirectional_iterator_tag;
317 const_iterator() = default;
318 const_iterator(const_iterator&& other) = default;
319 const_iterator(const_iterator const& other) = default;
320 const_iterator& operator=(const_iterator&& other) = default;
321 const_iterator& operator=(const_iterator const& other) = default;
323 const_iterator(basic_multi_buffer const& b,
324 typename list_type::const_iterator const& it)
331 operator==(const_iterator const& other) const
333 return b_ == other.b_ && it_ == other.it_;
337 operator!=(const_iterator const& other) const
339 return !(*this == other);
345 auto const& e = *it_;
346 return value_type{e.data(),
347 &e == &*std::prev(b_->list_.end()) ?
348 b_->out_end_ : e.size()} +
349 (&e == &*b_->out_ ? b_->out_pos_ : 0);
353 operator->() const = delete;
386 template<class Allocator>
387 basic_multi_buffer<Allocator>::
388 mutable_buffers_type::
389 mutable_buffers_type(
390 basic_multi_buffer const& b)
395 template<class Allocator>
397 basic_multi_buffer<Allocator>::
398 mutable_buffers_type::
402 return const_iterator{*b_, b_->out_};
405 template<class Allocator>
407 basic_multi_buffer<Allocator>::
408 mutable_buffers_type::
412 return const_iterator{*b_, b_->list_.end()};
415 //------------------------------------------------------------------------------
417 template<class Allocator>
418 basic_multi_buffer<Allocator>::
419 ~basic_multi_buffer()
424 template<class Allocator>
425 basic_multi_buffer<Allocator>::
431 template<class Allocator>
432 basic_multi_buffer<Allocator>::
433 basic_multi_buffer(std::size_t limit)
439 template<class Allocator>
440 basic_multi_buffer<Allocator>::
441 basic_multi_buffer(Allocator const& alloc)
442 : detail::empty_base_optimization<
443 base_alloc_type>(alloc)
448 template<class Allocator>
449 basic_multi_buffer<Allocator>::
450 basic_multi_buffer(std::size_t limit,
451 Allocator const& alloc)
452 : detail::empty_base_optimization<
453 base_alloc_type>(alloc)
459 template<class Allocator>
460 basic_multi_buffer<Allocator>::
461 basic_multi_buffer(basic_multi_buffer&& other)
462 : detail::empty_base_optimization<
463 base_alloc_type>(std::move(other.member()))
465 , in_size_(other.in_size_)
466 , in_pos_(other.in_pos_)
467 , out_pos_(other.out_pos_)
468 , out_end_(other.out_end_)
471 other.out_ == other.list_.end();
472 list_ = std::move(other.list_);
473 out_ = at_end ? list_.end() : other.out_;
475 other.out_ = other.list_.end();
481 template<class Allocator>
482 basic_multi_buffer<Allocator>::
483 basic_multi_buffer(basic_multi_buffer&& other,
484 Allocator const& alloc)
485 : detail::empty_base_optimization<
486 base_alloc_type>(alloc)
489 if(this->member() != other.member())
498 other.out_ == other.list_.end();
499 list_ = std::move(other.list_);
500 out_ = at_end ? list_.end() : other.out_;
501 in_size_ = other.in_size_;
502 in_pos_ = other.in_pos_;
503 out_pos_ = other.out_pos_;
504 out_end_ = other.out_end_;
506 other.out_ = other.list_.end();
513 template<class Allocator>
514 basic_multi_buffer<Allocator>::
515 basic_multi_buffer(basic_multi_buffer const& other)
516 : detail::empty_base_optimization<
517 base_alloc_type>(alloc_traits::
518 select_on_container_copy_construction(
526 template<class Allocator>
527 basic_multi_buffer<Allocator>::
528 basic_multi_buffer(basic_multi_buffer const& other,
529 Allocator const& alloc)
530 : detail::empty_base_optimization<
531 base_alloc_type>(alloc)
538 template<class Allocator>
539 template<class OtherAlloc>
540 basic_multi_buffer<Allocator>::
542 basic_multi_buffer<OtherAlloc> const& other)
548 template<class Allocator>
549 template<class OtherAlloc>
550 basic_multi_buffer<Allocator>::
552 basic_multi_buffer<OtherAlloc> const& other,
553 allocator_type const& alloc)
554 : detail::empty_base_optimization<
555 base_alloc_type>(alloc)
562 template<class Allocator>
564 basic_multi_buffer<Allocator>::
565 operator=(basic_multi_buffer&& other) ->
572 move_assign(other, std::integral_constant<bool,
573 alloc_traits::propagate_on_container_move_assignment::value>{});
577 template<class Allocator>
579 basic_multi_buffer<Allocator>::
580 operator=(basic_multi_buffer const& other) ->
585 copy_assign(other, std::integral_constant<bool,
586 alloc_traits::propagate_on_container_copy_assignment::value>{});
590 template<class Allocator>
591 template<class OtherAlloc>
593 basic_multi_buffer<Allocator>::
595 basic_multi_buffer<OtherAlloc> const& other) ->
604 template<class Allocator>
606 basic_multi_buffer<Allocator>::
610 if(pos == list_.end())
612 auto n = pos->size() - out_pos_;
613 while(++pos != list_.end())
618 template<class Allocator>
620 basic_multi_buffer<Allocator>::
624 return const_buffers_type(*this);
627 template<class Allocator>
629 basic_multi_buffer<Allocator>::
630 prepare(size_type n) ->
633 if(in_size_ + n > max_)
634 BOOST_THROW_EXCEPTION(std::length_error{
635 "dynamic buffer overflow"});
637 std::size_t total = in_size_;
638 // put all empty buffers on reuse list
639 if(out_ != list_.end())
641 total += out_->size() - out_pos_;
642 if(out_ != list_.iterator_to(list_.back()))
644 out_end_ = out_->size();
645 reuse.splice(reuse.end(), list_,
646 std::next(out_), list_.end());
647 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
651 auto const avail = out_->size() - out_pos_;
654 out_end_ = out_->size();
659 out_end_ = out_pos_ + n;
662 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
666 // get space from reuse buffers
667 while(n > 0 && ! reuse.empty())
669 auto& e = reuse.front();
670 reuse.erase(reuse.iterator_to(e));
683 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
687 BOOST_ASSERT(total <= max_);
688 if(! reuse.empty() || n > 0)
690 for(auto it = reuse.begin(); it != reuse.end();)
693 reuse.erase(list_.iterator_to(e));
694 auto const len = sizeof(e) + e.size();
695 alloc_traits::destroy(this->member(), &e);
696 alloc_traits::deallocate(this->member(),
697 reinterpret_cast<char*>(&e), len);
701 static auto const growth_factor = 2.0f;
703 (std::min<std::size_t>)(
705 (std::max<std::size_t>)({
706 static_cast<std::size_t>(
707 in_size_ * growth_factor - in_size_),
710 auto& e = *reinterpret_cast<element*>(static_cast<
711 void*>(alloc_traits::allocate(this->member(),
712 sizeof(element) + size)));
713 alloc_traits::construct(this->member(), &e, size);
715 if(out_ == list_.end())
716 out_ = list_.iterator_to(e);
718 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
723 return mutable_buffers_type(*this);
726 template<class Allocator>
728 basic_multi_buffer<Allocator>::
733 if(out_ == list_.end())
736 list_.iterator_to(list_.back());
740 out_->size() - out_pos_;
745 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
754 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
759 n = (std::min)(n, out_end_ - out_pos_);
762 if(out_pos_ == out_->size())
768 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
773 template<class Allocator>
775 basic_multi_buffer<Allocator>::
782 if(list_.begin() != out_)
785 list_.front().size() - in_pos_;
790 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
798 auto& e = list_.front();
799 list_.erase(list_.iterator_to(e));
800 auto const len = sizeof(e) + e.size();
801 alloc_traits::destroy(this->member(), &e);
802 alloc_traits::deallocate(this->member(),
803 reinterpret_cast<char*>(&e), len);
804 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
810 auto const avail = out_pos_ - in_pos_;
819 if(out_ != list_.iterator_to(list_.back()) ||
820 out_pos_ != out_end_)
826 // Input and output sequences are empty, reuse buffer.
827 // Alternatively we could deallocate it.
833 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
841 template<class Allocator>
844 basic_multi_buffer<Allocator>::
847 for(auto iter = list_.begin(); iter != list_.end();)
850 auto const len = sizeof(e) + e.size();
851 alloc_traits::destroy(this->member(), &e);
852 alloc_traits::deallocate(this->member(),
853 reinterpret_cast<char*>(&e), len);
857 template<class Allocator>
860 basic_multi_buffer<Allocator>::
872 template<class Allocator>
873 template<class DynamicBuffer>
876 basic_multi_buffer<Allocator>::
877 copy_from(DynamicBuffer const& buffer)
879 if(buffer.size() == 0)
881 using boost::asio::buffer_copy;
883 prepare(buffer.size()), buffer.data()));
886 template<class Allocator>
889 basic_multi_buffer<Allocator>::
890 move_assign(basic_multi_buffer& other, std::false_type)
892 if(this->member() != other.member())
899 move_assign(other, std::true_type{});
903 template<class Allocator>
906 basic_multi_buffer<Allocator>::
907 move_assign(basic_multi_buffer& other, std::true_type)
909 this->member() = std::move(other.member());
911 other.out_ == other.list_.end();
912 list_ = std::move(other.list_);
913 out_ = at_end ? list_.end() : other.out_;
915 in_size_ = other.in_size_;
916 in_pos_ = other.in_pos_;
917 out_pos_ = other.out_pos_;
918 out_end_ = other.out_end_;
921 other.out_ = other.list_.end();
927 template<class Allocator>
930 basic_multi_buffer<Allocator>::
932 basic_multi_buffer const& other, std::false_type)
939 template<class Allocator>
942 basic_multi_buffer<Allocator>::
944 basic_multi_buffer const& other, std::true_type)
948 this->member() = other.member();
952 template<class Allocator>
955 basic_multi_buffer<Allocator>::
956 swap(basic_multi_buffer& other)
959 alloc_traits::propagate_on_container_swap{});
962 template<class Allocator>
965 basic_multi_buffer<Allocator>::
966 swap(basic_multi_buffer& other, std::true_type)
972 other.out_ == other.list_.end();
973 swap(this->member(), other.member());
974 swap(list_, other.list_);
975 swap(out_, other.out_);
979 other.out_ = other.list_.end();
980 swap(in_size_, other.in_size_);
981 swap(in_pos_, other.in_pos_);
982 swap(out_pos_, other.out_pos_);
983 swap(out_end_, other.out_end_);
986 template<class Allocator>
989 basic_multi_buffer<Allocator>::
990 swap(basic_multi_buffer& other, std::false_type)
992 BOOST_ASSERT(this->member() == other.member());
997 other.out_ == other.list_.end();
998 swap(list_, other.list_);
999 swap(out_, other.out_);
1003 other.out_ = other.list_.end();
1004 swap(in_size_, other.in_size_);
1005 swap(in_pos_, other.in_pos_);
1006 swap(out_pos_, other.out_pos_);
1007 swap(out_end_, other.out_end_);
1010 template<class Allocator>
1013 basic_multi_buffer<Allocator>& lhs,
1014 basic_multi_buffer<Allocator>& rhs)
1019 template<class Allocator>
1021 basic_multi_buffer<Allocator>::
1025 using boost::asio::buffer_size;
1026 BOOST_ASSERT(buffer_size(data()) == in_size_);
1029 BOOST_ASSERT(in_pos_ == 0);
1030 BOOST_ASSERT(in_size_ == 0);
1031 BOOST_ASSERT(out_pos_ == 0);
1032 BOOST_ASSERT(out_end_ == 0);
1033 BOOST_ASSERT(out_ == list_.end());
1037 auto const& front = list_.front();
1039 BOOST_ASSERT(in_pos_ < front.size());
1041 if(out_ == list_.end())
1043 BOOST_ASSERT(out_pos_ == 0);
1044 BOOST_ASSERT(out_end_ == 0);
1048 auto const& out = *out_;
1049 auto const& back = list_.back();
1051 BOOST_ASSERT(out_end_ <= back.size());
1052 BOOST_ASSERT(out_pos_ < out.size());
1053 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
1054 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
1055 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);