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.
193  
    /** Construct from an iterator range.
194  

194  

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

198  

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

217  

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

219  

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

222  

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

225  

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

246  

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

254  

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

271  

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

279  

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

287  

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

295  

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

303  

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

311  

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

319  

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

326  

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

333  

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

344  

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

357  

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

369  

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

375  

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

381  

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

388  

389  
//------------------------------------------------
389  
//------------------------------------------------
390  

390  

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

392  

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

397  

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

399  

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

404  

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

407  

408  
#endif
408  
#endif