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     /** Request the removal of unused capacity.
362 
363         This function attempts to reduce @ref capacity()
364         to @ref size(), which may not succeed.
365 
366         @esafe
367 
368         No-throw guarantee.
369     */
370     void
371     shrink_to_fit() noexcept;
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 writable bytes.
400     using mutable_buffers_type = net::mutable_buffer;
401 
402     /// Returns the number of readable bytes.
403     std::size_t
size() const404     size() const noexcept
405     {
406         return dist(in_, out_);
407     }
408 
409     /// Return the maximum number of bytes, both readable and writable, that can ever be held.
410     std::size_t
max_size() const411     max_size() const noexcept
412     {
413         return max_;
414     }
415 
416     /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
417     std::size_t
capacity() const418     capacity() const noexcept
419     {
420         return dist(begin_, end_);
421     }
422 
423     /// Returns a constant buffer sequence representing the readable bytes
424     const_buffers_type
data() const425     data() const noexcept
426     {
427         return {in_, dist(in_, out_)};
428     }
429 
430     /// Returns a constant buffer sequence representing the readable bytes
431     const_buffers_type
cdata() const432     cdata() const noexcept
433     {
434         return data();
435     }
436 
437     /// Returns a mutable buffer sequence representing the readable bytes
438     mutable_buffers_type
data()439     data() noexcept
440     {
441         return {in_, dist(in_, out_)};
442     }
443 
444     /** Returns a mutable buffer sequence representing writable bytes.
445 
446         Returns a mutable buffer sequence representing the writable
447         bytes containing exactly `n` bytes of storage. Memory may be
448         reallocated as needed.
449 
450         All buffers sequences previously obtained using
451         @ref data or @ref prepare become invalid.
452 
453         @param n The desired number of bytes in the returned buffer
454         sequence.
455 
456         @throws std::length_error if `size() + n` exceeds either
457         `max_size()` or the allocator's maximum allocation size.
458 
459         @esafe
460 
461         Strong guarantee.
462     */
463     mutable_buffers_type
464     prepare(std::size_t n);
465 
466     /** Append writable bytes to the readable bytes.
467 
468         Appends n bytes from the start of the writable bytes to the
469         end of the readable bytes. The remainder of the writable bytes
470         are discarded. If n is greater than the number of writable
471         bytes, all writable bytes are appended to the readable bytes.
472 
473         All buffers sequences previously obtained using
474         @ref data or @ref prepare become invalid.
475 
476         @param n The number of bytes to append. If this number
477         is greater than the number of writable bytes, all
478         writable bytes are appended.
479 
480         @esafe
481 
482         No-throw guarantee.
483     */
484     void
commit(std::size_t n)485     commit(std::size_t n) noexcept
486     {
487         out_ += (std::min)(n, dist(out_, last_));
488     }
489 
490     /** Remove bytes from beginning of the readable bytes.
491 
492         Removes n bytes from the beginning of the readable bytes.
493 
494         All buffers sequences previously obtained using
495         @ref data or @ref prepare become invalid.
496 
497         @param n The number of bytes to remove. If this number
498         is greater than the number of readable bytes, all
499         readable bytes are removed.
500 
501         @esafe
502 
503         No-throw guarantee.
504     */
505     void
506     consume(std::size_t n) noexcept;
507 
508 private:
509     template<class OtherAlloc>
510     void copy_from(basic_flat_buffer<OtherAlloc> const& other);
511     void move_assign(basic_flat_buffer&, std::true_type);
512     void move_assign(basic_flat_buffer&, std::false_type);
513     void copy_assign(basic_flat_buffer const&, std::true_type);
514     void copy_assign(basic_flat_buffer const&, std::false_type);
515     void swap(basic_flat_buffer&);
516     void swap(basic_flat_buffer&, std::true_type);
517     void swap(basic_flat_buffer&, std::false_type);
518     char* alloc(std::size_t n);
519 };
520 
521 /// A flat buffer which uses the default allocator.
522 using flat_buffer =
523     basic_flat_buffer<std::allocator<char>>;
524 
525 } // beast
526 } // boost
527 
528 #include <boost/beast/core/impl/flat_buffer.hpp>
529 
530 #endif
531