1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_BUFFERS_BUFFER_ARRAY_HPP
10  
#ifndef BOOST_CAPY_BUFFERS_BUFFER_ARRAY_HPP
11  
#define BOOST_CAPY_BUFFERS_BUFFER_ARRAY_HPP
11  
#define BOOST_CAPY_BUFFERS_BUFFER_ARRAY_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/detail/except.hpp>
14  
#include <boost/capy/detail/except.hpp>
15  
#include <boost/capy/buffers.hpp>
15  
#include <boost/capy/buffers.hpp>
16  

16  

17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <new>
18  
#include <new>
19  
#include <span>
19  
#include <span>
20  
#include <utility>
20  
#include <utility>
21  

21  

22  
namespace boost {
22  
namespace boost {
23  
namespace capy {
23  
namespace capy {
24  

24  

25  
namespace detail {
25  
namespace detail {
26  

26  

27  
BOOST_CAPY_DECL
27  
BOOST_CAPY_DECL
28  
void
28  
void
29  
buffer_array_remove_prefix(
29  
buffer_array_remove_prefix(
30  
    const_buffer* arr,
30  
    const_buffer* arr,
31  
    std::size_t* count,
31  
    std::size_t* count,
32  
    std::size_t* total_size,
32  
    std::size_t* total_size,
33  
    std::size_t n) noexcept;
33  
    std::size_t n) noexcept;
34  

34  

35  
BOOST_CAPY_DECL
35  
BOOST_CAPY_DECL
36  
void
36  
void
37  
buffer_array_remove_prefix(
37  
buffer_array_remove_prefix(
38  
    mutable_buffer* arr,
38  
    mutable_buffer* arr,
39  
    std::size_t* count,
39  
    std::size_t* count,
40  
    std::size_t* total_size,
40  
    std::size_t* total_size,
41  
    std::size_t n) noexcept;
41  
    std::size_t n) noexcept;
42  

42  

43  
BOOST_CAPY_DECL
43  
BOOST_CAPY_DECL
44  
void
44  
void
45  
buffer_array_keep_prefix(
45  
buffer_array_keep_prefix(
46  
    const_buffer* arr,
46  
    const_buffer* arr,
47  
    std::size_t* count,
47  
    std::size_t* count,
48  
    std::size_t* total_size,
48  
    std::size_t* total_size,
49  
    std::size_t n) noexcept;
49  
    std::size_t n) noexcept;
50  

50  

51  
BOOST_CAPY_DECL
51  
BOOST_CAPY_DECL
52  
void
52  
void
53  
buffer_array_keep_prefix(
53  
buffer_array_keep_prefix(
54  
    mutable_buffer* arr,
54  
    mutable_buffer* arr,
55  
    std::size_t* count,
55  
    std::size_t* count,
56  
    std::size_t* total_size,
56  
    std::size_t* total_size,
57  
    std::size_t n) noexcept;
57  
    std::size_t n) noexcept;
58  

58  

59  
} // namespace detail
59  
} // namespace detail
60  

60  

61  
/** A buffer sequence holding up to N buffers.
61  
/** A buffer sequence holding up to N buffers.
62  

62  

63  
    This class template stores a fixed-capacity array of buffer
63  
    This class template stores a fixed-capacity array of buffer
64  
    descriptors, where the actual count can vary from 0 to N.
64  
    descriptors, where the actual count can vary from 0 to N.
65  
    It provides efficient storage for small buffer sequences
65  
    It provides efficient storage for small buffer sequences
66  
    without dynamic allocation.
66  
    without dynamic allocation.
67  

67  

68  
    @tparam N Maximum number of buffers the array can hold.
68  
    @tparam N Maximum number of buffers the array can hold.
69  
    @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
69  
    @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
70  

70  

71  
    @par Usage
71  
    @par Usage
72  

72  

73  
    @code
73  
    @code
74  
    void process(ConstBufferSequence auto const& buffers)
74  
    void process(ConstBufferSequence auto const& buffers)
75  
    {
75  
    {
76  
        const_buffer_array<4> bufs(buffers);
76  
        const_buffer_array<4> bufs(buffers);
77  
        // use bufs.begin(), bufs.end(), bufs.to_span()
77  
        // use bufs.begin(), bufs.end(), bufs.to_span()
78  
    }
78  
    }
79  
    @endcode
79  
    @endcode
80  
*/
80  
*/
81  
template<std::size_t N, bool IsConst>
81  
template<std::size_t N, bool IsConst>
82  
class buffer_array
82  
class buffer_array
83  
{
83  
{
84  
public:
84  
public:
85  
    /** The type of buffer stored in the array.
85  
    /** The type of buffer stored in the array.
86  
    */
86  
    */
87  
    using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
87  
    using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
88  

88  

89  
private:
89  
private:
90  
    std::size_t n_ = 0;
90  
    std::size_t n_ = 0;
91  
    std::size_t size_ = 0;
91  
    std::size_t size_ = 0;
92  
    union {
92  
    union {
93  
        int dummy_;
93  
        int dummy_;
94  
        value_type arr_[N];
94  
        value_type arr_[N];
95  
    };
95  
    };
96  

96  

97  
public:
97  
public:
98  
    /** Default constructor.
98  
    /** Default constructor.
99  

99  

100  
        Constructs an empty buffer array.
100  
        Constructs an empty buffer array.
101  
    */
101  
    */
102  
    buffer_array() noexcept
102  
    buffer_array() noexcept
103  
        : dummy_(0)
103  
        : dummy_(0)
104  
    {
104  
    {
105  
    }
105  
    }
106  

106  

107  
    /** Copy constructor.
107  
    /** Copy constructor.
108  
    */
108  
    */
109  
    buffer_array(buffer_array const& other) noexcept
109  
    buffer_array(buffer_array const& other) noexcept
110  
        : n_(other.n_)
110  
        : n_(other.n_)
111  
        , size_(other.size_)
111  
        , size_(other.size_)
112  
    {
112  
    {
113  
        for(std::size_t i = 0; i < n_; ++i)
113  
        for(std::size_t i = 0; i < n_; ++i)
114  
            ::new(&arr_[i]) value_type(other.arr_[i]);
114  
            ::new(&arr_[i]) value_type(other.arr_[i]);
115  
    }
115  
    }
116  

116  

117  
    /** Construct from a single buffer.
117  
    /** Construct from a single buffer.
118  

118  

119  
        @param b The buffer to store.
119  
        @param b The buffer to store.
120  
    */
120  
    */
121  
    buffer_array(value_type const& b) noexcept
121  
    buffer_array(value_type const& b) noexcept
122  
        : dummy_(0)
122  
        : dummy_(0)
123  
    {
123  
    {
124  
        if(b.size() != 0)
124  
        if(b.size() != 0)
125  
        {
125  
        {
126  
            ::new(&arr_[0]) value_type(b);
126  
            ::new(&arr_[0]) value_type(b);
127  
            n_ = 1;
127  
            n_ = 1;
128  
            size_ = b.size();
128  
            size_ = b.size();
129  
        }
129  
        }
130  
    }
130  
    }
131  

131  

132  
    /** Construct from a buffer sequence.
132  
    /** Construct from a buffer sequence.
133  

133  

134  
        Copies up to N buffer descriptors from the source
134  
        Copies up to N buffer descriptors from the source
135  
        sequence into the internal array. If the sequence
135  
        sequence into the internal array. If the sequence
136  
        contains more than N non-empty buffers, excess
136  
        contains more than N non-empty buffers, excess
137  
        buffers are silently ignored.
137  
        buffers are silently ignored.
138  

138  

139  
        @param bs The buffer sequence to copy from.
139  
        @param bs The buffer sequence to copy from.
140  
    */
140  
    */
141  
    template<class BS>
141  
    template<class BS>
142  
        requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
142  
        requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
143  
            && (!std::same_as<std::remove_cvref_t<BS>, buffer_array>)
143  
            && (!std::same_as<std::remove_cvref_t<BS>, buffer_array>)
144  
            && (!std::same_as<std::remove_cvref_t<BS>, value_type>)
144  
            && (!std::same_as<std::remove_cvref_t<BS>, value_type>)
145  
    buffer_array(BS const& bs) noexcept
145  
    buffer_array(BS const& bs) noexcept
146  
        : dummy_(0)
146  
        : dummy_(0)
147  
    {
147  
    {
148  
        auto it = capy::begin(bs);
148  
        auto it = capy::begin(bs);
149  
        auto const last = capy::end(bs);
149  
        auto const last = capy::end(bs);
150  
        while(it != last && n_ < N)
150  
        while(it != last && n_ < N)
151  
        {
151  
        {
152  
            value_type b(*it);
152  
            value_type b(*it);
153  
            if(b.size() != 0)
153  
            if(b.size() != 0)
154  
            {
154  
            {
155  
                ::new(&arr_[n_++]) value_type(b);
155  
                ::new(&arr_[n_++]) value_type(b);
156  
                size_ += b.size();
156  
                size_ += b.size();
157  
            }
157  
            }
158  
            ++it;
158  
            ++it;
159  
        }
159  
        }
160  
    }
160  
    }
161  

161  

162  
    /** Construct from a buffer sequence with overflow checking.
162  
    /** Construct from a buffer sequence with overflow checking.
163  

163  

164  
        Copies buffer descriptors from the source sequence
164  
        Copies buffer descriptors from the source sequence
165  
        into the internal array.
165  
        into the internal array.
166  

166  

167  
        @param bs The buffer sequence to copy from.
167  
        @param bs The buffer sequence to copy from.
168  

168  

169  
        @throws std::length_error if the sequence contains
169  
        @throws std::length_error if the sequence contains
170  
        more than N non-empty buffers.
170  
        more than N non-empty buffers.
171  
    */
171  
    */
172  
    template<class BS>
172  
    template<class BS>
173  
        requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
173  
        requires (IsConst ? ConstBufferSequence<BS> : MutableBufferSequence<BS>)
174  
    buffer_array(std::in_place_t, BS const& bs)
174  
    buffer_array(std::in_place_t, BS const& bs)
175  
        : dummy_(0)
175  
        : dummy_(0)
176  
    {
176  
    {
177  
        auto it = capy::begin(bs);
177  
        auto it = capy::begin(bs);
178  
        auto const last = capy::end(bs);
178  
        auto const last = capy::end(bs);
179  
        while(it != last)
179  
        while(it != last)
180  
        {
180  
        {
181  
            value_type b(*it);
181  
            value_type b(*it);
182  
            if(b.size() != 0)
182  
            if(b.size() != 0)
183  
            {
183  
            {
184  
                if(n_ >= N)
184  
                if(n_ >= N)
185  
                    detail::throw_length_error();
185  
                    detail::throw_length_error();
186  
                ::new(&arr_[n_++]) value_type(b);
186  
                ::new(&arr_[n_++]) value_type(b);
187  
                size_ += b.size();
187  
                size_ += b.size();
188  
            }
188  
            }
189  
            ++it;
189  
            ++it;
190  
        }
190  
        }
191  
    }
191  
    }
192  

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 +
    buffer_array(Iterator first, Iterator last) noexcept
 
204 +
        : dummy_(0)
 
205 +
    {
 
206 +
        while(first != last && n_ < N)
 
207 +
        {
 
208 +
            value_type b(*first);
 
209 +
            if(b.size() != 0)
 
210 +
            {
 
211 +
                ::new(&arr_[n_++]) value_type(b);
 
212 +
                size_ += b.size();
 
213 +
            }
 
214 +
            ++first;
 
215 +
        }
 
216 +
    }
 
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 +
    buffer_array(std::in_place_t, Iterator first, Iterator last)
 
231 +
        : dummy_(0)
 
232 +
    {
 
233 +
        while(first != last)
 
234 +
        {
 
235 +
            value_type b(*first);
 
236 +
            if(b.size() != 0)
 
237 +
            {
 
238 +
                if(n_ >= N)
 
239 +
                    detail::throw_length_error();
 
240 +
                ::new(&arr_[n_++]) value_type(b);
 
241 +
                size_ += b.size();
 
242 +
            }
 
243 +
            ++first;
 
244 +
        }
 
245 +
    }
 
246 +

193  
    /** Destructor.
247  
    /** Destructor.
194  
    */
248  
    */
195  
    ~buffer_array()
249  
    ~buffer_array()
196  
    {
250  
    {
197  
        while(n_--)
251  
        while(n_--)
198  
            arr_[n_].~value_type();
252  
            arr_[n_].~value_type();
199  
    }
253  
    }
200  

254  

201  
    /** Copy assignment.
255  
    /** Copy assignment.
202  
    */
256  
    */
203  
    buffer_array&
257  
    buffer_array&
204  
    operator=(buffer_array const& other) noexcept
258  
    operator=(buffer_array const& other) noexcept
205  
    {
259  
    {
206  
        if(this != &other)
260  
        if(this != &other)
207  
        {
261  
        {
208  
            while(n_--)
262  
            while(n_--)
209  
                arr_[n_].~value_type();
263  
                arr_[n_].~value_type();
210  
            n_ = other.n_;
264  
            n_ = other.n_;
211  
            size_ = other.size_;
265  
            size_ = other.size_;
212  
            for(std::size_t i = 0; i < n_; ++i)
266  
            for(std::size_t i = 0; i < n_; ++i)
213  
                ::new(&arr_[i]) value_type(other.arr_[i]);
267  
                ::new(&arr_[i]) value_type(other.arr_[i]);
214  
        }
268  
        }
215  
        return *this;
269  
        return *this;
216  
    }
270  
    }
217  

271  

218  
    /** Return an iterator to the beginning.
272  
    /** Return an iterator to the beginning.
219  
    */
273  
    */
220  
    value_type*
274  
    value_type*
221  
    begin() noexcept
275  
    begin() noexcept
222  
    {
276  
    {
223  
        return arr_;
277  
        return arr_;
224  
    }
278  
    }
225  

279  

226  
    /** Return an iterator to the beginning.
280  
    /** Return an iterator to the beginning.
227  
    */
281  
    */
228  
    value_type const*
282  
    value_type const*
229  
    begin() const noexcept
283  
    begin() const noexcept
230  
    {
284  
    {
231  
        return arr_;
285  
        return arr_;
232  
    }
286  
    }
233  

287  

234  
    /** Return an iterator to the end.
288  
    /** Return an iterator to the end.
235  
    */
289  
    */
236  
    value_type*
290  
    value_type*
237  
    end() noexcept
291  
    end() noexcept
238  
    {
292  
    {
239  
        return arr_ + n_;
293  
        return arr_ + n_;
240  
    }
294  
    }
241  

295  

242  
    /** Return an iterator to the end.
296  
    /** Return an iterator to the end.
243  
    */
297  
    */
244  
    value_type const*
298  
    value_type const*
245  
    end() const noexcept
299  
    end() const noexcept
246  
    {
300  
    {
247  
        return arr_ + n_;
301  
        return arr_ + n_;
248  
    }
302  
    }
249  

303  

250  
    /** Return a span of the buffers.
304  
    /** Return a span of the buffers.
251  
    */
305  
    */
252  
    std::span<value_type>
306  
    std::span<value_type>
253  
    to_span() noexcept
307  
    to_span() noexcept
254  
    {
308  
    {
255  
        return { arr_, n_ };
309  
        return { arr_, n_ };
256  
    }
310  
    }
257  

311  

258  
    /** Return a span of the buffers.
312  
    /** Return a span of the buffers.
259  
    */
313  
    */
260  
    std::span<value_type const>
314  
    std::span<value_type const>
261  
    to_span() const noexcept
315  
    to_span() const noexcept
262  
    {
316  
    {
263  
        return { arr_, n_ };
317  
        return { arr_, n_ };
264  
    }
318  
    }
265  

319  

266  
    /** Conversion to mutable span.
320  
    /** Conversion to mutable span.
267  
    */
321  
    */
268  
    operator std::span<value_type>() noexcept
322  
    operator std::span<value_type>() noexcept
269  
    {
323  
    {
270  
        return { arr_, n_ };
324  
        return { arr_, n_ };
271  
    }
325  
    }
272  

326  

273  
    /** Conversion to const span.
327  
    /** Conversion to const span.
274  
    */
328  
    */
275  
    operator std::span<value_type const>() const noexcept
329  
    operator std::span<value_type const>() const noexcept
276  
    {
330  
    {
277  
        return { arr_, n_ };
331  
        return { arr_, n_ };
278  
    }
332  
    }
279  

333  

280  
    /** Return the total byte count in O(1).
334  
    /** Return the total byte count in O(1).
281  
    */
335  
    */
282  
    friend
336  
    friend
283  
    std::size_t
337  
    std::size_t
284  
    tag_invoke(
338  
    tag_invoke(
285  
        size_tag const&,
339  
        size_tag const&,
286  
        buffer_array const& ba) noexcept
340  
        buffer_array const& ba) noexcept
287  
    {
341  
    {
288  
        return ba.size_;
342  
        return ba.size_;
289  
    }
343  
    }
290  

344  

291  
    /** Slice customization point.
345  
    /** Slice customization point.
292  
    */
346  
    */
293  
    friend
347  
    friend
294  
    void
348  
    void
295  
    tag_invoke(
349  
    tag_invoke(
296  
        slice_tag const&,
350  
        slice_tag const&,
297  
        buffer_array& ba,
351  
        buffer_array& ba,
298  
        slice_how how,
352  
        slice_how how,
299  
        std::size_t n) noexcept
353  
        std::size_t n) noexcept
300  
    {
354  
    {
301  
        ba.slice_impl(how, n);
355  
        ba.slice_impl(how, n);
302  
    }
356  
    }
303  

357  

304  
private:
358  
private:
305  
    void
359  
    void
306  
    slice_impl(
360  
    slice_impl(
307  
        slice_how how,
361  
        slice_how how,
308  
        std::size_t n) noexcept
362  
        std::size_t n) noexcept
309  
    {
363  
    {
310  
        switch(how)
364  
        switch(how)
311  
        {
365  
        {
312  
        case slice_how::remove_prefix:
366  
        case slice_how::remove_prefix:
313  
            remove_prefix_impl(n);
367  
            remove_prefix_impl(n);
314  
            break;
368  
            break;
315  

369  

316  
        case slice_how::keep_prefix:
370  
        case slice_how::keep_prefix:
317  
            keep_prefix_impl(n);
371  
            keep_prefix_impl(n);
318  
            break;
372  
            break;
319  
        }
373  
        }
320  
    }
374  
    }
321  

375  

322  
    void
376  
    void
323  
    remove_prefix_impl(std::size_t n) noexcept
377  
    remove_prefix_impl(std::size_t n) noexcept
324  
    {
378  
    {
325  
        detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
379  
        detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
326  
    }
380  
    }
327  

381  

328  
    void
382  
    void
329  
    keep_prefix_impl(std::size_t n) noexcept
383  
    keep_prefix_impl(std::size_t n) noexcept
330  
    {
384  
    {
331  
        detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
385  
        detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
332  
    }
386  
    }
333  
};
387  
};
334  

388  

335  
//------------------------------------------------
389  
//------------------------------------------------
336  

390  

337  
/** Alias for buffer_array holding const_buffer.
391  
/** Alias for buffer_array holding const_buffer.
338  

392  

339  
    @tparam N Maximum number of buffers.
393  
    @tparam N Maximum number of buffers.
340  
*/
394  
*/
341  
template<std::size_t N>
395  
template<std::size_t N>
342  
using const_buffer_array = buffer_array<N, true>;
396  
using const_buffer_array = buffer_array<N, true>;
343  

397  

344  
/** Alias for buffer_array holding mutable_buffer.
398  
/** Alias for buffer_array holding mutable_buffer.
345  

399  

346  
    @tparam N Maximum number of buffers.
400  
    @tparam N Maximum number of buffers.
347  
*/
401  
*/
348  
template<std::size_t N>
402  
template<std::size_t N>
349  
using mutable_buffer_array = buffer_array<N, false>;
403  
using mutable_buffer_array = buffer_array<N, false>;
350  

404  

351  
} // namespace capy
405  
} // namespace capy
352  
} // namespace boost
406  
} // namespace boost
353  

407  

354  
#endif
408  
#endif