Line data 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 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 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 618 : while(it != last && n_ < N)
151 : {
152 433 : value_type b(*it);
153 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 14 : while(it != last)
180 : {
181 12 : value_type b(*it);
182 12 : if(b.size() != 0)
183 : {
184 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 26 : while(first != last && n_ < N)
207 : {
208 18 : value_type b(*first);
209 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 14 : while(first != last)
234 : {
235 12 : value_type b(*first);
236 12 : if(b.size() != 0)
237 : {
238 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 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 4 : if(this != &other)
261 : {
262 4 : while(n_--)
263 0 : arr_[n_].~value_type();
264 4 : n_ = other.n_;
265 4 : size_ = other.size_;
266 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 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
|