libs/capy/include/boost/capy/buffers/vector_dynamic_buffer.hpp

98.0% Lines (50/51) 100.0% Functions (9/9) 92.9% Branches (13/14)
libs/capy/include/boost/capy/buffers/vector_dynamic_buffer.hpp
Line Branch Hits 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
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 3 times.
213 ? v_->max_size()
113 213 : max_size)
114 {
115
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 213 times.
213 if(v_->size() > max_size_)
116 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/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
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
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 168 times.
169 if(n > max_size_ - in_size_)
170 1 detail::throw_invalid_argument();
171
172
2/2
✓ Branch 1 taken 167 times.
✓ Branch 2 taken 1 time.
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
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 146 times.
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
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 165 times.
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
251