2 // Copyright (c) 2016-2019 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_HPP
11 #define BOOST_BEAST_IMPL_MULTI_BUFFER_HPP
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>
22 #include <type_traits>
28 /* These diagrams illustrate the layout and state variables.
30 1 Input and output contained entirely in one element:
33 |<------+-----------+--------------------------------+----->|
34 in_pos_ out_pos_ out_end_
37 2 Output contained in first and second elements:
40 |<------+-----------+------>| |<-------------------+----->|
41 in_pos_ out_pos_ out_end_
44 3 Output contained in the second element:
47 |<------+------------------>| |<----+--------------+----->|
48 in_pos_ out_pos_ out_end_
51 4 Output contained in second and third elements:
54 |<------+------->| |<-------+------>| |<---------+----->|
55 in_pos_ out_pos_ out_end_
58 5 Input sequence is empty:
61 |<------+------------------>| |<-------------------+----->|
65 6 Output sequence is empty:
68 |<------+------------------>| |<------+------------------>|
73 7 The end of output can point to the end of an element.
74 But out_pos_ should never point to the end:
77 |<------+------------------>| |<------+------------------>|
78 in_pos_ out_pos_ out_end_
81 8 When the input sequence entirely fills the last element and
82 the output sequence is empty, out_ will point to the end of
83 the list of buffers, and out_pos_ and out_end_ will be 0:
86 |<------+------------------>| out_ == list_.end()
91 //------------------------------------------------------------------------------
93 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
94 # pragma warning (push)
95 # pragma warning (disable: 4521) // multiple copy constructors specified
96 # pragma warning (disable: 4522) // multiple assignment operators specified
99 template<class Allocator>
100 template<bool isMutable>
101 class basic_multi_buffer<Allocator>::readable_bytes
103 basic_multi_buffer const* b_;
105 friend class basic_multi_buffer;
109 basic_multi_buffer const& b) noexcept
115 using value_type = typename
119 net::const_buffer>::type;
121 class const_iterator;
123 readable_bytes() = delete;
124 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
125 readable_bytes(readable_bytes const& other)
130 readable_bytes& operator=(readable_bytes const& other)
136 readable_bytes(readable_bytes const&) = default;
137 readable_bytes& operator=(readable_bytes const&) = default;
141 bool isMutable_ = isMutable,
142 class = typename std::enable_if<! isMutable_>::type>
144 readable_bytes<true> const& other) noexcept
150 bool isMutable_ = isMutable,
151 class = typename std::enable_if<! isMutable_>::type>
152 readable_bytes& operator=(
153 readable_bytes<true> const& other) noexcept
159 const_iterator begin() const noexcept;
160 const_iterator end() const noexcept;
163 buffer_bytes() const noexcept
169 #if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
170 # pragma warning (pop)
173 //------------------------------------------------------------------------------
175 template<class Allocator>
176 template<bool isMutable>
178 basic_multi_buffer<Allocator>::
179 readable_bytes<isMutable>::
182 basic_multi_buffer const* b_ = nullptr;
183 typename list_type::const_iterator it_;
187 typename readable_bytes::value_type;
188 using pointer = value_type const*;
189 using reference = value_type;
190 using difference_type = std::ptrdiff_t;
191 using iterator_category =
192 std::bidirectional_iterator_tag;
194 const_iterator() = default;
196 const_iterator const& other) = default;
197 const_iterator& operator=(
198 const_iterator const& other) = default;
201 basic_multi_buffer const& b, typename
202 list_type::const_iterator const& it) noexcept
209 operator==(const_iterator const& other) const noexcept
211 return b_ == other.b_ && it_ == other.it_;
215 operator!=(const_iterator const& other) const noexcept
217 return !(*this == other);
221 operator*() const noexcept
223 auto const& e = *it_;
224 return value_type{e.data(),
225 (b_->out_ == b_->list_.end() ||
226 &e != &*b_->out_) ? e.size() : b_->out_pos_} +
227 (&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
231 operator->() const = delete;
234 operator++() noexcept
241 operator++(int) noexcept
249 operator--() noexcept
256 operator--(int) noexcept
264 //------------------------------------------------------------------------------
266 template<class Allocator>
267 class basic_multi_buffer<Allocator>::mutable_buffers_type
269 basic_multi_buffer const* b_;
271 friend class basic_multi_buffer;
274 mutable_buffers_type(
275 basic_multi_buffer const& b) noexcept
281 using value_type = net::mutable_buffer;
283 class const_iterator;
285 mutable_buffers_type() = delete;
286 mutable_buffers_type(mutable_buffers_type const&) = default;
287 mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
289 const_iterator begin() const noexcept;
290 const_iterator end() const noexcept;
293 //------------------------------------------------------------------------------
295 template<class Allocator>
296 class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
298 basic_multi_buffer const* b_ = nullptr;
299 typename list_type::const_iterator it_;
302 using value_type = typename
303 mutable_buffers_type::value_type;
304 using pointer = value_type const*;
305 using reference = value_type;
306 using difference_type = std::ptrdiff_t;
307 using iterator_category =
308 std::bidirectional_iterator_tag;
310 const_iterator() = default;
311 const_iterator(const_iterator const& other) = default;
312 const_iterator& operator=(const_iterator const& other) = default;
315 basic_multi_buffer const& b,
316 typename list_type::const_iterator const& it) noexcept
323 operator==(const_iterator const& other) const noexcept
325 return b_ == other.b_ && it_ == other.it_;
329 operator!=(const_iterator const& other) const noexcept
331 return !(*this == other);
335 operator*() const noexcept
337 auto const& e = *it_;
338 return value_type{e.data(),
339 &e == &*std::prev(b_->list_.end()) ?
340 b_->out_end_ : e.size()} +
341 (&e == &*b_->out_ ? b_->out_pos_ : 0);
345 operator->() const = delete;
348 operator++() noexcept
355 operator++(int) noexcept
363 operator--() noexcept
370 operator--(int) noexcept
378 //------------------------------------------------------------------------------
380 template<class Allocator>
381 template<bool isMutable>
383 basic_multi_buffer<Allocator>::
384 readable_bytes<isMutable>::
385 begin() const noexcept ->
388 return const_iterator{*b_, b_->list_.begin()};
391 template<class Allocator>
392 template<bool isMutable>
394 basic_multi_buffer<Allocator>::
395 readable_bytes<isMutable>::
396 end() const noexcept ->
399 return const_iterator{*b_, b_->out_ ==
400 b_->list_.end() ? b_->list_.end() :
401 std::next(b_->out_)};
404 template<class Allocator>
406 basic_multi_buffer<Allocator>::
407 mutable_buffers_type::
408 begin() const noexcept ->
411 return const_iterator{*b_, b_->out_};
414 template<class Allocator>
416 basic_multi_buffer<Allocator>::
417 mutable_buffers_type::
418 end() const noexcept ->
421 return const_iterator{*b_, b_->list_.end()};
424 //------------------------------------------------------------------------------
426 template<class Allocator>
427 basic_multi_buffer<Allocator>::
428 ~basic_multi_buffer()
433 template<class Allocator>
434 basic_multi_buffer<Allocator>::
435 basic_multi_buffer() noexcept(default_nothrow)
436 : max_(alloc_traits::max_size(this->get()))
441 template<class Allocator>
442 basic_multi_buffer<Allocator>::
444 std::size_t limit) noexcept(default_nothrow)
450 template<class Allocator>
451 basic_multi_buffer<Allocator>::
453 Allocator const& alloc) noexcept
454 : boost::empty_value<Allocator>(
455 boost::empty_init_t(), alloc)
456 , max_(alloc_traits::max_size(this->get()))
461 template<class Allocator>
462 basic_multi_buffer<Allocator>::
465 Allocator const& alloc) noexcept
466 : boost::empty_value<Allocator>(
467 boost::empty_init_t(), alloc)
473 template<class Allocator>
474 basic_multi_buffer<Allocator>::
476 basic_multi_buffer&& other) noexcept
477 : boost::empty_value<Allocator>(
478 boost::empty_init_t(), std::move(other.get()))
480 , in_size_(boost::exchange(other.in_size_, 0))
481 , in_pos_(boost::exchange(other.in_pos_, 0))
482 , out_pos_(boost::exchange(other.out_pos_, 0))
483 , out_end_(boost::exchange(other.out_end_, 0))
486 other.out_ == other.list_.end();
487 list_ = std::move(other.list_);
488 out_ = at_end ? list_.end() : other.out_;
489 other.out_ = other.list_.end();
492 template<class Allocator>
493 basic_multi_buffer<Allocator>::
495 basic_multi_buffer&& other,
496 Allocator const& alloc)
497 : boost::empty_value<Allocator>(
498 boost::empty_init_t(), alloc)
501 if(this->get() != other.get())
506 other.shrink_to_fit();
511 other.out_ == other.list_.end();
512 list_ = std::move(other.list_);
513 out_ = at_end ? list_.end() : other.out_;
514 in_size_ = other.in_size_;
515 in_pos_ = other.in_pos_;
516 out_pos_ = other.out_pos_;
517 out_end_ = other.out_end_;
519 other.out_ = other.list_.end();
525 template<class Allocator>
526 basic_multi_buffer<Allocator>::
528 basic_multi_buffer const& other)
529 : boost::empty_value<Allocator>(
530 boost::empty_init_t(), alloc_traits::
531 select_on_container_copy_construction(
539 template<class Allocator>
540 basic_multi_buffer<Allocator>::
542 basic_multi_buffer const& other,
543 Allocator const& alloc)
544 : boost::empty_value<Allocator>(
545 boost::empty_init_t(), alloc)
552 template<class Allocator>
553 template<class OtherAlloc>
554 basic_multi_buffer<Allocator>::
556 basic_multi_buffer<OtherAlloc> const& other)
562 template<class Allocator>
563 template<class OtherAlloc>
564 basic_multi_buffer<Allocator>::
566 basic_multi_buffer<OtherAlloc> const& other,
567 allocator_type const& alloc)
568 : boost::empty_value<Allocator>(
569 boost::empty_init_t(), alloc)
576 template<class Allocator>
578 basic_multi_buffer<Allocator>::
579 operator=(basic_multi_buffer&& other) ->
586 move_assign(other, pocma{});
590 template<class Allocator>
592 basic_multi_buffer<Allocator>::
593 operator=(basic_multi_buffer const& other) ->
598 copy_assign(other, pocca{});
602 template<class Allocator>
603 template<class OtherAlloc>
605 basic_multi_buffer<Allocator>::
607 basic_multi_buffer<OtherAlloc> const& other) ->
614 //------------------------------------------------------------------------------
616 template<class Allocator>
618 basic_multi_buffer<Allocator>::
619 capacity() const noexcept
622 if(pos == list_.end())
624 auto n = pos->size() - out_pos_;
625 while(++pos != list_.end())
630 template<class Allocator>
632 basic_multi_buffer<Allocator>::
633 data() const noexcept ->
636 return const_buffers_type(*this);
639 template<class Allocator>
641 basic_multi_buffer<Allocator>::
645 return mutable_data_type(*this);
648 template<class Allocator>
650 basic_multi_buffer<Allocator>::
651 reserve(std::size_t n)
653 // VFALCO The amount needs to be adjusted for
654 // the sizeof(element) plus padding
655 if(n > alloc_traits::max_size(this->get()))
656 BOOST_THROW_EXCEPTION(std::length_error(
657 "A basic_multi_buffer exceeded the allocator's maximum size"));
658 std::size_t total = in_size_;
661 if(out_ != list_.end())
663 total += out_->size() - out_pos_;
666 for(auto it = out_;;)
668 if(++it == list_.end())
675 BOOST_ASSERT(n > total);
676 (void)prepare(n - size());
679 template<class Allocator>
681 basic_multi_buffer<Allocator>::
688 // zero readable bytes
698 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
704 // one or more unused output buffers
705 if(out_ != list_.end())
707 if(out_ != list_.iterator_to(list_.back()))
717 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
724 list_.iterator_to(list_.back()));
727 BOOST_ASSERT(out_ != list_.begin());
733 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
740 [&](iter pos, element& e)
743 list_.insert(pos, e);
750 // partial last buffer
751 if(list_.size() > 1 && out_ != list_.end())
754 list_.iterator_to(list_.back()));
755 BOOST_ASSERT(out_pos_ != 0);
756 auto& e = alloc(out_pos_);
765 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
770 // partial first buffer
773 if(out_ != list_.begin())
776 list_.front().size() - in_pos_;
780 list_.front().data() + in_pos_,
782 replace(list_.begin(), e);
787 BOOST_ASSERT(list_.size() == 1);
788 BOOST_ASSERT(out_pos_ > in_pos_);
789 auto const n = out_pos_ - in_pos_;
793 list_.front().data() + in_pos_,
795 replace(list_.begin(), e);
799 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
805 template<class Allocator>
807 basic_multi_buffer<Allocator>::
810 out_ = list_.begin();
817 template<class Allocator>
819 basic_multi_buffer<Allocator>::
820 prepare(size_type n) ->
823 if(in_size_ > max_ || n > (max_ - in_size_))
824 BOOST_THROW_EXCEPTION(std::length_error{
825 "basic_multi_buffer too long"});
827 std::size_t total = in_size_;
828 // put all empty buffers on reuse list
829 if(out_ != list_.end())
831 total += out_->size() - out_pos_;
832 if(out_ != list_.iterator_to(list_.back()))
834 out_end_ = out_->size();
835 reuse.splice(reuse.end(), list_,
836 std::next(out_), list_.end());
837 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
841 auto const avail = out_->size() - out_pos_;
844 out_end_ = out_->size();
849 out_end_ = out_pos_ + n;
852 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
856 // get space from reuse buffers
857 while(n > 0 && ! reuse.empty())
859 auto& e = reuse.front();
860 reuse.erase(reuse.iterator_to(e));
873 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
877 BOOST_ASSERT(total <= max_);
878 if(! reuse.empty() || n > 0)
883 static auto const growth_factor = 2.0f;
885 (std::min<std::size_t>)(
887 (std::max<std::size_t>)({
888 static_cast<std::size_t>(
889 in_size_ * growth_factor - in_size_),
892 auto& e = alloc(size);
894 if(out_ == list_.end())
895 out_ = list_.iterator_to(e);
897 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
902 return mutable_buffers_type(*this);
905 template<class Allocator>
907 basic_multi_buffer<Allocator>::
908 commit(size_type n) noexcept
912 if(out_ == list_.end())
915 list_.iterator_to(list_.back());
919 out_->size() - out_pos_;
924 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
933 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
938 n = (std::min)(n, out_end_ - out_pos_);
941 if(out_pos_ == out_->size())
947 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
952 template<class Allocator>
954 basic_multi_buffer<Allocator>::
955 consume(size_type n) noexcept
961 if(list_.begin() != out_)
964 list_.front().size() - in_pos_;
969 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
977 auto& e = list_.front();
978 list_.erase(list_.iterator_to(e));
980 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
986 auto const avail = out_pos_ - in_pos_;
995 if(out_ != list_.iterator_to(list_.back()) ||
996 out_pos_ != out_end_)
1002 // Input and output sequences are empty, reuse buffer.
1003 // Alternatively we could deallocate it.
1009 #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
1017 template<class Allocator>
1018 template<class OtherAlloc>
1020 basic_multi_buffer<Allocator>::
1021 copy_from(basic_multi_buffer<OtherAlloc> const& other)
1025 if(other.size() == 0)
1027 commit(net::buffer_copy(
1028 prepare(other.size()), other.data()));
1031 template<class Allocator>
1033 basic_multi_buffer<Allocator>::
1034 move_assign(basic_multi_buffer& other, std::true_type) noexcept
1036 this->get() = std::move(other.get());
1038 other.out_ == other.list_.end();
1039 list_ = std::move(other.list_);
1040 out_ = at_end ? list_.end() : other.out_;
1042 in_size_ = other.in_size_;
1043 in_pos_ = other.in_pos_;
1044 out_pos_ = other.out_pos_;
1045 out_end_ = other.out_end_;
1049 other.out_ = other.list_.end();
1055 template<class Allocator>
1057 basic_multi_buffer<Allocator>::
1058 move_assign(basic_multi_buffer& other, std::false_type)
1060 if(this->get() != other.get())
1064 other.shrink_to_fit();
1068 move_assign(other, std::true_type{});
1072 template<class Allocator>
1074 basic_multi_buffer<Allocator>::
1076 basic_multi_buffer const& other, std::false_type)
1081 template<class Allocator>
1083 basic_multi_buffer<Allocator>::
1085 basic_multi_buffer const& other, std::true_type)
1088 this->get() = other.get();
1092 template<class Allocator>
1094 basic_multi_buffer<Allocator>::
1095 swap(basic_multi_buffer& other) noexcept
1097 swap(other, typename
1098 alloc_traits::propagate_on_container_swap{});
1101 template<class Allocator>
1103 basic_multi_buffer<Allocator>::
1104 swap(basic_multi_buffer& other, std::true_type) noexcept
1107 auto const at_end0 =
1108 out_ == list_.end();
1109 auto const at_end1 =
1110 other.out_ == other.list_.end();
1111 swap(this->get(), other.get());
1112 swap(list_, other.list_);
1113 swap(out_, other.out_);
1117 other.out_ = other.list_.end();
1118 swap(in_size_, other.in_size_);
1119 swap(in_pos_, other.in_pos_);
1120 swap(out_pos_, other.out_pos_);
1121 swap(out_end_, other.out_end_);
1124 template<class Allocator>
1126 basic_multi_buffer<Allocator>::
1127 swap(basic_multi_buffer& other, std::false_type) noexcept
1129 BOOST_ASSERT(this->get() == other.get());
1131 auto const at_end0 =
1132 out_ == list_.end();
1133 auto const at_end1 =
1134 other.out_ == other.list_.end();
1135 swap(list_, other.list_);
1136 swap(out_, other.out_);
1140 other.out_ = other.list_.end();
1141 swap(in_size_, other.in_size_);
1142 swap(in_pos_, other.in_pos_);
1143 swap(out_pos_, other.out_pos_);
1144 swap(out_end_, other.out_end_);
1147 template<class Allocator>
1150 basic_multi_buffer<Allocator>& lhs,
1151 basic_multi_buffer<Allocator>& rhs) noexcept
1156 template<class Allocator>
1158 basic_multi_buffer<Allocator>::
1159 destroy(list_type& list) noexcept
1161 for(auto it = list.begin();
1166 template<class Allocator>
1168 basic_multi_buffer<Allocator>::
1169 destroy(const_iter it)
1171 auto& e = list_.erase(it);
1175 template<class Allocator>
1177 basic_multi_buffer<Allocator>::
1180 auto a = rebind_type{this->get()};
1182 (sizeof(element) + e.size() +
1183 sizeof(align_type) - 1) /
1186 alloc_traits::deallocate(a,
1187 reinterpret_cast<align_type*>(&e), n);
1190 template<class Allocator>
1192 basic_multi_buffer<Allocator>::
1193 alloc(std::size_t size) ->
1196 if(size > alloc_traits::max_size(this->get()))
1197 BOOST_THROW_EXCEPTION(std::length_error(
1198 "A basic_multi_buffer exceeded the allocator's maximum size"));
1199 auto a = rebind_type{this->get()};
1200 auto const p = alloc_traits::allocate(a,
1201 (sizeof(element) + size + sizeof(align_type) - 1) /
1202 sizeof(align_type));
1203 return *(::new(p) element(size));
1206 template<class Allocator>
1208 basic_multi_buffer<Allocator>::
1212 BOOST_ASSERT(buffer_bytes(data()) == in_size_);
1215 BOOST_ASSERT(in_pos_ == 0);
1216 BOOST_ASSERT(in_size_ == 0);
1217 BOOST_ASSERT(out_pos_ == 0);
1218 BOOST_ASSERT(out_end_ == 0);
1219 BOOST_ASSERT(out_ == list_.end());
1223 auto const& front = list_.front();
1225 BOOST_ASSERT(in_pos_ < front.size());
1227 if(out_ == list_.end())
1229 BOOST_ASSERT(out_pos_ == 0);
1230 BOOST_ASSERT(out_end_ == 0);
1234 auto const& out = *out_;
1235 auto const& back = list_.back();
1237 BOOST_ASSERT(out_end_ <= back.size());
1238 BOOST_ASSERT(out_pos_ < out.size());
1239 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
1240 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
1241 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);