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_FLAT_BUFFER_HPP
11 #define BOOST_BEAST_FLAT_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 <limits>
18 #include <memory>
19 #include <type_traits>
20 
21 namespace boost {
22 namespace beast {
23 
24 /** A dynamic buffer providing buffer sequences of length one.
25 
26     A dynamic buffer encapsulates memory storage that may be
27     automatically resized as required, where the memory is
28     divided into two regions: readable bytes followed by
29     writable bytes. These memory regions are internal to
30     the dynamic buffer, but direct access to the elements
31     is provided to permit them to be efficiently used with
32     I/O operations.
33 
34     Objects of this type meet the requirements of <em>DynamicBuffer</em>
35     and have the following additional properties:
36 
37     @li A mutable buffer sequence representing the readable
38     bytes is returned by @ref data when `this` is non-const.
39 
40     @li A configurable maximum buffer size may be set upon
41     construction. Attempts to exceed the buffer size will throw
42     `std::length_error`.
43 
44     @li Buffer sequences representing the readable and writable
45     bytes, returned by @ref data and @ref prepare, will have
46     length one.
47 
48     Upon construction, a maximum size for the buffer may be
49     specified. If this limit is exceeded, the `std::length_error`
50     exception will be thrown.
51 
52     @note This class is designed for use with algorithms that
53     take dynamic buffers as parameters, and are optimized
54     for the case where the input sequence or output sequence
55     is stored in a single contiguous buffer.
56 */
57 template<class Allocator>
58 class basic_flat_buffer
59 #if ! BOOST_BEAST_DOXYGEN
60     : private boost::empty_value<
61         typename detail::allocator_traits<Allocator>::
62             template rebind_alloc<char>>
63 #endif
64 {
65     template<class OtherAlloc>
66     friend class basic_flat_buffer;
67 
68     using base_alloc_type = typename
69         detail::allocator_traits<Allocator>::
70             template rebind_alloc<char>;
71 
72     static bool constexpr default_nothrow =
73         std::is_nothrow_default_constructible<Allocator>::value;
74 
75     using alloc_traits =
76         beast::detail::allocator_traits<base_alloc_type>;
77 
78     using pocma = typename
79         alloc_traits::propagate_on_container_move_assignment;
80 
81     using pocca = typename
82         alloc_traits::propagate_on_container_copy_assignment;
83 
84     static
85     std::size_t
dist(char const * first,char const * last)86     dist(char const* first, char const* last) noexcept
87     {
88         return static_cast<std::size_t>(last - first);
89     }
90 
91     char* begin_;
92     char* in_;
93     char* out_;
94     char* last_;
95     char* end_;
96     std::size_t max_;
97 
98 public:
99     /// The type of allocator used.
100     using allocator_type = Allocator;
101 
102     /// Destructor
103     ~basic_flat_buffer();
104 
105     /** Constructor
106 
107         After construction, @ref capacity will return zero, and
108         @ref max_size will return the largest value which may
109         be passed to the allocator's `allocate` function.
110     */
111     basic_flat_buffer() noexcept(default_nothrow);
112 
113     /** Constructor
114 
115         After construction, @ref capacity will return zero, and
116         @ref max_size will return the specified value of `limit`.
117 
118         @param limit The desired maximum size.
119     */
120     explicit
121     basic_flat_buffer(
122         std::size_t limit) noexcept(default_nothrow);
123 
124     /** Constructor
125 
126         After construction, @ref capacity will return zero, and
127         @ref max_size will return the largest value which may
128         be passed to the allocator's `allocate` function.
129 
130         @param alloc The allocator to use for the object.
131 
132         @esafe
133 
134         No-throw guarantee.
135     */
136     explicit
137     basic_flat_buffer(Allocator const& alloc) noexcept;
138 
139     /** Constructor
140 
141         After construction, @ref capacity will return zero, and
142         @ref max_size will return the specified value of `limit`.
143 
144         @param limit The desired maximum size.
145 
146         @param alloc The allocator to use for the object.
147 
148         @esafe
149 
150         No-throw guarantee.
151     */
152     basic_flat_buffer(
153         std::size_t limit,
154         Allocator const& alloc) noexcept;
155 
156     /** Move Constructor
157 
158         The container is constructed with the contents of `other`
159         using move semantics. The maximum size will be the same
160         as the moved-from object.
161 
162         Buffer sequences previously obtained from `other` using
163         @ref data or @ref prepare remain valid after the move.
164 
165         @param other The object to move from. After the move, the
166         moved-from object will have zero capacity, zero readable
167         bytes, and zero writable bytes.
168 
169         @esafe
170 
171         No-throw guarantee.
172     */
173     basic_flat_buffer(basic_flat_buffer&& other) noexcept;
174 
175     /** Move Constructor
176 
177         Using `alloc` as the allocator for the new container, the
178         contents of `other` are moved. If `alloc != other.get_allocator()`,
179         this results in a copy. The maximum size will be the same
180         as the moved-from object.
181 
182         Buffer sequences previously obtained from `other` using
183         @ref data or @ref prepare become invalid after the move.
184 
185         @param other The object to move from. After the move,
186         the moved-from object will have zero capacity, zero readable
187         bytes, and zero writable bytes.
188 
189         @param alloc The allocator to use for the object.
190 
191         @throws std::length_error if `other.size()` exceeds the
192         maximum allocation size of `alloc`.
193     */
194     basic_flat_buffer(
195         basic_flat_buffer&& other,
196         Allocator const& alloc);
197 
198     /** Copy Constructor
199 
200         This container is constructed with the contents of `other`
201         using copy semantics. The maximum size will be the same
202         as the copied object.
203 
204         @param other The object to copy from.
205 
206         @throws std::length_error if `other.size()` exceeds the
207         maximum allocation size of the allocator.
208     */
209     basic_flat_buffer(basic_flat_buffer const& other);
210 
211     /** Copy Constructor
212 
213         This container is constructed with the contents of `other`
214         using copy semantics and the specified allocator. The maximum
215         size will be the same as the copied object.
216 
217         @param other The object to copy from.
218 
219         @param alloc The allocator to use for the object.
220 
221         @throws std::length_error if `other.size()` exceeds the
222         maximum allocation size of `alloc`.
223     */
224     basic_flat_buffer(
225         basic_flat_buffer const& other,
226         Allocator const& alloc);
227 
228     /** Copy Constructor
229 
230         This container is constructed with the contents of `other`
231         using copy semantics. The maximum size will be the same
232         as the copied object.
233 
234         @param other The object to copy from.
235 
236         @throws std::length_error if `other.size()` exceeds the
237         maximum allocation size of the allocator.
238     */
239     template<class OtherAlloc>
240     basic_flat_buffer(
241         basic_flat_buffer<OtherAlloc> const& other)
242             noexcept(default_nothrow);
243 
244     /** Copy Constructor
245 
246         This container is constructed with the contents of `other`
247         using copy semantics. The maximum size will be the same
248         as the copied object.
249 
250         @param other The object to copy from.
251 
252         @param alloc The allocator to use for the object.
253 
254         @throws std::length_error if `other.size()` exceeds the
255         maximum allocation size of `alloc`.
256     */
257     template<class OtherAlloc>
258     basic_flat_buffer(
259         basic_flat_buffer<OtherAlloc> const& other,
260         Allocator const& alloc);
261 
262     /** Move Assignment
263 
264         The container is assigned with the contents of `other`
265         using move semantics. The maximum size will be the same
266         as the moved-from object.
267 
268         Buffer sequences previously obtained from `other` using
269         @ref data or @ref prepare remain valid after the move.
270 
271         @param other The object to move from. After the move,
272         the moved-from object will have zero capacity, zero readable
273         bytes, and zero writable bytes.
274 
275         @esafe
276 
277         No-throw guarantee.
278     */
279     basic_flat_buffer&
280     operator=(basic_flat_buffer&& other) noexcept;
281 
282     /** Copy Assignment
283 
284         The container is assigned with the contents of `other`
285         using copy semantics. The maximum size will be the same
286         as the copied object.
287 
288         After the copy, `this` will have zero writable bytes.
289 
290         @param other The object to copy from.
291 
292         @throws std::length_error if `other.size()` exceeds the
293         maximum allocation size of the allocator.
294     */
295     basic_flat_buffer&
296     operator=(basic_flat_buffer const& other);
297 
298     /** Copy assignment
299 
300         The container is assigned with the contents of `other`
301         using copy semantics. The maximum size will be the same
302         as the copied object.
303 
304         After the copy, `this` will have zero writable bytes.
305 
306         @param other The object to copy from.
307 
308         @throws std::length_error if `other.size()` exceeds the
309         maximum allocation size of the allocator.
310     */
311     template<class OtherAlloc>
312     basic_flat_buffer&
313     operator=(basic_flat_buffer<OtherAlloc> const& other);
314 
315     /// Returns a copy of the allocator used.
316     allocator_type
get_allocator() const317     get_allocator() const
318     {
319         return this->get();
320     }
321 
322     /** Set the maximum allowed capacity
323 
324         This function changes the currently configured upper limit
325         on capacity to the specified value.
326 
327         @param n The maximum number of bytes ever allowed for capacity.
328 
329         @esafe
330 
331         No-throw guarantee.
332     */
333     void
max_size(std::size_t n)334     max_size(std::size_t n) noexcept
335     {
336         max_ = n;
337     }
338 
339     /** Guarantee a minimum capacity
340 
341         This function adjusts the internal storage (if necessary)
342         to guarantee space for at least `n` bytes.
343 
344         Buffer sequences previously obtained using @ref data or
345         @ref prepare become invalid.
346 
347         @param n The minimum number of byte for the new capacity.
348         If this value is greater than the maximum size, then the
349         maximum size will be adjusted upwards to this value.
350 
351         @esafe
352 
353         Basic guarantee.
354 
355         @throws std::length_error if n is larger than the maximum
356         allocation size of the allocator.
357     */
358     void
359     reserve(std::size_t n);
360 
361     /** Reallocate the buffer to fit the readable bytes exactly.
362 
363         Buffer sequences previously obtained using @ref data or
364         @ref prepare become invalid.
365 
366         @esafe
367 
368         Strong guarantee.
369     */
370     void
371     shrink_to_fit();
372 
373     /** Set the size of the readable and writable bytes to zero.
374 
375         This clears the buffer without changing capacity.
376         Buffer sequences previously obtained using @ref data or
377         @ref prepare become invalid.
378 
379         @esafe
380 
381         No-throw guarantee.
382     */
383     void
384     clear() noexcept;
385 
386     /// Exchange two dynamic buffers
387     template<class Alloc>
388     friend
389     void
390     swap(
391         basic_flat_buffer<Alloc>&,
392         basic_flat_buffer<Alloc>&);
393 
394     //--------------------------------------------------------------------------
395 
396     /// The ConstBufferSequence used to represent the readable bytes.
397     using const_buffers_type = net::const_buffer;
398 
399     /// The MutableBufferSequence used to represent the readable bytes.
400     using mutable_data_type = net::mutable_buffer;
401 
402     /// The MutableBufferSequence used to represent the writable bytes.
403     using mutable_buffers_type = net::mutable_buffer;
404 
405     /// Returns the number of readable bytes.
406     std::size_t
size() const407     size() const noexcept
408     {
409         return dist(in_, out_);
410     }
411 
412     /// Return the maximum number of bytes, both readable and writable, that can ever be held.
413     std::size_t
max_size() const414     max_size() const noexcept
415     {
416         return max_;
417     }
418 
419     /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
420     std::size_t
capacity() const421     capacity() const noexcept
422     {
423         return dist(begin_, end_);
424     }
425 
426     /// Returns a constant buffer sequence representing the readable bytes
427     const_buffers_type
data() const428     data() const noexcept
429     {
430         return {in_, dist(in_, out_)};
431     }
432 
433     /// Returns a constant buffer sequence representing the readable bytes
434     const_buffers_type
cdata() const435     cdata() const noexcept
436     {
437         return data();
438     }
439 
440     /// Returns a mutable buffer sequence representing the readable bytes
441     mutable_data_type
data()442     data() noexcept
443     {
444         return {in_, dist(in_, out_)};
445     }
446 
447     /** Returns a mutable buffer sequence representing writable bytes.
448 
449         Returns a mutable buffer sequence representing the writable
450         bytes containing exactly `n` bytes of storage. Memory may be
451         reallocated as needed.
452 
453         All buffers sequences previously obtained using
454         @ref data or @ref prepare become invalid.
455 
456         @param n The desired number of bytes in the returned buffer
457         sequence.
458 
459         @throws std::length_error if `size() + n` exceeds either
460         `max_size()` or the allocator's maximum allocation size.
461 
462         @esafe
463 
464         Strong guarantee.
465     */
466     mutable_buffers_type
467     prepare(std::size_t n);
468 
469     /** Append writable bytes to the readable bytes.
470 
471         Appends n bytes from the start of the writable bytes to the
472         end of the readable bytes. The remainder of the writable bytes
473         are discarded. If n is greater than the number of writable
474         bytes, all writable bytes are appended to the readable bytes.
475 
476         All buffers sequences previously obtained using
477         @ref data or @ref prepare become invalid.
478 
479         @param n The number of bytes to append. If this number
480         is greater than the number of writable bytes, all
481         writable bytes are appended.
482 
483         @esafe
484 
485         No-throw guarantee.
486     */
487     void
commit(std::size_t n)488     commit(std::size_t n) noexcept
489     {
490         out_ += (std::min)(n, dist(out_, last_));
491     }
492 
493     /** Remove bytes from beginning of the readable bytes.
494 
495         Removes n bytes from the beginning of the readable bytes.
496 
497         All buffers sequences previously obtained using
498         @ref data or @ref prepare become invalid.
499 
500         @param n The number of bytes to remove. If this number
501         is greater than the number of readable bytes, all
502         readable bytes are removed.
503 
504         @esafe
505 
506         No-throw guarantee.
507     */
508     void
509     consume(std::size_t n) noexcept;
510 
511 private:
512     template<class OtherAlloc>
513     void copy_from(basic_flat_buffer<OtherAlloc> const& other);
514     void move_assign(basic_flat_buffer&, std::true_type);
515     void move_assign(basic_flat_buffer&, std::false_type);
516     void copy_assign(basic_flat_buffer const&, std::true_type);
517     void copy_assign(basic_flat_buffer const&, std::false_type);
518     void swap(basic_flat_buffer&);
519     void swap(basic_flat_buffer&, std::true_type);
520     void swap(basic_flat_buffer&, std::false_type);
521     char* alloc(std::size_t n);
522 };
523 
524 /// A flat buffer which uses the default allocator.
525 using flat_buffer =
526     basic_flat_buffer<std::allocator<char>>;
527 
528 } // beast
529 } // boost
530 
531 #include <boost/beast/core/impl/flat_buffer.hpp>
532 
533 #endif
534