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  
/*
10  
/*
11  
    COROUTINE BUFFER SEQUENCE LIFETIME REQUIREMENT
11  
    COROUTINE BUFFER SEQUENCE LIFETIME REQUIREMENT
12  
    ===============================================
12  
    ===============================================
13  
    Buffer sequence parameters in coroutine APIs MUST be passed BY VALUE,
13  
    Buffer sequence parameters in coroutine APIs MUST be passed BY VALUE,
14  
    never by reference. When a coroutine suspends, reference parameters may
14  
    never by reference. When a coroutine suspends, reference parameters may
15  
    dangle if the caller's object goes out of scope before resumption.
15  
    dangle if the caller's object goes out of scope before resumption.
16  

16  

17  
    CORRECT:   task<> read_some(MutableBufferSequence auto buffers)
17  
    CORRECT:   task<> read_some(MutableBufferSequence auto buffers)
18  
    WRONG:     task<> read_some(MutableBufferSequence auto& buffers)
18  
    WRONG:     task<> read_some(MutableBufferSequence auto& buffers)
19  
    WRONG:     task<> read_some(MutableBufferSequence auto const& buffers)
19  
    WRONG:     task<> read_some(MutableBufferSequence auto const& buffers)
20  

20  

21  
    The buffer_param class works with this model: it takes a const& in its
21  
    The buffer_param class works with this model: it takes a const& in its
22  
    constructor (for the non-coroutine scope) but the caller's template
22  
    constructor (for the non-coroutine scope) but the caller's template
23  
    function accepts the buffer sequence by value, ensuring the sequence
23  
    function accepts the buffer sequence by value, ensuring the sequence
24  
    lives in the coroutine frame.
24  
    lives in the coroutine frame.
25  
*/
25  
*/
26  

26  

27  
#ifndef BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
27  
#ifndef BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
28  
#define BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
28  
#define BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
29  

29  

30  
#include <boost/capy/detail/config.hpp>
30  
#include <boost/capy/detail/config.hpp>
31  
#include <boost/capy/buffers.hpp>
31  
#include <boost/capy/buffers.hpp>
32  

32  

33  
#include <new>
33  
#include <new>
34  
#include <span>
34  
#include <span>
35  
#include <type_traits>
35  
#include <type_traits>
36  

36  

