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

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