include/boost/capy/buffers/buffer_array.hpp

99.1% Lines (106/107) 100.0% Functions (75/75)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 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_BUFFER_ARRAY_HPP
11 #define BOOST_CAPY_BUFFERS_BUFFER_ARRAY_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/detail/except.hpp>
15 #include <boost/capy/buffers.hpp>
16
17 #include <cstddef>
18 #include <new>
19 #include <span>
20 #include <utility>
21
22 namespace boost {
23 namespace capy {
24
25 namespace detail {
26
27 BOOST_CAPY_DECL
28 void
29 buffer_array_remove_prefix(
30 const_buffer* arr,
31 std::size_t* count,
32 std::size_t* total_size,
33 std::size_t n) noexcept;
34
35 BOOST_CAPY_DECL
36 void
37 buffer_array_remove_prefix(
38 mutable_buffer* arr,
39 std::size_t* count,
40 std::size_t* total_size,
41 std::size_t n) noexcept;
42
43 BOOST_CAPY_DECL
44 void
45 buffer_array_keep_prefix(
46 const_buffer* arr,
47 std::size_t* count,
48 std::size_t* total_size,
49 std::size_t n) noexcept;
50
51 BOOST_CAPY_DECL
52 void
53 buffer_array_keep_prefix(
54 mutable_buffer* arr,
55 std::size_t* count,
56 std::size_t* total_size,
57 std::size_t n) noexcept;
58
59 } // namespace detail
60
61 /** A buffer sequence holding up to N buffers.
62
63 This class template stores a fixed-capacity array of buffer
64 descriptors, where the actual count can vary from 0 to N.
65 It provides efficient storage for small buffer sequences
66 without dynamic allocation.
67
68 @par Example
69 @code
70 void process(ConstBufferSequence auto const& buffers)
71 {
72 const_buffer_array<4> bufs(buffers);
73 // use bufs.begin(), bufs.end(), bufs.to_span()
74 }
75 @endcode
76
77 @tparam N Maximum number of buffers the array can hold.
78 @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
79 */
80 template<std::size_t N, bool IsConst>
81 class buffer_array
82 {
83 public:
84 /** The type of buffer stored in the array.
85 */
86 using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
87
88 private:
89 std::size_t n_ = 0;
90 std::size_t size_ = 0;
91 union {
92 int dummy_;
93 value_type arr_[N];
94 };
95
96 public:
97 /** Construct a default instance.
98
99 Constructs an empty buffer array.
100 */
101 6x buffer_array() noexcept
102 6x : dummy_(0)
103 {
104 6x }
105
106 /** Construct a copy.
107 */
108 4644x buffer_array(buffer_array const& other) noexcept
109 4644x : n_(other.n_)
110 4644x , size_(other.size_)
111 {
112 12123x for(std::size_t i = 0; i < n_; ++i)
113 7479x ::new(&arr_[i]) value_type(other.arr_[i]);
114 4644x }
115
116 /** Construct from a single buffer.
117
118 @param b The buffer to store.
119 */
120 130x buffer_array(value_type const& b) noexcept
121 130x : dummy_(0)
122 {
123 130x if(b.size() != 0)
124 {
125 122x ::new(&arr_[0]) value_type(b);
126 122x n_ = 1;
127 122x size_ = b.size();
128 }
129 130x }
130
131 /** Construct from a buffer sequence.
132
133 Copies up to N buffer descriptors from the source
134 sequence into the internal array. If the sequence
135 contains more than N non-empty buffers, excess
136 buffers are silently ignored.
137
138 @param bs The buffer sequence to copy from.
139 */
140 template<class BS>
141 requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
142 && (!std::same_as<std::remove_cvref_t<BS>, buffer_array>)
143 && (!std::same_as<std::remove_cvref_t<BS>, value_type>)
144 185x buffer_array(BS const& bs) noexcept
145 185x : dummy_(0)
146 {
147 185x auto it = capy::begin(bs);
148 185x auto const last = capy::end(bs);
149 618x while(it != last && n_ < N)
150 {
151 433x value_type b(*it);
152 433x if(b.size() != 0)
153 {
154 427x ::new(&arr_[n_++]) value_type(b);
155 427x size_ += b.size();
156 }
157 433x ++it;
158 }
159 185x }
160
161 /** Construct from a buffer sequence with overflow checking.
162
163 Copies buffer descriptors from the source sequence
164 into the internal array.
165
166 @param bs The buffer sequence to copy from.
167
168 @throws std::length_error if the sequence contains
169 more than N non-empty buffers.
170 */
171 template<class BS>
172 requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
173 4x buffer_array(std::in_place_t, BS const& bs)
174 4x : dummy_(0)
175 {
176 4x auto it = capy::begin(bs);
177 4x auto const last = capy::end(bs);
178 14x while(it != last)
179 {
180 12x value_type b(*it);
181 12x if(b.size() != 0)
182 {
183 12x if(n_ >= N)
184 2x detail::throw_length_error();
185 10x ::new(&arr_[n_++]) value_type(b);
186 10x size_ += b.size();
187 }
188 10x ++it;
189 }
190 2x }
191
192 /** Construct from an iterator range.
193
194 Copies up to N non-empty buffer descriptors from the
195 range `[first, last)`. If the range contains more than
196 N non-empty buffers, excess buffers are silently ignored.
197
198 @param first Iterator to the first buffer descriptor.
199 @param last Iterator past the last buffer descriptor.
200 */
201 template<class Iterator>
202 8x buffer_array(Iterator first, Iterator last) noexcept
203 8x : dummy_(0)
204 {
205 26x while(first != last && n_ < N)
206 {
207 18x value_type b(*first);
208 18x if(b.size() != 0)
209 {
210 14x ::new(&arr_[n_++]) value_type(b);
211 14x size_ += b.size();
212 }
213 18x ++first;
214 }
215 8x }
216
217 /** Construct from an iterator range with overflow checking.
218
219 Copies all non-empty buffer descriptors from the range
220 `[first, last)` into the internal array.
221
222 @param first Iterator to the first buffer descriptor.
223 @param last Iterator past the last buffer descriptor.
224
225 @throws std::length_error if the range contains more
226 than N non-empty buffers.
227 */
228 template<class Iterator>
229 4x buffer_array(std::in_place_t, Iterator first, Iterator last)
230 4x : dummy_(0)
231 {
232 14x while(first != last)
233 {
234 12x value_type b(*first);
235 12x if(b.size() != 0)
236 {
237 12x if(n_ >= N)
238 2x detail::throw_length_error();
239 10x ::new(&arr_[n_++]) value_type(b);
240 10x size_ += b.size();
241 }
242 10x ++first;
243 }
244 2x }
245
246 /** Destructor.
247 */
248 4977x ~buffer_array()
249 {
250 11837x while(n_--)
251 6860x arr_[n_].~value_type();
252 4977x }
253
254 /** Assign by copying.
255 */
256 buffer_array&
257 4x operator=(buffer_array const& other) noexcept
258 {
259 4x if(this != &other)
260 {
261 4x while(n_--)
262 arr_[n_].~value_type();
263 4x n_ = other.n_;
264 4x size_ = other.size_;
265 10x for(std::size_t i = 0; i < n_; ++i)
266 6x ::new(&arr_[i]) value_type(other.arr_[i]);
267 }
268 4x return *this;
269 }
270
271 /** Return an iterator to the beginning.
272 */
273 value_type*
274 8834x begin() noexcept
275 {
276 8834x return arr_;
277 }
278
279 /** Return an iterator to the beginning.
280 */
281 value_type const*
282 11022x begin() const noexcept
283 {
284 11022x return arr_;
285 }
286
287 /** Return an iterator to the end.
288 */
289 value_type*
290 8833x end() noexcept
291 {
292 8833x return arr_ + n_;
293 }
294
295 /** Return an iterator to the end.
296 */
297 value_type const*
298 11022x end() const noexcept
299 {
300 11022x return arr_ + n_;
301 }
302
303 /** Return a span of the buffers.
304 */
305 std::span<value_type>
306 379x to_span() noexcept
307 {
308 379x return { arr_, n_ };
309 }
310
311 /** Return a span of the buffers.
312 */
313 std::span<value_type const>
314 175x to_span() const noexcept
315 {
316 175x return { arr_, n_ };
317 }
318
319 /** Conversion to mutable span.
320 */
321 1x operator std::span<value_type>() noexcept
322 {
323 1x return { arr_, n_ };
324 }
325
326 /** Conversion to const span.
327 */
328 operator std::span<value_type const>() const noexcept
329 {
330 return { arr_, n_ };
331 }
332
333 /** Return the total byte count in O(1).
334 */
335 friend
336 std::size_t
337 5499x tag_invoke(
338 size_tag const&,
339 buffer_array const& ba) noexcept
340 {
341 5499x return ba.size_;
342 }
343
344 /** Slice customization point.
345 */
346 friend
347 void
348 2080x tag_invoke(
349 slice_tag const&,
350 buffer_array& ba,
351 slice_how how,
352 std::size_t n) noexcept
353 {
354 2080x ba.slice_impl(how, n);
355 2080x }
356
357 private:
358 void
359 2080x slice_impl(
360 slice_how how,
361 std::size_t n) noexcept
362 {
363 2080x switch(how)
364 {
365 1024x case slice_how::remove_prefix:
366 1024x remove_prefix_impl(n);
367 1024x break;
368
369 1056x case slice_how::keep_prefix:
370 1056x keep_prefix_impl(n);
371 1056x break;
372 }
373 2080x }
374
375 void
376 1024x remove_prefix_impl(std::size_t n) noexcept
377 {
378 1024x detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
379 1024x }
380
381 void
382 1056x keep_prefix_impl(std::size_t n) noexcept
383 {
384 1056x detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
385 1056x }
386 };
387
388 /** Alias for buffer_array holding const_buffer.
389
390 @tparam N Maximum number of buffers.
391 */
392 template<std::size_t N>
393 using const_buffer_array = buffer_array<N, true>;
394
395 /** Alias for buffer_array holding mutable_buffer.
396
397 @tparam N Maximum number of buffers.
398 */
399 template<std::size_t N>
400 using mutable_buffer_array = buffer_array<N, false>;
401
402 } // namespace capy
403 } // namespace boost
404
405 #endif
406