TLA 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 : @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 HIT 6 : buffer_array() noexcept
102 6 : : dummy_(0)
103 : {
104 6 : }
105 :
106 : /** Construct a copy.
107 : */
108 4644 : buffer_array(buffer_array const& other) noexcept
109 4644 : : n_(other.n_)
110 4644 : , size_(other.size_)
111 : {
112 12123 : for(std::size_t i = 0; i < n_; ++i)
113 7479 : ::new(&arr_[i]) value_type(other.arr_[i]);
114 4644 : }
115 :
116 : /** Construct from a single buffer.
117 :
118 : @param b The buffer to store.
119 : */
120 130 : buffer_array(value_type const& b) noexcept
121 130 : : dummy_(0)
122 : {
123 130 : if(b.size() != 0)
124 : {
125 122 : ::new(&arr_[0]) value_type(b);
126 122 : n_ = 1;
127 122 : size_ = b.size();
128 : }
129 130 : }
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 185 : buffer_array(BS const& bs) noexcept
145 185 : : dummy_(0)
146 : {
147 185 : auto it = capy::begin(bs);
148 185 : auto const last = capy::end(bs);
149 618 : while(it != last && n_ < N)
150 : {
151 433 : value_type b(*it);
152 433 : if(b.size() != 0)
153 : {
154 427 : ::new(&arr_[n_++]) value_type(b);
155 427 : size_ += b.size();
156 : }
157 433 : ++it;
158 : }
159 185 : }
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 4 : buffer_array(std::in_place_t, BS const& bs)
174 4 : : dummy_(0)
175 : {
176 4 : auto it = capy::begin(bs);
177 4 : auto const last = capy::end(bs);
178 14 : while(it != last)
179 : {
180 12 : value_type b(*it);
181 12 : if(b.size() != 0)
182 : {
183 12 : if(n_ >= N)
184 2 : detail::throw_length_error();
185 10 : ::new(&arr_[n_++]) value_type(b);
186 10 : size_ += b.size();
187 : }
188 10 : ++it;
189 : }
190 2 : }
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 8 : buffer_array(Iterator first, Iterator last) noexcept
203 8 : : dummy_(0)
204 : {
205 26 : while(first != last && n_ < N)
206 : {
207 18 : value_type b(*first);
208 18 : if(b.size() != 0)
209 : {
210 14 : ::new(&arr_[n_++]) value_type(b);
211 14 : size_ += b.size();
212 : }
213 18 : ++first;
214 : }
215 8 : }
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 4 : buffer_array(std::in_place_t, Iterator first, Iterator last)
230 4 : : dummy_(0)
231 : {
232 14 : while(first != last)
233 : {
234 12 : value_type b(*first);
235 12 : if(b.size() != 0)
236 : {
237 12 : if(n_ >= N)
238 2 : detail::throw_length_error();
239 10 : ::new(&arr_[n_++]) value_type(b);
240 10 : size_ += b.size();
241 : }
242 10 : ++first;
243 : }
244 2 : }
245 :
246 : /** Destructor.
247 : */
248 4977 : ~buffer_array()
249 : {
250 11837 : while(n_--)
251 6860 : arr_[n_].~value_type();
252 4977 : }
253 :
254 : /** Assign by copying.
255 : */
256 : buffer_array&
257 4 : operator=(buffer_array const& other) noexcept
258 : {
259 4 : if(this != &other)
260 : {
261 4 : while(n_--)
262 MIS 0 : arr_[n_].~value_type();
263 HIT 4 : n_ = other.n_;
264 4 : size_ = other.size_;
265 10 : for(std::size_t i = 0; i < n_; ++i)
266 6 : ::new(&arr_[i]) value_type(other.arr_[i]);
267 : }
268 4 : return *this;
269 : }
270 :
271 : /** Return an iterator to the beginning.
272 : */
273 : value_type*
274 8834 : begin() noexcept
275 : {
276 8834 : return arr_;
277 : }
278 :
279 : /** Return an iterator to the beginning.
280 : */
281 : value_type const*
282 11022 : begin() const noexcept
283 : {
284 11022 : return arr_;
285 : }
286 :
287 : /** Return an iterator to the end.
288 : */
289 : value_type*
290 8833 : end() noexcept
291 : {
292 8833 : return arr_ + n_;
293 : }
294 :
295 : /** Return an iterator to the end.
296 : */
297 : value_type const*
298 11022 : end() const noexcept
299 : {
300 11022 : return arr_ + n_;
301 : }
302 :
303 : /** Return a span of the buffers.
304 : */
305 : std::span<value_type>
306 379 : to_span() noexcept
307 : {
308 379 : return { arr_, n_ };
309 : }
310 :
311 : /** Return a span of the buffers.
312 : */
313 : std::span<value_type const>
314 175 : to_span() const noexcept
315 : {
316 175 : return { arr_, n_ };
317 : }
318 :
319 : /** Conversion to mutable span.
320 : */
321 1 : operator std::span<value_type>() noexcept
322 : {
323 1 : 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 5499 : tag_invoke(
338 : size_tag const&,
339 : buffer_array const& ba) noexcept
340 : {
341 5499 : return ba.size_;
342 : }
343 :
344 : /** Slice customization point.
345 : */
346 : friend
347 : void
348 2080 : tag_invoke(
349 : slice_tag const&,
350 : buffer_array& ba,
351 : slice_how how,
352 : std::size_t n) noexcept
353 : {
354 2080 : ba.slice_impl(how, n);
355 2080 : }
356 :
357 : private:
358 : void
359 2080 : slice_impl(
360 : slice_how how,
361 : std::size_t n) noexcept
362 : {
363 2080 : switch(how)
364 : {
365 1024 : case slice_how::remove_prefix:
366 1024 : remove_prefix_impl(n);
367 1024 : break;
368 :
369 1056 : case slice_how::keep_prefix:
370 1056 : keep_prefix_impl(n);
371 1056 : break;
372 : }
373 2080 : }
374 :
375 : void
376 1024 : remove_prefix_impl(std::size_t n) noexcept
377 : {
378 1024 : detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
379 1024 : }
380 :
381 : void
382 1056 : keep_prefix_impl(std::size_t n) noexcept
383 : {
384 1056 : detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
385 1056 : }
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
|