Line data Source code
1 : //
2 : // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_BUFFERS_VECTOR_DYNAMIC_BUFFER_HPP
11 : #define BOOST_CAPY_BUFFERS_VECTOR_DYNAMIC_BUFFER_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/buffers.hpp>
15 : #include <boost/capy/detail/except.hpp>
16 : #include <type_traits>
17 : #include <vector>
18 :
19 : namespace boost {
20 : namespace capy {
21 :
22 : /** A dynamic buffer using an underlying vector.
23 :
24 : This class adapts a `std::vector` of byte-sized elements
25 : to satisfy the DynamicBuffer concept. The vector provides
26 : automatic memory management and growth.
27 :
28 : @par Constraints
29 :
30 : The element type `T` must be a fundamental type with
31 : `sizeof( T ) == 1`. This includes `char`, `unsigned char`,
32 : `signed char`, and similar byte-sized fundamental types.
33 :
34 : @par Example
35 : @code
36 : std::vector<unsigned char> v;
37 : vector_dynamic_buffer vb( &v );
38 :
39 : // Write data
40 : auto mb = vb.prepare( 100 );
41 : std::memcpy( mb.data(), "hello", 5 );
42 : vb.commit( 5 );
43 :
44 : // Read data
45 : auto data = vb.data();
46 : // process data...
47 : vb.consume( 5 );
48 : @endcode
49 :
50 : @par Thread Safety
51 : Distinct objects: Safe.
52 : Shared objects: Unsafe.
53 :
54 : @tparam T The element type. Must be fundamental with sizeof 1.
55 : @tparam Allocator The allocator type for the vector.
56 :
57 : @see flat_dynamic_buffer, circular_dynamic_buffer, string_dynamic_buffer
58 : */
59 : template<
60 : class T,
61 : class Allocator = std::allocator<T>>
62 : requires std::is_fundamental_v<T> && (sizeof(T) == 1)
63 : class basic_vector_dynamic_buffer
64 : {
65 : std::vector<T, Allocator>* v_;
66 : std::size_t max_size_;
67 :
68 : std::size_t in_size_ = 0;
69 : std::size_t out_size_ = 0;
70 :
71 : public:
72 : /// Indicates this is a DynamicBuffer adapter over external storage.
73 : using is_dynamic_buffer_adapter = void;
74 :
75 : /// The underlying vector type.
76 : using vector_type = std::vector<T, Allocator>;
77 :
78 : /// The ConstBufferSequence type for readable bytes.
79 : using const_buffers_type = const_buffer;
80 :
81 : /// The MutableBufferSequence type for writable bytes.
82 : using mutable_buffers_type = mutable_buffer;
83 :
84 : ~basic_vector_dynamic_buffer() = default;
85 :
86 : /** Move constructor.
87 : */
88 2 : basic_vector_dynamic_buffer(
89 : basic_vector_dynamic_buffer&& other) noexcept
90 2 : : v_(other.v_)
91 2 : , max_size_(other.max_size_)
92 2 : , in_size_(other.in_size_)
93 2 : , out_size_(other.out_size_)
94 : {
95 2 : other.v_ = nullptr;
96 2 : }
97 :
98 : /** Construct a dynamic buffer over a vector.
99 :
100 : @param v Pointer to the vector to use as storage.
101 : @param max_size Optional maximum size limit. Defaults
102 : to the vector's `max_size()`.
103 : */
104 : explicit
105 213 : basic_vector_dynamic_buffer(
106 : vector_type* v,
107 : std::size_t max_size =
108 : std::size_t(-1)) noexcept
109 213 : : v_(v)
110 213 : , max_size_(
111 213 : max_size > v_->max_size()
112 213 : ? v_->max_size()
113 213 : : max_size)
114 : {
115 213 : if(v_->size() > max_size_)
116 0 : v_->resize(max_size_);
117 213 : in_size_ = v_->size();
118 213 : }
119 :
120 : /// Copy assignment is deleted.
121 : basic_vector_dynamic_buffer& operator=(
122 : basic_vector_dynamic_buffer const&) = delete;
123 :
124 : /// Return the number of readable bytes.
125 : std::size_t
126 820 : size() const noexcept
127 : {
128 820 : return in_size_;
129 : }
130 :
131 : /// Return the maximum number of bytes the buffer can hold.
132 : std::size_t
133 2 : max_size() const noexcept
134 : {
135 2 : return max_size_;
136 : }
137 :
138 : /// Return the number of writable bytes without reallocation.
139 : std::size_t
140 2 : capacity() const noexcept
141 : {
142 2 : if(v_->capacity() <= max_size_)
143 1 : return v_->capacity() - in_size_;
144 1 : return max_size_ - in_size_;
145 : }
146 :
147 : /// Return a buffer sequence representing the readable bytes.
148 : const_buffers_type
149 147 : data() const noexcept
150 : {
151 147 : return const_buffers_type(
152 294 : v_->data(), in_size_);
153 : }
154 :
155 : /** Return a buffer sequence for writing.
156 :
157 : Invalidates buffer sequences previously obtained
158 : from @ref prepare.
159 :
160 : @param n The desired number of writable bytes.
161 :
162 : @return A mutable buffer sequence of size @p n.
163 :
164 : @throws std::invalid_argument if `size() + n > max_size()`.
165 : */
166 : mutable_buffers_type
167 169 : prepare(std::size_t n)
168 : {
169 169 : if(n > max_size_ - in_size_)
170 1 : detail::throw_invalid_argument();
171 :
172 168 : if(v_->size() < in_size_ + n)
173 167 : v_->resize(in_size_ + n);
174 168 : out_size_ = n;
175 504 : return mutable_buffers_type(
176 168 : v_->data() + in_size_, out_size_);
177 : }
178 :
179 : /** Move bytes from the output to the input sequence.
180 :
181 : Invalidates buffer sequences previously obtained
182 : from @ref prepare. Buffer sequences from @ref data
183 : remain valid.
184 :
185 : @param n The number of bytes to commit. If greater
186 : than the prepared size, all prepared bytes
187 : are committed.
188 : */
189 : void
190 147 : commit(std::size_t n) noexcept
191 : {
192 147 : if(n < out_size_)
193 1 : in_size_ += n;
194 : else
195 146 : in_size_ += out_size_;
196 147 : out_size_ = 0;
197 147 : v_->resize(in_size_);
198 147 : }
199 :
200 : /** Remove bytes from the beginning of the input sequence.
201 :
202 : Invalidates buffer sequences previously obtained
203 : from @ref data. Buffer sequences from @ref prepare
204 : remain valid.
205 :
206 : @param n The number of bytes to consume. If greater
207 : than @ref size(), all readable bytes are consumed.
208 : */
209 : void
210 166 : consume(std::size_t n) noexcept
211 : {
212 166 : if(n < in_size_)
213 : {
214 1 : v_->erase(v_->begin(), v_->begin() + n);
215 1 : in_size_ -= n;
216 : }
217 : else
218 : {
219 165 : v_->clear();
220 165 : in_size_ = 0;
221 : }
222 166 : out_size_ = 0;
223 166 : }
224 : };
225 :
226 : /// A dynamic buffer using `std::vector<unsigned char>`.
227 : using vector_dynamic_buffer =
228 : basic_vector_dynamic_buffer<unsigned char>;
229 :
230 : /** Create a dynamic buffer from a vector.
231 :
232 : @param v The vector to wrap. Element type must be
233 : a fundamental type with sizeof 1.
234 : @param max_size Optional maximum size limit.
235 : @return A vector_dynamic_buffer wrapping the vector.
236 : */
237 : template<class T, class Allocator>
238 : requires std::is_fundamental_v<T> && (sizeof(T) == 1)
239 : basic_vector_dynamic_buffer<T, Allocator>
240 : dynamic_buffer(
241 : std::vector<T, Allocator>& v,
242 : std::size_t max_size = std::size_t(-1))
243 : {
244 : return basic_vector_dynamic_buffer<T, Allocator>(&v, max_size);
245 : }
246 :
247 : } // capy
248 : } // boost
249 :
250 : #endif
|