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 +
    @par Example
69 -
    @tparam IsConst If true, holds const_buffer; otherwise mutable_buffer.
 
70 -

 
71 -
    @par Usage
 
72 -

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

87  

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

95  

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

98  

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

105  

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

115  

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

117  

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

130  

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

132  

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

137  

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

160  

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

162  

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

165  

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

167  

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

191  

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

193  

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

197  

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

216  

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

218  

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

221  

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

224  

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

245  

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

253  

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

270  

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

278  

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

286  

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

294  

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

302  

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

310  

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

318  

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

325  

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

332  

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

343  

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

356  

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

368  

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

374  

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

380  

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

 
388 -
//------------------------------------------------
 
389  
};
386  
};
390  

387  

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

389  

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

394  

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

396  

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

401  

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

404  

408  
#endif
405  
#endif