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

98.8% Lines (84/85) 100.0% Functions (49/49) 86.2% Branches (25/29)
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 4386 buffer_array(buffer_array const& other) noexcept
110 4386 : n_(other.n_)
111 4386 , size_(other.size_)
112 {
113
2/2
✓ Branch 0 taken 7022 times.
✓ Branch 1 taken 4386 times.
11408 for(std::size_t i = 0; i < n_; ++i)
114 7022 ::new(&arr_[i]) value_type(other.arr_[i]);
115 4386 }
116
117 /** Construct from a single buffer.
118
119 @param b The buffer to store.
120 */
121 11 buffer_array(value_type const& b) noexcept
122 11 : dummy_(0)
123 {
124
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 time.
11 if(b.size() != 0)
125 {
126 10 ::new(&arr_[0]) value_type(b);
127 10 n_ = 1;
128 10 size_ = b.size();
129 }
130 11 }
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 38 buffer_array(BS const& bs) noexcept
146 38 : dummy_(0)
147 {
148 38 auto it = capy::begin(bs);
149 38 auto const last = capy::end(bs);
150
6/6
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 78 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 78 times.
✓ Branch 6 taken 38 times.
116 while(it != last && n_ < N)
151 {
152 78 value_type b(*it);
153
2/2
✓ Branch 1 taken 74 times.
✓ Branch 2 taken 4 times.
78 if(b.size() != 0)
154 {
155 74 ::new(&arr_[n_++]) value_type(b);
156 74 size_ += b.size();
157 }
158 78 ++it;
159 }
160 38 }
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 /** Destructor.
194 */
195 4443 ~buffer_array()
196 {
197
2/2
✓ Branch 0 taken 5918 times.
✓ Branch 1 taken 4443 times.
10361 while(n_--)
198 5918 arr_[n_].~value_type();
199 4443 }
200
201 /** Copy assignment.
202 */
203 buffer_array&
204 4 operator=(buffer_array const& other) noexcept
205 {
206
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(this != &other)
207 {
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 while(n_--)
209 arr_[n_].~value_type();
210 4 n_ = other.n_;
211 4 size_ = other.size_;
212
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 for(std::size_t i = 0; i < n_; ++i)
213 6 ::new(&arr_[i]) value_type(other.arr_[i]);
214 }
215 4 return *this;
216 }
217
218 /** Return an iterator to the beginning.
219 */
220 value_type*
221 8834 begin() noexcept
222 {
223 8834 return arr_;
224 }
225
226 /** Return an iterator to the beginning.
227 */
228 value_type const*
229 11018 begin() const noexcept
230 {
231 11018 return arr_;
232 }
233
234 /** Return an iterator to the end.
235 */
236 value_type*
237 8833 end() noexcept
238 {
239 8833 return arr_ + n_;
240 }
241
242 /** Return an iterator to the end.
243 */
244 value_type const*
245 11018 end() const noexcept
246 {
247 11018 return arr_ + n_;
248 }
249
250 /** Return a span of the buffers.
251 */
252 std::span<value_type>
253 19 to_span() noexcept
254 {
255 19 return { arr_, n_ };
256 }
257
258 /** Return a span of the buffers.
259 */
260 std::span<value_type const>
261 to_span() const noexcept
262 {
263 return { arr_, n_ };
264 }
265
266 /** Conversion to mutable span.
267 */
268 1 operator std::span<value_type>() noexcept
269 {
270 1 return { arr_, n_ };
271 }
272
273 /** Conversion to const span.
274 */
275 operator std::span<value_type const>() const noexcept
276 {
277 return { arr_, n_ };
278 }
279
280 /** Return the total byte count in O(1).
281 */
282 friend
283 std::size_t
284 5489 tag_invoke(
285 size_tag const&,
286 buffer_array const& ba) noexcept
287 {
288 5489 return ba.size_;
289 }
290
291 /** Slice customization point.
292 */
293 friend
294 void
295 2080 tag_invoke(
296 slice_tag const&,
297 buffer_array& ba,
298 slice_how how,
299 std::size_t n) noexcept
300 {
301 2080 ba.slice_impl(how, n);
302 2080 }
303
304 private:
305 void
306 2080 slice_impl(
307 slice_how how,
308 std::size_t n) noexcept
309 {
310
2/3
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 1056 times.
✗ Branch 2 not taken.
2080 switch(how)
311 {
312 1024 case slice_how::remove_prefix:
313 1024 remove_prefix_impl(n);
314 1024 break;
315
316 1056 case slice_how::keep_prefix:
317 1056 keep_prefix_impl(n);
318 1056 break;
319 }
320 2080 }
321
322 void
323 1024 remove_prefix_impl(std::size_t n) noexcept
324 {
325 1024 detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
326 1024 }
327
328 void
329 1056 keep_prefix_impl(std::size_t n) noexcept
330 {
331 1056 detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
332 1056 }
333 };
334
335 //------------------------------------------------
336
337 /** Alias for buffer_array holding const_buffer.
338
339 @tparam N Maximum number of buffers.
340 */
341 template<std::size_t N>
342 using const_buffer_array = buffer_array<N, true>;
343
344 /** Alias for buffer_array holding mutable_buffer.
345
346 @tparam N Maximum number of buffers.
347 */
348 template<std::size_t N>
349 using mutable_buffer_array = buffer_array<N, false>;
350
351 } // namespace capy
352 } // namespace boost
353
354 #endif
355