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 4386 : buffer_array(buffer_array const& other) noexcept
110 4386 : : n_(other.n_)
111 4386 : , size_(other.size_)
112 : {
113 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 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 116 : while(it != last && n_ < N)
151 : {
152 78 : value_type b(*it);
153 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 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 : /** Destructor.
194 : */
195 4443 : ~buffer_array()
196 : {
197 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 4 : if(this != &other)
207 : {
208 4 : while(n_--)
209 0 : arr_[n_].~value_type();
210 4 : n_ = other.n_;
211 4 : size_ = other.size_;
212 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 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
|