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_MULTI_BUFFER_HPP
11 #define BOOST_BEAST_MULTI_BUFFER_HPP
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/detail/allocator.hpp>
15 #include <boost/asio/buffer.hpp>
16 #include <boost/core/empty_value.hpp>
17 #include <boost/intrusive/list.hpp>
18 #include <boost/type_traits/type_with_alignment.hpp>
22 #include <type_traits>
27 /** A dynamic buffer providing sequences of variable length.
29 A dynamic buffer encapsulates memory storage that may be
30 automatically resized as required, where the memory is
31 divided into two regions: readable bytes followed by
32 writable bytes. These memory regions are internal to
33 the dynamic buffer, but direct access to the elements
34 is provided to permit them to be efficiently used with
37 The implementation uses a sequence of one or more byte
38 arrays of varying sizes to represent the readable and
39 writable bytes. Additional byte array objects are
40 appended to the sequence to accommodate changes in the
41 desired size. The behavior and implementation of this
42 container is most similar to `std::deque`.
44 Objects of this type meet the requirements of <em>DynamicBuffer</em>
45 and have the following additional properties:
47 @li A mutable buffer sequence representing the readable
48 bytes is returned by @ref data when `this` is non-const.
50 @li Buffer sequences representing the readable and writable
51 bytes, returned by @ref data and @ref prepare, may have
52 length greater than one.
54 @li A configurable maximum size may be set upon construction
55 and adjusted afterwards. Calls to @ref prepare that would
56 exceed this size will throw `std::length_error`.
58 @li Sequences previously obtained using @ref data remain
59 valid after calls to @ref prepare or @ref commit.
61 @tparam Allocator The allocator to use for managing memory.
63 template<class Allocator>
64 class basic_multi_buffer
65 #if ! BOOST_BEAST_DOXYGEN
66 : private boost::empty_value<Allocator>
69 // Fancy pointers are not supported
70 static_assert(std::is_pointer<typename
71 std::allocator_traits<Allocator>::pointer>::value,
72 "Allocator must use regular pointers");
74 static bool constexpr default_nothrow =
75 std::is_nothrow_default_constructible<Allocator>::value;
77 // Storage for the list of buffers representing the input
78 // and output sequences. The allocation for each element
79 // contains `element` followed by raw storage bytes.
81 : public boost::intrusive::list_base_hook<
82 boost::intrusive::link_mode<
83 boost::intrusive::normal_link>>
85 using size_type = typename
86 detail::allocator_traits<Allocator>::size_type;
88 size_type const size_;
91 element(element const&) = delete;
94 element(size_type n) noexcept
100 size() const noexcept
106 data() const noexcept
108 return const_cast<char*>(
109 reinterpret_cast<char const*>(this + 1));
116 using size_type = typename
117 detail::allocator_traits<Allocator>::size_type;
119 using align_type = typename
120 boost::type_with_alignment<alignof(element)>::type;
122 using rebind_type = typename
123 beast::detail::allocator_traits<Allocator>::
124 template rebind_alloc<align_type>;
127 beast::detail::allocator_traits<rebind_type>;
129 using list_type = typename boost::intrusive::make_list<
130 element, boost::intrusive::constant_time_size<true>>::type;
132 using iter = typename list_type::iterator;
134 using const_iter = typename list_type::const_iterator;
136 using pocma = typename
137 alloc_traits::propagate_on_container_move_assignment;
139 using pocca = typename
140 alloc_traits::propagate_on_container_copy_assignment;
142 static_assert(std::is_base_of<std::bidirectional_iterator_tag,
143 typename std::iterator_traits<iter>::iterator_category>::value,
144 "BidirectionalIterator type requirements not met");
146 static_assert(std::is_base_of<std::bidirectional_iterator_tag,
147 typename std::iterator_traits<const_iter>::iterator_category>::value,
148 "BidirectionalIterator type requirements not met");
151 list_type list_; // list of allocated buffers
152 iter out_; // element that contains out_pos_
153 size_type in_size_ = 0; // size of the input sequence
154 size_type in_pos_ = 0; // input offset in list_.front()
155 size_type out_pos_ = 0; // output offset in *out_
156 size_type out_end_ = 0; // output end offset in list_.back()
159 #if BOOST_BEAST_DOXYGEN
160 /// The ConstBufferSequence used to represent the readable bytes.
161 using const_buffers_type = __implementation_defined__;
163 /// The MutableBufferSequence used to represent the writable bytes.
164 using mutable_buffers_type = __implementation_defined__;
166 using const_buffers_type = subrange<false>;
168 using mutable_buffers_type = subrange<true>;
171 /// The type of allocator used.
172 using allocator_type = Allocator;
175 ~basic_multi_buffer();
179 After construction, @ref capacity will return zero, and
180 @ref max_size will return the largest value which may
181 be passed to the allocator's `allocate` function.
183 basic_multi_buffer() noexcept(default_nothrow);
187 After construction, @ref capacity will return zero, and
188 @ref max_size will return the specified value of `limit`.
190 @param limit The desired maximum size.
194 std::size_t limit) noexcept(default_nothrow);
198 After construction, @ref capacity will return zero, and
199 @ref max_size will return the largest value which may
200 be passed to the allocator's `allocate` function.
202 @param alloc The allocator to use for the object.
209 basic_multi_buffer(Allocator const& alloc) noexcept;
213 After construction, @ref capacity will return zero, and
214 @ref max_size will return the specified value of `limit`.
216 @param limit The desired maximum size.
218 @param alloc The allocator to use for the object.
225 std::size_t limit, Allocator const& alloc) noexcept;
229 The container is constructed with the contents of `other`
230 using move semantics. The maximum size will be the same
231 as the moved-from object.
233 Buffer sequences previously obtained from `other` using
234 @ref data or @ref prepare remain valid after the move.
236 @param other The object to move from. After the move, the
237 moved-from object will have zero capacity, zero readable
238 bytes, and zero writable bytes.
244 basic_multi_buffer(basic_multi_buffer&& other) noexcept;
248 Using `alloc` as the allocator for the new container, the
249 contents of `other` are moved. If `alloc != other.get_allocator()`,
250 this results in a copy. The maximum size will be the same
251 as the moved-from object.
253 Buffer sequences previously obtained from `other` using
254 @ref data or @ref prepare become invalid after the move.
256 @param other The object to move from. After the move,
257 the moved-from object will have zero capacity, zero readable
258 bytes, and zero writable bytes.
260 @param alloc The allocator to use for the object.
262 @throws std::length_error if `other.size()` exceeds the
263 maximum allocation size of `alloc`.
266 basic_multi_buffer&& other,
267 Allocator const& alloc);
271 This container is constructed with the contents of `other`
272 using copy semantics. The maximum size will be the same
273 as the copied object.
275 @param other The object to copy from.
277 @throws std::length_error if `other.size()` exceeds the
278 maximum allocation size of the allocator.
280 basic_multi_buffer(basic_multi_buffer const& other);
284 This container is constructed with the contents of `other`
285 using copy semantics and the specified allocator. The maximum
286 size will be the same as the copied object.
288 @param other The object to copy from.
290 @param alloc The allocator to use for the object.
292 @throws std::length_error if `other.size()` exceeds the
293 maximum allocation size of `alloc`.
295 basic_multi_buffer(basic_multi_buffer const& other,
296 Allocator const& alloc);
300 This container is constructed with the contents of `other`
301 using copy semantics. The maximum size will be the same
302 as the copied object.
304 @param other The object to copy from.
306 @throws std::length_error if `other.size()` exceeds the
307 maximum allocation size of the allocator.
309 template<class OtherAlloc>
310 basic_multi_buffer(basic_multi_buffer<
311 OtherAlloc> const& other);
315 This container is constructed with the contents of `other`
316 using copy semantics. The maximum size will be the same
317 as the copied object.
319 @param other The object to copy from.
321 @param alloc The allocator to use for the object.
323 @throws std::length_error if `other.size()` exceeds the
324 maximum allocation size of `alloc`.
326 template<class OtherAlloc>
328 basic_multi_buffer<OtherAlloc> const& other,
329 allocator_type const& alloc);
333 The container is assigned with the contents of `other`
334 using move semantics. The maximum size will be the same
335 as the moved-from object.
337 Buffer sequences previously obtained from `other` using
338 @ref data or @ref prepare remain valid after the move.
340 @param other The object to move from. After the move,
341 the moved-from object will have zero capacity, zero readable
342 bytes, and zero writable bytes.
345 operator=(basic_multi_buffer&& other);
349 The container is assigned with the contents of `other`
350 using copy semantics. The maximum size will be the same
351 as the copied object.
353 After the copy, `this` will have zero writable bytes.
355 @param other The object to copy from.
357 @throws std::length_error if `other.size()` exceeds the
358 maximum allocation size of the allocator.
360 basic_multi_buffer& operator=(
361 basic_multi_buffer const& other);
365 The container is assigned with the contents of `other`
366 using copy semantics. The maximum size will be the same
367 as the copied object.
369 After the copy, `this` will have zero writable bytes.
371 @param other The object to copy from.
373 @throws std::length_error if `other.size()` exceeds the
374 maximum allocation size of the allocator.
376 template<class OtherAlloc>
377 basic_multi_buffer& operator=(
378 basic_multi_buffer<OtherAlloc> const& other);
380 /// Returns a copy of the allocator used.
382 get_allocator() const
387 /** Set the maximum allowed capacity
389 This function changes the currently configured upper limit
390 on capacity to the specified value.
392 @param n The maximum number of bytes ever allowed for capacity.
399 max_size(std::size_t n) noexcept
404 /** Guarantee a minimum capacity
406 This function adjusts the internal storage (if necessary)
407 to guarantee space for at least `n` bytes.
409 Buffer sequences previously obtained using @ref data remain
410 valid, while buffer sequences previously obtained using
411 @ref prepare become invalid.
413 @param n The minimum number of byte for the new capacity.
414 If this value is greater than the maximum size, then the
415 maximum size will be adjusted upwards to this value.
417 @throws std::length_error if n is larger than the maximum
418 allocation size of the allocator.
425 reserve(std::size_t n);
427 /** Reallocate the buffer to fit the readable bytes exactly.
429 Buffer sequences previously obtained using @ref data or
430 @ref prepare become invalid.
439 /** Set the size of the readable and writable bytes to zero.
441 This clears the buffer without changing capacity.
442 Buffer sequences previously obtained using @ref data or
443 @ref prepare become invalid.
452 /// Exchange two dynamic buffers
453 template<class Alloc>
457 basic_multi_buffer<Alloc>& lhs,
458 basic_multi_buffer<Alloc>& rhs) noexcept;
460 //--------------------------------------------------------------------------
462 /// Returns the number of readable bytes.
464 size() const noexcept
469 /// Return the maximum number of bytes, both readable and writable, that can ever be held.
471 max_size() const noexcept
476 /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
478 capacity() const noexcept;
480 /** Returns a constant buffer sequence representing the readable bytes
482 @note The sequence may contain multiple contiguous memory regions.
485 data() const noexcept;
487 /** Returns a constant buffer sequence representing the readable bytes
489 @note The sequence may contain multiple contiguous memory regions.
492 cdata() const noexcept
497 /** Returns a mutable buffer sequence representing the readable bytes.
499 @note The sequence may contain multiple contiguous memory regions.
504 /** Returns a mutable buffer sequence representing writable bytes.
506 Returns a mutable buffer sequence representing the writable
507 bytes containing exactly `n` bytes of storage. Memory may be
508 reallocated as needed.
510 All buffer sequences previously obtained using @ref prepare are
511 invalidated. Buffer sequences previously obtained using @ref data
514 @param n The desired number of bytes in the returned buffer
517 @throws std::length_error if `size() + n` exceeds `max_size()`.
524 prepare(size_type n);
526 /** Append writable bytes to the readable bytes.
528 Appends n bytes from the start of the writable bytes to the
529 end of the readable bytes. The remainder of the writable bytes
530 are discarded. If n is greater than the number of writable
531 bytes, all writable bytes are appended to the readable bytes.
533 All buffer sequences previously obtained using @ref prepare are
534 invalidated. Buffer sequences previously obtained using @ref data
537 @param n The number of bytes to append. If this number
538 is greater than the number of writable bytes, all
539 writable bytes are appended.
546 commit(size_type n) noexcept;
548 /** Remove bytes from beginning of the readable bytes.
550 Removes n bytes from the beginning of the readable bytes.
552 All buffers sequences previously obtained using
553 @ref data or @ref prepare are invalidated.
555 @param n The number of bytes to remove. If this number
556 is greater than the number of readable bytes, all
557 readable bytes are removed.
564 consume(size_type n) noexcept;
567 template<class OtherAlloc>
568 friend class basic_multi_buffer;
570 template<class OtherAlloc>
571 void copy_from(basic_multi_buffer<OtherAlloc> const&);
572 void move_assign(basic_multi_buffer& other, std::false_type);
573 void move_assign(basic_multi_buffer& other, std::true_type) noexcept;
574 void copy_assign(basic_multi_buffer const& other, std::false_type);
575 void copy_assign(basic_multi_buffer const& other, std::true_type);
576 void swap(basic_multi_buffer&) noexcept;
577 void swap(basic_multi_buffer&, std::true_type) noexcept;
578 void swap(basic_multi_buffer&, std::false_type) noexcept;
579 void destroy(list_type& list) noexcept;
580 void destroy(const_iter it);
581 void destroy(element& e);
582 element& alloc(std::size_t size);
583 void debug_check() const;
586 /// A typical multi buffer
587 using multi_buffer = basic_multi_buffer<std::allocator<char>>;
592 #include <boost/beast/core/impl/multi_buffer.hpp>