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 -
    @par Example
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 +

69  
    @code
73  
    @code
70  
    void process(ConstBufferSequence auto const& buffers)
74  
    void process(ConstBufferSequence auto const& buffers)
71  
    {
75  
    {
72  
        const_buffer_array<4> bufs(buffers);
76  
        const_buffer_array<4> bufs(buffers);
73  
        // use bufs.begin(), bufs.end(), bufs.to_span()
77  
        // use bufs.begin(), bufs.end(), bufs.to_span()
74  
    }
78  
    }
75 -

 
76 -
    @tparam N Maximum number of buffers the array can hold.
 
77 -
    @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
 
78  
    @endcode
79  
    @endcode
79  
*/
80  
*/
80  
template<std::size_t N, bool IsConst>
81  
template<std::size_t N, bool IsConst>
81  
class buffer_array
82  
class buffer_array
82  
{
83  
{
83  
public:
84  
public:
84  
    /** The type of buffer stored in the array.
85  
    /** The type of buffer stored in the array.
85  
    */
86  
    */
86  
    using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
87  
    using value_type = std::conditional_t<IsConst, const_buffer, mutable_buffer>;
87  

88  

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

96  

96  
public:
97  
public:
97 -
    /** Construct a default instance.
98 +
    /** Default constructor.
98  

99  

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

106  

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

116  

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

118  

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

131  

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

133  

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

138  

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

161  

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

163  

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

166  

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

168  

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

192  

192  
    /** Construct from an iterator range.
193  
    /** Construct from an iterator range.
193  

194  

194  
        Copies up to N non-empty buffer descriptors from the
195  
        Copies up to N non-empty buffer descriptors from the
195  
        range `[first, last)`. If the range contains more than
196  
        range `[first, last)`. If the range contains more than
196  
        N non-empty buffers, excess buffers are silently ignored.
197  
        N non-empty buffers, excess buffers are silently ignored.
197  

198  

198  
        @param first Iterator to the first buffer descriptor.
199  
        @param first Iterator to the first buffer descriptor.
199  
        @param last Iterator past the last buffer descriptor.
200  
        @param last Iterator past the last buffer descriptor.
200  
    */
201  
    */
201  
    template<class Iterator>
202  
    template<class Iterator>
202  
    buffer_array(Iterator first, Iterator last) noexcept
203  
    buffer_array(Iterator first, Iterator last) noexcept
203  
        : dummy_(0)
204  
        : dummy_(0)
204  
    {
205  
    {
205  
        while(first != last && n_ < N)
206  
        while(first != last && n_ < N)
206  
        {
207  
        {
207  
            value_type b(*first);
208  
            value_type b(*first);
208  
            if(b.size() != 0)
209  
            if(b.size() != 0)
209  
            {
210  
            {
210  
                ::new(&arr_[n_++]) value_type(b);
211  
                ::new(&arr_[n_++]) value_type(b);
211  
                size_ += b.size();
212  
                size_ += b.size();
212  
            }
213  
            }
213  
            ++first;
214  
            ++first;
214  
        }
215  
        }
215  
    }
216  
    }
216  

217  

217  
    /** Construct from an iterator range with overflow checking.
218  
    /** Construct from an iterator range with overflow checking.
218  

219  

219  
        Copies all non-empty buffer descriptors from the range
220  
        Copies all non-empty buffer descriptors from the range
220  
        `[first, last)` into the internal array.
221  
        `[first, last)` into the internal array.
221  

222  

222  
        @param first Iterator to the first buffer descriptor.
223  
        @param first Iterator to the first buffer descriptor.
223  
        @param last Iterator past the last buffer descriptor.
224  
        @param last Iterator past the last buffer descriptor.
224  

225  

225  
        @throws std::length_error if the range contains more
226  
        @throws std::length_error if the range contains more
226  
        than N non-empty buffers.
227  
        than N non-empty buffers.
227  
    */
228  
    */
228  
    template<class Iterator>
229  
    template<class Iterator>
229  
    buffer_array(std::in_place_t, Iterator first, Iterator last)
230  
    buffer_array(std::in_place_t, Iterator first, Iterator last)
230  
        : dummy_(0)
231  
        : dummy_(0)
231  
    {
232  
    {
232  
        while(first != last)
233  
        while(first != last)
233  
        {
234  
        {
234  
            value_type b(*first);
235  
            value_type b(*first);
235  
            if(b.size() != 0)
236  
            if(b.size() != 0)
236  
            {
237  
            {
237  
                if(n_ >= N)
238  
                if(n_ >= N)
238  
                    detail::throw_length_error();
239  
                    detail::throw_length_error();
239  
                ::new(&arr_[n_++]) value_type(b);
240  
                ::new(&arr_[n_++]) value_type(b);
240  
                size_ += b.size();
241  
                size_ += b.size();
241  
            }
242  
            }
242  
            ++first;
243  
            ++first;
243  
        }
244  
        }
244  
    }
245  
    }
245  

246  

246  
    /** Destructor.
247  
    /** Destructor.
247  
    */
248  
    */
248  
    ~buffer_array()
249  
    ~buffer_array()
249  
    {
250  
    {
250  
        while(n_--)
251  
        while(n_--)
251  
            arr_[n_].~value_type();
252  
            arr_[n_].~value_type();
252  
    }
253  
    }
253  

254  

254 -
    /** Assign by copying.
255 +
    /** Copy assignment.
255  
    */
256  
    */
256  
    buffer_array&
257  
    buffer_array&
257  
    operator=(buffer_array const& other) noexcept
258  
    operator=(buffer_array const& other) noexcept
258  
    {
259  
    {
259  
        if(this != &other)
260  
        if(this != &other)
260  
        {
261  
        {
261  
            while(n_--)
262  
            while(n_--)
262  
                arr_[n_].~value_type();
263  
                arr_[n_].~value_type();
263  
            n_ = other.n_;
264  
            n_ = other.n_;
264  
            size_ = other.size_;
265  
            size_ = other.size_;
265  
            for(std::size_t i = 0; i < n_; ++i)
266  
            for(std::size_t i = 0; i < n_; ++i)
266  
                ::new(&arr_[i]) value_type(other.arr_[i]);
267  
                ::new(&arr_[i]) value_type(other.arr_[i]);
267  
        }
268  
        }
268  
        return *this;
269  
        return *this;
269  
    }
270  
    }
270  

271  

271  
    /** Return an iterator to the beginning.
272  
    /** Return an iterator to the beginning.
272  
    */
273  
    */
273  
    value_type*
274  
    value_type*
274  
    begin() noexcept
275  
    begin() noexcept
275  
    {
276  
    {
276  
        return arr_;
277  
        return arr_;
277  
    }
278  
    }
278  

279  

279  
    /** Return an iterator to the beginning.
280  
    /** Return an iterator to the beginning.
280  
    */
281  
    */
281  
    value_type const*
282  
    value_type const*
282  
    begin() const noexcept
283  
    begin() const noexcept
283  
    {
284  
    {
284  
        return arr_;
285  
        return arr_;
285  
    }
286  
    }
286  

287  

287  
    /** Return an iterator to the end.
288  
    /** Return an iterator to the end.
288  
    */
289  
    */
289  
    value_type*
290  
    value_type*
290  
    end() noexcept
291  
    end() noexcept
291  
    {
292  
    {
292  
        return arr_ + n_;
293  
        return arr_ + n_;
293  
    }
294  
    }
294  

295  

295  
    /** Return an iterator to the end.
296  
    /** Return an iterator to the end.
296  
    */
297  
    */
297  
    value_type const*
298  
    value_type const*
298  
    end() const noexcept
299  
    end() const noexcept
299  
    {
300  
    {
300  
        return arr_ + n_;
301  
        return arr_ + n_;
301  
    }
302  
    }
302  

303  

303  
    /** Return a span of the buffers.
304  
    /** Return a span of the buffers.
304  
    */
305  
    */
305  
    std::span<value_type>
306  
    std::span<value_type>
306  
    to_span() noexcept
307  
    to_span() noexcept
307  
    {
308  
    {
308  
        return { arr_, n_ };
309  
        return { arr_, n_ };
309  
    }
310  
    }
310  

311  

311  
    /** Return a span of the buffers.
312  
    /** Return a span of the buffers.
312  
    */
313  
    */
313  
    std::span<value_type const>
314  
    std::span<value_type const>
314  
    to_span() const noexcept
315  
    to_span() const noexcept
315  
    {
316  
    {
316  
        return { arr_, n_ };
317  
        return { arr_, n_ };
317  
    }
318  
    }
318  

319  

319  
    /** Conversion to mutable span.
320  
    /** Conversion to mutable span.
320  
    */
321  
    */
321  
    operator std::span<value_type>() noexcept
322  
    operator std::span<value_type>() noexcept
322  
    {
323  
    {
323  
        return { arr_, n_ };
324  
        return { arr_, n_ };
324  
    }
325  
    }
325  

326  

326  
    /** Conversion to const span.
327  
    /** Conversion to const span.
327  
    */
328  
    */
328  
    operator std::span<value_type const>() const noexcept
329  
    operator std::span<value_type const>() const noexcept
329  
    {
330  
    {
330  
        return { arr_, n_ };
331  
        return { arr_, n_ };
331  
    }
332  
    }
332  

333  

333  
    /** Return the total byte count in O(1).
334  
    /** Return the total byte count in O(1).
334  
    */
335  
    */
335  
    friend
336  
    friend
336  
    std::size_t
337  
    std::size_t
337  
    tag_invoke(
338  
    tag_invoke(
338  
        size_tag const&,
339  
        size_tag const&,
339  
        buffer_array const& ba) noexcept
340  
        buffer_array const& ba) noexcept
340  
    {
341  
    {
341  
        return ba.size_;
342  
        return ba.size_;
342  
    }
343  
    }
343  

344  

344  
    /** Slice customization point.
345  
    /** Slice customization point.
345  
    */
346  
    */
346  
    friend
347  
    friend
347  
    void
348  
    void
348  
    tag_invoke(
349  
    tag_invoke(
349  
        slice_tag const&,
350  
        slice_tag const&,
350  
        buffer_array& ba,
351  
        buffer_array& ba,
351  
        slice_how how,
352  
        slice_how how,
352  
        std::size_t n) noexcept
353  
        std::size_t n) noexcept
353  
    {
354  
    {
354  
        ba.slice_impl(how, n);
355  
        ba.slice_impl(how, n);
355  
    }
356  
    }
356  

357  

357  
private:
358  
private:
358  
    void
359  
    void
359  
    slice_impl(
360  
    slice_impl(
360  
        slice_how how,
361  
        slice_how how,
361  
        std::size_t n) noexcept
362  
        std::size_t n) noexcept
362  
    {
363  
    {
363  
        switch(how)
364  
        switch(how)
364  
        {
365  
        {
365  
        case slice_how::remove_prefix:
366  
        case slice_how::remove_prefix:
366  
            remove_prefix_impl(n);
367  
            remove_prefix_impl(n);
367  
            break;
368  
            break;
368  

369  

369  
        case slice_how::keep_prefix:
370  
        case slice_how::keep_prefix:
370  
            keep_prefix_impl(n);
371  
            keep_prefix_impl(n);
371  
            break;
372  
            break;
372  
        }
373  
        }
373  
    }
374  
    }
374  

375  

375  
    void
376  
    void
376  
    remove_prefix_impl(std::size_t n) noexcept
377  
    remove_prefix_impl(std::size_t n) noexcept
377  
    {
378  
    {
378  
        detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
379  
        detail::buffer_array_remove_prefix(arr_, &n_, &size_, n);
379  
    }
380  
    }
380  

381  

381  
    void
382  
    void
382  
    keep_prefix_impl(std::size_t n) noexcept
383  
    keep_prefix_impl(std::size_t n) noexcept
383  
    {
384  
    {
384  
        detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
385  
        detail::buffer_array_keep_prefix(arr_, &n_, &size_, n);
385  
    }
386  
    }
386  
};
387  
};
 
388 +

 
389 +
//------------------------------------------------
387  

390  

388  
/** Alias for buffer_array holding const_buffer.
391  
/** Alias for buffer_array holding const_buffer.
389  

392  

390  
    @tparam N Maximum number of buffers.
393  
    @tparam N Maximum number of buffers.
391  
*/
394  
*/
392  
template<std::size_t N>
395  
template<std::size_t N>
393  
using const_buffer_array = buffer_array<N, true>;
396  
using const_buffer_array = buffer_array<N, true>;
394  

397  

395  
/** Alias for buffer_array holding mutable_buffer.
398  
/** Alias for buffer_array holding mutable_buffer.
396  

399  

397  
    @tparam N Maximum number of buffers.
400  
    @tparam N Maximum number of buffers.
398  
*/
401  
*/
399  
template<std::size_t N>
402  
template<std::size_t N>
400  
using mutable_buffer_array = buffer_array<N, false>;
403  
using mutable_buffer_array = buffer_array<N, false>;
401  

404  

402  
} // namespace capy
405  
} // namespace capy
403  
} // namespace boost
406  
} // namespace boost
404  

407  

405  
#endif
408  
#endif