1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
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)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9 
10 #ifndef BOOST_BEAST_MULTI_BUFFER_HPP
11 #define BOOST_BEAST_MULTI_BUFFER_HPP
12 
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>
19 #include <iterator>
20 #include <limits>
21 #include <memory>
22 #include <type_traits>
23 
24 namespace boost {
25 namespace beast {
26 
27 /** A dynamic buffer providing sequences of variable length.
28 
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
35     I/O operations.
36 
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`.
43 
44     Objects of this type meet the requirements of <em>DynamicBuffer</em>
45     and have the following additional properties:
46 
47     @li A mutable buffer sequence representing the readable
48     bytes is returned by @ref data when `this` is non-const.
49 
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.
53 
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`.
57 
58     @li Sequences previously obtained using @ref data remain
59     valid after calls to @ref prepare or @ref commit.
60 
61     @tparam Allocator The allocator to use for managing memory.
62 */
63 template<class Allocator>
64 class basic_multi_buffer
65 #if ! BOOST_BEAST_DOXYGEN
66     : private boost::empty_value<Allocator>
67 #endif
68 {
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");
73 
74     static bool constexpr default_nothrow =
75         std::is_nothrow_default_constructible<Allocator>::value;
76 
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.
80     class element
81         : public boost::intrusive::list_base_hook<
82             boost::intrusive::link_mode<
83                 boost::intrusive::normal_link>>
84     {
85         using size_type = typename
86             detail::allocator_traits<Allocator>::size_type;
87 
88         size_type const size_;
89 
90     public:
91         element(element const&) = delete;
92 
93         explicit
element(size_type n)94         element(size_type n) noexcept
95             : size_(n)
96         {
97         }
98 
99         size_type
size() const100         size() const noexcept
101         {
102             return size_;
103         }
104 
105         char*
data() const106         data() const noexcept
107         {
108             return const_cast<char*>(
109                 reinterpret_cast<char const*>(this + 1));
110         }
111     };
112 
113     template<bool>
114     class subrange;
115 
116     using size_type = typename
117         detail::allocator_traits<Allocator>::size_type;
118 
119     using align_type = typename
120         boost::type_with_alignment<alignof(element)>::type;
121 
122     using rebind_type = typename
123         beast::detail::allocator_traits<Allocator>::
124             template rebind_alloc<align_type>;
125 
126     using alloc_traits =
127         beast::detail::allocator_traits<rebind_type>;
128 
129     using list_type = typename boost::intrusive::make_list<
130         element, boost::intrusive::constant_time_size<true>>::type;
131 
132     using iter = typename list_type::iterator;
133 
134     using const_iter = typename list_type::const_iterator;
135 
136     using pocma = typename
137         alloc_traits::propagate_on_container_move_assignment;
138 
139     using pocca = typename
140         alloc_traits::propagate_on_container_copy_assignment;
141 
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");
145 
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");
149 
150     std::size_t max_;
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()
157 
158 public:
159 #if BOOST_BEAST_DOXYGEN
160     /// The ConstBufferSequence used to represent the readable bytes.
161     using const_buffers_type = __implementation_defined__;
162 
163     /// The MutableBufferSequence used to represent the writable bytes.
164     using mutable_buffers_type = __implementation_defined__;
165 #else
166     using const_buffers_type = subrange<false>;
167 
168     using mutable_buffers_type = subrange<true>;
169 #endif
170 
171     /// The type of allocator used.
172     using allocator_type = Allocator;
173 
174     /// Destructor
175     ~basic_multi_buffer();
176 
177     /** Constructor
178 
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.
182     */
183     basic_multi_buffer() noexcept(default_nothrow);
184 
185     /** Constructor
186 
187         After construction, @ref capacity will return zero, and
188         @ref max_size will return the specified value of `limit`.
189 
190         @param limit The desired maximum size.
191     */
192     explicit
193     basic_multi_buffer(
194         std::size_t limit) noexcept(default_nothrow);
195 
196     /** Constructor
197 
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.
201 
202         @param alloc The allocator to use for the object.
203 
204         @esafe
205 
206         No-throw guarantee.
207     */
208     explicit
209     basic_multi_buffer(Allocator const& alloc) noexcept;
210 
211     /** Constructor
212 
213         After construction, @ref capacity will return zero, and
214         @ref max_size will return the specified value of `limit`.
215 
216         @param limit The desired maximum size.
217 
218         @param alloc The allocator to use for the object.
219 
220         @esafe
221 
222         No-throw guarantee.
223     */
224     basic_multi_buffer(
225         std::size_t limit, Allocator const& alloc) noexcept;
226 
227     /** Move Constructor
228 
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.
232 
233         Buffer sequences previously obtained from `other` using
234         @ref data or @ref prepare remain valid after the move.
235 
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.
239 
240         @esafe
241 
242         No-throw guarantee.
243     */
244     basic_multi_buffer(basic_multi_buffer&& other) noexcept;
245 
246     /** Move Constructor
247 
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.
252 
253         Buffer sequences previously obtained from `other` using
254         @ref data or @ref prepare become invalid after the move.
255 
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.
259 
260         @param alloc The allocator to use for the object.
261 
262         @throws std::length_error if `other.size()` exceeds the
263         maximum allocation size of `alloc`.
264     */
265     basic_multi_buffer(
266         basic_multi_buffer&& other,
267         Allocator const& alloc);
268 
269     /** Copy Constructor
270 
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.
274 
275         @param other The object to copy from.
276 
277         @throws std::length_error if `other.size()` exceeds the
278         maximum allocation size of the allocator.
279     */
280     basic_multi_buffer(basic_multi_buffer const& other);
281 
282     /** Copy Constructor
283 
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.
287 
288         @param other The object to copy from.
289 
290         @param alloc The allocator to use for the object.
291 
292         @throws std::length_error if `other.size()` exceeds the
293         maximum allocation size of `alloc`.
294     */
295     basic_multi_buffer(basic_multi_buffer const& other,
296         Allocator const& alloc);
297 
298     /** Copy Constructor
299 
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.
303 
304         @param other The object to copy from.
305 
306         @throws std::length_error if `other.size()` exceeds the
307         maximum allocation size of the allocator.
308     */
309     template<class OtherAlloc>
310     basic_multi_buffer(basic_multi_buffer<
311         OtherAlloc> const& other);
312 
313     /** Copy Constructor
314 
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.
318 
319         @param other The object to copy from.
320 
321         @param alloc The allocator to use for the object.
322 
323         @throws std::length_error if `other.size()` exceeds the
324         maximum allocation size of `alloc`.
325     */
326     template<class OtherAlloc>
327     basic_multi_buffer(
328         basic_multi_buffer<OtherAlloc> const& other,
329         allocator_type const& alloc);
330 
331     /** Move Assignment
332 
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.
336 
337         Buffer sequences previously obtained from `other` using
338         @ref data or @ref prepare remain valid after the move.
339 
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.
343     */
344     basic_multi_buffer&
345     operator=(basic_multi_buffer&& other);
346 
347     /** Copy Assignment
348 
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.
352 
353         After the copy, `this` will have zero writable bytes.
354 
355         @param other The object to copy from.
356 
357         @throws std::length_error if `other.size()` exceeds the
358         maximum allocation size of the allocator.
359     */
360     basic_multi_buffer& operator=(
361         basic_multi_buffer const& other);
362 
363     /** Copy Assignment
364 
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.
368 
369         After the copy, `this` will have zero writable bytes.
370 
371         @param other The object to copy from.
372 
373         @throws std::length_error if `other.size()` exceeds the
374         maximum allocation size of the allocator.
375     */
376     template<class OtherAlloc>
377     basic_multi_buffer& operator=(
378         basic_multi_buffer<OtherAlloc> const& other);
379 
380     /// Returns a copy of the allocator used.
381     allocator_type
get_allocator() const382     get_allocator() const
383     {
384         return this->get();
385     }
386 
387     /** Set the maximum allowed capacity
388 
389         This function changes the currently configured upper limit
390         on capacity to the specified value.
391 
392         @param n The maximum number of bytes ever allowed for capacity.
393 
394         @esafe
395 
396         No-throw guarantee.
397     */
398     void
max_size(std::size_t n)399     max_size(std::size_t n) noexcept
400     {
401         max_ = n;
402     }
403 
404     /** Guarantee a minimum capacity
405 
406         This function adjusts the internal storage (if necessary)
407         to guarantee space for at least `n` bytes.
408 
409         Buffer sequences previously obtained using @ref data remain
410         valid, while buffer sequences previously obtained using
411         @ref prepare become invalid.
412 
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.
416 
417         @throws std::length_error if n is larger than the maximum
418         allocation size of the allocator.
419 
420         @esafe
421 
422         Strong guarantee.
423     */
424     void
425     reserve(std::size_t n);
426 
427     /** Reallocate the buffer to fit the readable bytes exactly.
428 
429         Buffer sequences previously obtained using @ref data or
430         @ref prepare become invalid.
431 
432         @esafe
433 
434         Strong guarantee.
435     */
436     void
437     shrink_to_fit();
438 
439     /** Set the size of the readable and writable bytes to zero.
440 
441         This clears the buffer without changing capacity.
442         Buffer sequences previously obtained using @ref data or
443         @ref prepare become invalid.
444 
445         @esafe
446 
447         No-throw guarantee.
448     */
449     void
450     clear() noexcept;
451 
452     /// Exchange two dynamic buffers
453     template<class Alloc>
454     friend
455     void
456     swap(
457         basic_multi_buffer<Alloc>& lhs,
458         basic_multi_buffer<Alloc>& rhs) noexcept;
459 
460     //--------------------------------------------------------------------------
461 
462     /// Returns the number of readable bytes.
463     size_type
size() const464     size() const noexcept
465     {
466         return in_size_;
467     }
468 
469     /// Return the maximum number of bytes, both readable and writable, that can ever be held.
470     size_type
max_size() const471     max_size() const noexcept
472     {
473         return max_;
474     }
475 
476     /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
477     std::size_t
478     capacity() const noexcept;
479 
480     /** Returns a constant buffer sequence representing the readable bytes
481 
482         @note The sequence may contain multiple contiguous memory regions.
483     */
484     const_buffers_type
485     data() const noexcept;
486 
487     /** Returns a constant buffer sequence representing the readable bytes
488 
489         @note The sequence may contain multiple contiguous memory regions.
490     */
491     const_buffers_type
cdata() const492     cdata() const noexcept
493     {
494         return data();
495     }
496 
497     /** Returns a mutable buffer sequence representing the readable bytes.
498 
499         @note The sequence may contain multiple contiguous memory regions.
500     */
501     mutable_buffers_type
502     data() noexcept;
503 
504     /** Returns a mutable buffer sequence representing writable bytes.
505 
506         Returns a mutable buffer sequence representing the writable
507         bytes containing exactly `n` bytes of storage. Memory may be
508         reallocated as needed.
509 
510         All buffer sequences previously obtained using @ref prepare are
511         invalidated. Buffer sequences previously obtained using @ref data
512         remain valid.
513 
514         @param n The desired number of bytes in the returned buffer
515         sequence.
516 
517         @throws std::length_error if `size() + n` exceeds `max_size()`.
518 
519         @esafe
520 
521         Strong guarantee.
522     */
523     mutable_buffers_type
524     prepare(size_type n);
525 
526     /** Append writable bytes to the readable bytes.
527 
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.
532 
533         All buffer sequences previously obtained using @ref prepare are
534         invalidated. Buffer sequences previously obtained using @ref data
535         remain valid.
536 
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.
540 
541         @esafe
542 
543         No-throw guarantee.
544     */
545     void
546     commit(size_type n) noexcept;
547 
548     /** Remove bytes from beginning of the readable bytes.
549 
550         Removes n bytes from the beginning of the readable bytes.
551 
552         All buffers sequences previously obtained using
553         @ref data or @ref prepare are invalidated.
554 
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.
558 
559         @esafe
560 
561         No-throw guarantee.
562     */
563     void
564     consume(size_type n) noexcept;
565 
566 private:
567     template<class OtherAlloc>
568     friend class basic_multi_buffer;
569 
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;
584 };
585 
586 /// A typical multi buffer
587 using multi_buffer = basic_multi_buffer<std::allocator<char>>;
588 
589 } // beast
590 } // boost
591 
592 #include <boost/beast/core/impl/multi_buffer.hpp>
593 
594 #endif