37  
namespace boost {
37  
namespace boost {
38  
namespace capy {
38  
namespace capy {
39  

39  

40  
/** A buffer sequence wrapper providing windowed access.
40  
/** A buffer sequence wrapper providing windowed access.
41  

41  

42  
    This template class wraps any buffer sequence and provides
42  
    This template class wraps any buffer sequence and provides
43  
    incremental access through a sliding window of buffer
43  
    incremental access through a sliding window of buffer
44  
    descriptors. It handles both const and mutable buffer
44  
    descriptors. It handles both const and mutable buffer
45  
    sequences automatically.
45  
    sequences automatically.
46  

46  

47  
    @par Coroutine Lifetime Requirement
47  
    @par Coroutine Lifetime Requirement
48  

48  

49  
    When used in coroutine APIs, the outer template function
49  
    When used in coroutine APIs, the outer template function
50  
    MUST accept the buffer sequence parameter BY VALUE:
50  
    MUST accept the buffer sequence parameter BY VALUE:
51  

51  

52  
    @code
52  
    @code
53  
    task<> write(ConstBufferSequence auto buffers);   // CORRECT
53  
    task<> write(ConstBufferSequence auto buffers);   // CORRECT
54  
    task<> write(ConstBufferSequence auto& buffers);  // WRONG - dangling reference
54  
    task<> write(ConstBufferSequence auto& buffers);  // WRONG - dangling reference
55  
    @endcode
55  
    @endcode
56  

56  

57  
    Pass-by-value ensures the buffer sequence is copied into
57  
    Pass-by-value ensures the buffer sequence is copied into
58  
    the coroutine frame and remains valid across suspension
58  
    the coroutine frame and remains valid across suspension
59  
    points. References would dangle when the caller's scope
59  
    points. References would dangle when the caller's scope
60  
    exits before the coroutine resumes.
60  
    exits before the coroutine resumes.
61  

61  

62  
    @par Purpose
62  
    @par Purpose
63  

63  

64  
    When iterating through large buffer sequences, it is often
64  
    When iterating through large buffer sequences, it is often
65  
    more efficient to process buffers in batches rather than
65  
    more efficient to process buffers in batches rather than
66  
    one at a time. This class maintains a window of up to
66  
    one at a time. This class maintains a window of up to
67  
    @ref max_size buffer descriptors, automatically refilling
67  
    @ref max_size buffer descriptors, automatically refilling
68  
    from the underlying sequence as buffers are consumed.
68  
    from the underlying sequence as buffers are consumed.
69  

69  

70  
    @par Usage
70  
    @par Usage
71  

71  

72  
    Create a `buffer_param` from any buffer sequence and use
72  
    Create a `buffer_param` from any buffer sequence and use
73  
    `data()` to get the current window of buffers. After
73  
    `data()` to get the current window of buffers. After
74  
    processing some bytes, call `consume()` to advance through
74  
    processing some bytes, call `consume()` to advance through
75  
    the sequence.
75  
    the sequence.
76  

76  

77  
    @code
77  
    @code
78  
    task<> send(ConstBufferSequence auto buffers)
78  
    task<> send(ConstBufferSequence auto buffers)
79  
    {
79  
    {
80  
        buffer_param bp(buffers);
80  
        buffer_param bp(buffers);
81  
        while(true)
81  
        while(true)
82  
        {
82  
        {
83  
            auto bufs = bp.data();
83  
            auto bufs = bp.data();
84  
            if(bufs.empty())
84  
            if(bufs.empty())
85  
                break;
85  
                break;
86  
            auto n = co_await do_something(bufs);
86  
            auto n = co_await do_something(bufs);
87  
            bp.consume(n);
87  
            bp.consume(n);
88  
        }
88  
        }
89  
    }
89  
    }
90  
    @endcode
90  
    @endcode
91  

91  

92  
    @par Virtual Interface Pattern
92  
    @par Virtual Interface Pattern
93  

93  

94  
    This class enables passing arbitrary buffer sequences through
94  
    This class enables passing arbitrary buffer sequences through
95  
    a virtual function boundary. The template function captures
95  
    a virtual function boundary. The template function captures
96  
    the buffer sequence by value and drives the iteration, while
96  
    the buffer sequence by value and drives the iteration, while
97  
    the virtual function receives a simple span:
97  
    the virtual function receives a simple span:
98  

98  

99  
    @code
99  
    @code
100  
    class base
100  
    class base
101  
    {
101  
    {
102  
    public:
102  
    public:
103  
        task<> write(ConstBufferSequence auto buffers)
103  
        task<> write(ConstBufferSequence auto buffers)
104  
        {
104  
        {
105  
            buffer_param bp(buffers);
105  
            buffer_param bp(buffers);
106  
            while(true)
106  
            while(true)
107  
            {
107  
            {
108  
                auto bufs = bp.data();
108  
                auto bufs = bp.data();
109  
                if(bufs.empty())
109  
                if(bufs.empty())
110  
                    break;
110  
                    break;
111  
                std::size_t n = 0;
111  
                std::size_t n = 0;
112  
                co_await write_impl(bufs, n);
112  
                co_await write_impl(bufs, n);
113  
                bp.consume(n);
113  
                bp.consume(n);
114  
            }
114  
            }
115  
        }
115  
        }
116  

116  

117  
    protected:
117  
    protected:
118  
        virtual task<> write_impl(
118  
        virtual task<> write_impl(
119  
            std::span<const_buffer> buffers,
119  
            std::span<const_buffer> buffers,
120  
            std::size_t& bytes_written) = 0;
120  
            std::size_t& bytes_written) = 0;
121  
    };
121  
    };
122  
    @endcode
122  
    @endcode
123  

123  

124  
    @tparam BS The buffer sequence type. Must satisfy either
124  
    @tparam BS The buffer sequence type. Must satisfy either
125  
        ConstBufferSequence or MutableBufferSequence.
125  
        ConstBufferSequence or MutableBufferSequence.
126  

126  

127  
    @see ConstBufferSequence, MutableBufferSequence
127  
    @see ConstBufferSequence, MutableBufferSequence
128  
*/
128  
*/
129  
template<class BS, bool MakeConst = false>
129  
template<class BS, bool MakeConst = false>
130  
    requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
130  
    requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
131  
class buffer_param
131  
class buffer_param
132  
{
132  
{
133  
public:
133  
public:
134  
    /// The buffer type (const_buffer or mutable_buffer)
134  
    /// The buffer type (const_buffer or mutable_buffer)
135  
    using buffer_type = std::conditional_t<
135  
    using buffer_type = std::conditional_t<
136  
        MakeConst,
136  
        MakeConst,
137  
        const_buffer,
137  
        const_buffer,
138  
        capy::buffer_type<BS>>;
138  
        capy::buffer_type<BS>>;
139  

139  

140  
private:
140  
private:
141  
    decltype(begin(std::declval<BS const&>())) it_;
141  
    decltype(begin(std::declval<BS const&>())) it_;
142  
    decltype(end(std::declval<BS const&>())) end_;
142  
    decltype(end(std::declval<BS const&>())) end_;
143  
    union {
143  
    union {
144  
        int dummy_;
144  
        int dummy_;
145  
        buffer_type arr_[detail::max_iovec_];
145  
        buffer_type arr_[detail::max_iovec_];
146  
    };
146  
    };
147  
    std::size_t size_ = 0;
147  
    std::size_t size_ = 0;
148  
    std::size_t pos_ = 0;
148  
    std::size_t pos_ = 0;
149  

149  

150  
    void
150  
    void
151  
    refill()
151  
    refill()
152  
    {
152  
    {
153  
        pos_ = 0;
153  
        pos_ = 0;
154  
        size_ = 0;
154  
        size_ = 0;
155  
        for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
155  
        for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
156  
        {
156  
        {
157  
            buffer_type buf(*it_);
157  
            buffer_type buf(*it_);
158  
            if(buf.size() > 0)
158  
            if(buf.size() > 0)
159  
                ::new(&arr_[size_++]) buffer_type(buf);
159  
                ::new(&arr_[size_++]) buffer_type(buf);
160  
        }
160  
        }
161  
    }
161  
    }
162  

162  

163  
public:
163  
public:
164  
    /** Construct from a buffer sequence.
164  
    /** Construct from a buffer sequence.
165  

165  

166  
        @param bs The buffer sequence to wrap. The caller must
166  
        @param bs The buffer sequence to wrap. The caller must
167  
            ensure the buffer sequence remains valid for the
167  
            ensure the buffer sequence remains valid for the
168  
            lifetime of this object.
168  
            lifetime of this object.
169  
    */
169  
    */
170  
    explicit
170  
    explicit
171  
    buffer_param(BS const& bs)
171  
    buffer_param(BS const& bs)
172  
        : it_(begin(bs))
172  
        : it_(begin(bs))
173  
        , end_(end(bs))
173  
        , end_(end(bs))
174  
        , dummy_(0)
174  
        , dummy_(0)
175  
    {
175  
    {
176  
        refill();
176  
        refill();
177  
    }
177  
    }
178  

178  

179  
    /** Return the current window of buffer descriptors.
179  
    /** Return the current window of buffer descriptors.
180  

180  

181  
        Returns a span of buffer descriptors representing the
181  
        Returns a span of buffer descriptors representing the
182  
        currently available portion of the buffer sequence.
182  
        currently available portion of the buffer sequence.
183  
        The span contains at most @ref max_size buffers.
183  
        The span contains at most @ref max_size buffers.
184  

184  

185  
        When the current window is exhausted, this function
185  
        When the current window is exhausted, this function
186  
        automatically refills from the underlying sequence.
186  
        automatically refills from the underlying sequence.
187  

187  

188  
        @return A span of buffer descriptors. Empty span
188  
        @return A span of buffer descriptors. Empty span
189  
            indicates no more data is available.
189  
            indicates no more data is available.
190  
    */
190  
    */
191  
    std::span<buffer_type>
191  
    std::span<buffer_type>
192  
    data()
192  
    data()
193  
    {
193  
    {
194  
        if(pos_ >= size_)
194  
        if(pos_ >= size_)
195  
            refill();
195  
            refill();
196  
        if(size_ == 0)
196  
        if(size_ == 0)
197  
            return {};
197  
            return {};
198  
        return {arr_ + pos_, size_ - pos_};
198  
        return {arr_ + pos_, size_ - pos_};
199  
    }
199  
    }
200  

200  

201  
    /** Check if more buffers exist beyond the current window.
201  
    /** Check if more buffers exist beyond the current window.
202  

202  

203  
        Returns `true` if the underlying buffer sequence has
203  
        Returns `true` if the underlying buffer sequence has
204  
        additional buffers that have not yet been loaded into
204  
        additional buffers that have not yet been loaded into
205  
        the current window. Call after @ref data to determine
205  
        the current window. Call after @ref data to determine
206  
        whether the current window is the last one.
206  
        whether the current window is the last one.
207  

207  

208  
        @return `true` if more buffers remain in the sequence.
208  
        @return `true` if more buffers remain in the sequence.
209  
    */
209  
    */
210  
    bool
210  
    bool
211  
    more() const noexcept
211  
    more() const noexcept
212  
    {
212  
    {
213  
        return it_ != end_;
213  
        return it_ != end_;
214  
    }
214  
    }
215  

215  

216  
    /** Consume bytes from the buffer sequence.
216  
    /** Consume bytes from the buffer sequence.
217  

217  

218  
        Advances the current position by `n` bytes, consuming
218  
        Advances the current position by `n` bytes, consuming
219  
        data from the front of the sequence. Partially consumed
219  
        data from the front of the sequence. Partially consumed
220  
        buffers are adjusted in place.
220  
        buffers are adjusted in place.
221  

221  

222  
        @param n Number of bytes to consume.
222  
        @param n Number of bytes to consume.
223  
    */
223  
    */
224  
    void
224  
    void
225  
    consume(std::size_t n)
225  
    consume(std::size_t n)
226  
    {
226  
    {
227  
        while(n > 0 && pos_ < size_)
227  
        while(n > 0 && pos_ < size_)
228  
        {
228  
        {
229  
            auto avail = arr_[pos_].size();
229  
            auto avail = arr_[pos_].size();
230  
            if(n < avail)
230  
            if(n < avail)
231  
            {
231  
            {
232  
                arr_[pos_] += n;
232  
                arr_[pos_] += n;
233  
                n = 0;
233  
                n = 0;
234  
            }
234  
            }
235  
            else
235  
            else
236  
            {
236  
            {
237  
                n -= avail;
237  
                n -= avail;
238  
                ++pos_;
238  
                ++pos_;
239  
            }
239  
            }
240  
        }
240  
        }
241  
    }
241  
    }
242  
};
242  
};
243  

243  

244  
// CTAD deduction guide
244  
// CTAD deduction guide
245  
template<class BS>
245  
template<class BS>
246  
buffer_param(BS const&) -> buffer_param<BS>;
246  
buffer_param(BS const&) -> buffer_param<BS>;
247  

247  

248  
/// Alias for buffer_param that always uses const_buffer storage.
248  
/// Alias for buffer_param that always uses const_buffer storage.
249  
template<class BS>
249  
template<class BS>
250  
using const_buffer_param = buffer_param<BS, true>;
250  
using const_buffer_param = buffer_param<BS, true>;
251  

251  

252  
} // namespace capy
252  
} // namespace capy
253  
} // namespace boost
253  
} // namespace boost
254  

254  

255  
#endif
255  
#endif