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));
114 class readable_bytes;
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 /// The type of allocator used.
160 using allocator_type = Allocator;
163 ~basic_multi_buffer();
167 After construction, @ref capacity will return zero, and
168 @ref max_size will return the largest value which may
169 be passed to the allocator's `allocate` function.
171 basic_multi_buffer() noexcept(default_nothrow);
175 After construction, @ref capacity will return zero, and
176 @ref max_size will return the specified value of `limit`.
178 @param limit The desired maximum size.
182 std::size_t limit) noexcept(default_nothrow);
186 After construction, @ref capacity will return zero, and
187 @ref max_size will return the largest value which may
188 be passed to the allocator's `allocate` function.
190 @param alloc The allocator to use for the object.
197 basic_multi_buffer(Allocator const& alloc) noexcept;
201 After construction, @ref capacity will return zero, and
202 @ref max_size will return the specified value of `limit`.
204 @param limit The desired maximum size.
206 @param alloc The allocator to use for the object.
213 std::size_t limit, Allocator const& alloc) noexcept;
217 The container is constructed with the contents of `other`
218 using move semantics. The maximum size will be the same
219 as the moved-from object.
221 Buffer sequences previously obtained from `other` using
222 @ref data or @ref prepare remain valid after the move.
224 @param other The object to move from. After the move, the
225 moved-from object will have zero capacity, zero readable
226 bytes, and zero writable bytes.
232 basic_multi_buffer(basic_multi_buffer&& other) noexcept;
236 Using `alloc` as the allocator for the new container, the
237 contents of `other` are moved. If `alloc != other.get_allocator()`,
238 this results in a copy. The maximum size will be the same
239 as the moved-from object.
241 Buffer sequences previously obtained from `other` using
242 @ref data or @ref prepare become invalid after the move.
244 @param other The object to move from. After the move,
245 the moved-from object will have zero capacity, zero readable
246 bytes, and zero writable bytes.
248 @param alloc The allocator to use for the object.
250 @throws std::length_error if `other.size()` exceeds the
251 maximum allocation size of `alloc`.
254 basic_multi_buffer&& other,
255 Allocator const& alloc);
259 This container is constructed with the contents of `other`
260 using copy semantics. The maximum size will be the same
261 as the copied object.
263 @param other The object to copy from.
265 @throws std::length_error if `other.size()` exceeds the
266 maximum allocation size of the allocator.
268 basic_multi_buffer(basic_multi_buffer const& other);
272 This container is constructed with the contents of `other`
273 using copy semantics and the specified allocator. The maximum
274 size will be the same as the copied object.
276 @param other The object to copy from.
278 @param alloc The allocator to use for the object.
280 @throws std::length_error if `other.size()` exceeds the
281 maximum allocation size of `alloc`.
283 basic_multi_buffer(basic_multi_buffer const& other,
284 Allocator const& alloc);
288 This container is constructed with the contents of `other`
289 using copy semantics. The maximum size will be the same
290 as the copied object.
292 @param other The object to copy from.
294 @throws std::length_error if `other.size()` exceeds the
295 maximum allocation size of the allocator.
297 template<class OtherAlloc>
298 basic_multi_buffer(basic_multi_buffer<
299 OtherAlloc> const& other);
303 This container is constructed with the contents of `other`
304 using copy semantics. The maximum size will be the same
305 as the copied object.
307 @param other The object to copy from.
309 @param alloc The allocator to use for the object.
311 @throws std::length_error if `other.size()` exceeds the
312 maximum allocation size of `alloc`.
314 template<class OtherAlloc>
316 basic_multi_buffer<OtherAlloc> const& other,
317 allocator_type const& alloc);
321 The container is assigned with the contents of `other`
322 using move semantics. The maximum size will be the same
323 as the moved-from object.
325 Buffer sequences previously obtained from `other` using
326 @ref data or @ref prepare remain valid after the move.
328 @param other The object to move from. After the move,
329 the moved-from object will have zero capacity, zero readable
330 bytes, and zero writable bytes.
333 operator=(basic_multi_buffer&& other);
337 The container is assigned with the contents of `other`
338 using copy semantics. The maximum size will be the same
339 as the copied object.
341 After the copy, `this` will have zero writable bytes.
343 @param other The object to copy from.
345 @throws std::length_error if `other.size()` exceeds the
346 maximum allocation size of the allocator.
348 basic_multi_buffer& operator=(
349 basic_multi_buffer const& other);
353 The container is assigned with the contents of `other`
354 using copy semantics. The maximum size will be the same
355 as the copied object.
357 After the copy, `this` will have zero writable bytes.
359 @param other The object to copy from.
361 @throws std::length_error if `other.size()` exceeds the
362 maximum allocation size of the allocator.
364 template<class OtherAlloc>
365 basic_multi_buffer& operator=(
366 basic_multi_buffer<OtherAlloc> const& other);
368 /// Returns a copy of the allocator used.
370 get_allocator() const
375 /** Set the maximum allowed capacity
377 This function changes the currently configured upper limit
378 on capacity to the specified value.
380 @param n The maximum number of bytes ever allowed for capacity.
387 max_size(std::size_t n) noexcept
392 /** Guarantee a minimum capacity
394 This function adjusts the internal storage (if necessary)
395 to guarantee space for at least `n` bytes.
397 Buffer sequences previously obtained using @ref data remain
398 valid, while buffer sequences previously obtained using
399 @ref prepare become invalid.
401 @param n The minimum number of byte for the new capacity.
402 If this value is greater than the maximum size, then the
403 maximum size will be adjusted upwards to this value.
405 @throws std::length_error if n is larger than the maximum
406 allocation size of the allocator.
413 reserve(std::size_t n);
415 /** Reallocate the buffer to fit the readable bytes exactly.
417 Buffer sequences previously obtained using @ref data or
418 @ref prepare become invalid.
427 /** Set the size of the readable and writable bytes to zero.
429 This clears the buffer without changing capacity.
430 Buffer sequences previously obtained using @ref data or
431 @ref prepare become invalid.
440 /// Exchange two dynamic buffers
441 template<class Alloc>
445 basic_multi_buffer<Alloc>& lhs,
446 basic_multi_buffer<Alloc>& rhs) noexcept;
448 //--------------------------------------------------------------------------
450 #if BOOST_BEAST_DOXYGEN
451 /// The ConstBufferSequence used to represent the readable bytes.
452 using const_buffers_type = __implementation_defined__;
454 /// The MutableBufferSequence used to represent the readable bytes.
455 using mutable_data_type = __implementation_defined__;
457 /// The MutableBufferSequence used to represent the writable bytes.
458 using mutable_buffers_type = __implementation_defined__;
460 using const_buffers_type = readable_bytes<false>;
461 using mutable_data_type = readable_bytes<true>;
462 class mutable_buffers_type;
465 /// Returns the number of readable bytes.
467 size() const noexcept
472 /// Return the maximum number of bytes, both readable and writable, that can ever be held.
474 max_size() const noexcept
479 /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
481 capacity() const noexcept;
483 /** Returns a constant buffer sequence representing the readable bytes
485 @note The sequence may contain multiple contiguous memory regions.
488 data() const noexcept;
490 /** Returns a constant buffer sequence representing the readable bytes
492 @note The sequence may contain multiple contiguous memory regions.
495 cdata() const noexcept
500 /** Returns a mutable buffer sequence representing the readable bytes.
502 @note The sequence may contain multiple contiguous memory regions.
507 /** Returns a mutable buffer sequence representing writable bytes.
509 Returns a mutable buffer sequence representing the writable
510 bytes containing exactly `n` bytes of storage. Memory may be
511 reallocated as needed.
513 All buffer sequences previously obtained using @ref prepare are
514 invalidated. Buffer sequences previously obtained using @ref data
517 @param n The desired number of bytes in the returned buffer
520 @throws std::length_error if `size() + n` exceeds `max_size()`.
527 prepare(size_type n);
529 /** Append writable bytes to the readable bytes.
531 Appends n bytes from the start of the writable bytes to the
532 end of the readable bytes. The remainder of the writable bytes
533 are discarded. If n is greater than the number of writable
534 bytes, all writable bytes are appended to the readable bytes.
536 All buffer sequences previously obtained using @ref prepare are
537 invalidated. Buffer sequences previously obtained using @ref data
540 @param n The number of bytes to append. If this number
541 is greater than the number of writable bytes, all
542 writable bytes are appended.
549 commit(size_type n) noexcept;
551 /** Remove bytes from beginning of the readable bytes.
553 Removes n bytes from the beginning of the readable bytes.
555 All buffers sequences previously obtained using
556 @ref data or @ref prepare are invalidated.
558 @param n The number of bytes to remove. If this number
559 is greater than the number of readable bytes, all
560 readable bytes are removed.
567 consume(size_type n) noexcept;
570 template<class OtherAlloc>
571 friend class basic_multi_buffer;
573 template<class OtherAlloc>
574 void copy_from(basic_multi_buffer<OtherAlloc> const&);
575 void move_assign(basic_multi_buffer& other, std::false_type);
576 void move_assign(basic_multi_buffer& other, std::true_type) noexcept;
577 void copy_assign(basic_multi_buffer const& other, std::false_type);
578 void copy_assign(basic_multi_buffer const& other, std::true_type);
579 void swap(basic_multi_buffer&) noexcept;
580 void swap(basic_multi_buffer&, std::true_type) noexcept;
581 void swap(basic_multi_buffer&, std::false_type) noexcept;
582 void destroy(list_type& list) noexcept;
583 void destroy(const_iter it);
584 void destroy(element& e);
585 element& alloc(std::size_t size);
586 void debug_check() const;
589 /// A typical multi buffer
590 using multi_buffer = basic_multi_buffer<std::allocator<char>>;
595 #include <boost/beast/core/impl/multi_buffer.hpp>