TLA 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 : /// Destroy the buffer.
85 : ~basic_vector_dynamic_buffer() = default;
86 :
87 : /** Construct by moving.
88 : */
89 HIT 2 : basic_vector_dynamic_buffer(
90 : basic_vector_dynamic_buffer&& other) noexcept
91 2 : : v_(other.v_)
92 2 : , max_size_(other.max_size_)
93 2 : , in_size_(other.in_size_)
94 2 : , out_size_(other.out_size_)
95 : {
96 2 : other.v_ = nullptr;
97 2 : }
98 :
99 : /** Construct a dynamic buffer over a vector.
100 :
101 : @param v Pointer to the vector to use as storage.
102 : @param max_size Optional maximum size limit. Defaults
103 : to the vector's `max_size()`.
104 : */
105 : explicit
106 213 : basic_vector_dynamic_buffer(
107 : vector_type* v,
108 : std::size_t max_size =
109 : std::size_t(-1)) noexcept
110 213 : : v_(v)
111 213 : , max_size_(
112 213 : max_size > v_->max_size()
113 213 : ? v_->max_size()
114 213 : : max_size)
115 : {
116 213 : if(v_->size() > max_size_)
117 MIS 0 : v_->resize(max_size_);
118 HIT 213 : in_size_ = v_->size();
119 213 : }
120 :
121 : /// Copy assignment is deleted.
122 : basic_vector_dynamic_buffer& operator=(
123 : basic_vector_dynamic_buffer const&) = delete;
124 :
125 : /// Return the number of readable bytes.
126 : std::size_t
127 820 : size() const noexcept
128 : {
129 820 : return in_size_;
130 : }
131 :
132 : /// Return the maximum number of bytes the buffer can hold.
133 : std::size_t
134 2 : max_size() const noexcept
135 : {
136 2 : return max_size_;
137 : }
138 :
139 : /// Return the number of writable bytes without reallocation.
140 : std::size_t
141 2 : capacity() const noexcept
142 : {
143 2 : if(v_->capacity() <= max_size_)
144 1 : return v_->capacity() - in_size_;
145 1 : return max_size_ - in_size_;
146 : }
147 :
148 : /// Return a buffer sequence representing the readable bytes.
149 : const_buffers_type
150 147 : data() const noexcept
151 : {
152 147 : return const_buffers_type(
153 294 : v_->data(), in_size_);
154 : }
155 :
156 : /** Return a buffer sequence for writing.
157 :
158 : Invalidates buffer sequences previously obtained
159 : from @ref prepare.
160 :
161 : @param n The desired number of writable bytes.
162 :
163 : @return A mutable buffer sequence of size @p n.
164 :
165 : @throws std::invalid_argument if `size() + n > max_size()`.
166 : */
167 : mutable_buffers_type
168 169 : prepare(std::size_t n)
169 : {
170 169 : if(n > max_size_ - in_size_)
171 1 : detail::throw_invalid_argument();
172 :
173 168 : if(v_->size() < in_size_ + n)
174 167 : v_->resize(in_size_ + n);
175 168 : out_size_ = n;
176 504 : return mutable_buffers_type(
177 168 : v_->data() + in_size_, out_size_);
178 : }
179 :
180 : /** Move bytes from the output to the input sequence.
181 :
182 : Invalidates buffer sequences previously obtained
183 : from @ref prepare. Buffer sequences from @ref data
184 : remain valid.
185 :
186 : @param n The number of bytes to commit. If greater
187 : than the prepared size, all prepared bytes
188 : are committed.
189 : */
190 : void
191 147 : commit(std::size_t n) noexcept
192 : {
193 147 : if(n < out_size_)
194 1 : in_size_ += n;
195 : else
196 146 : in_size_ += out_size_;
197 147 : out_size_ = 0;
198 147 : v_->resize(in_size_);
199 147 : }
200 :
201 : /** Remove bytes from the beginning of the input sequence.
202 :
203 : Invalidates buffer sequences previously obtained
204 : from @ref data. Buffer sequences from @ref prepare
205 : remain valid.
206 :
207 : @param n The number of bytes to consume. If greater
208 : than @ref size(), all readable bytes are consumed.
209 : */
210 : void
211 166 : consume(std::size_t n) noexcept
212 : {
213 166 : if(n < in_size_)
214 : {
215 1 : v_->erase(v_->begin(), v_->begin() + n);
216 1 : in_size_ -= n;
217 : }
218 : else
219 : {
220 165 : v_->clear();
221 165 : in_size_ = 0;
222 : }
223 166 : out_size_ = 0;
224 166 : }
225 : };
226 :
227 : /// A dynamic buffer using `std::vector<unsigned char>`.
228 : using vector_dynamic_buffer =
229 : basic_vector_dynamic_buffer<unsigned char>;
230 :
231 : /** Create a dynamic buffer from a vector.
232 :
233 : @param v The vector to wrap. Element type must be
234 : a fundamental type with sizeof 1.
235 : @param max_size Optional maximum size limit.
236 : @return A vector_dynamic_buffer wrapping the vector.
237 : */
238 : template<class T, class Allocator>
239 : requires std::is_fundamental_v<T> && (sizeof(T) == 1)
240 : basic_vector_dynamic_buffer<T, Allocator>
241 : dynamic_buffer(
242 : std::vector<T, Allocator>& v,
243 : std::size_t max_size = std::size_t(-1))
244 : {
245 : return basic_vector_dynamic_buffer<T, Allocator>(&v, max_size);
246 : }
247 :
248 : } // capy
249 : } // boost
250 :
251 : #endif
|