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  
    @par Example
69  
    @code
69  
    @code
70  
    void process(ConstBufferSequence auto const& buffers)
70  
    void process(ConstBufferSequence auto const& buffers)
71  
    {
71  
    {
72  
        const_buffer_array<4> bufs(buffers);
72  
        const_buffer_array<4> bufs(buffers);
73  
        // use bufs.begin(), bufs.end(), bufs.to_span()
73  
        // use bufs.begin(), bufs.end(), bufs.to_span()
74  
    }
74  
    }
75  
    @endcode
75  
    @endcode
76  

76  

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

87  

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

95  

96  
public:
96  
public:
97  
    /** Construct a default instance.
97  
    /** Construct a default instance.
98  

98  

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

105  

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

115  

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

117  

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

130  

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

132  

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

137  

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

160  

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

162  

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

165  

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

167  

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

191  

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

193  

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

197  

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

216  

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

218  

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

221  

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

224  

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

245  

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

253  

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

270  

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

278  

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

286  

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

294  

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

302  

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

310  

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

318  

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

325  

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

332  

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

343  

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

356  

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

368  

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

374  

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

380  

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

387  

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

389  

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

394  

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

396  

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

401  

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

404  

405  
#endif
405  
#endif