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 <span>
33  
#include <span>
34  

34  

35  
namespace boost {
35  
namespace boost {
36  
namespace capy {
36  
namespace capy {
37  

37  

38  
/** A buffer sequence wrapper providing windowed access.
38  
/** A buffer sequence wrapper providing windowed access.
39  

39  

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

44  

45  
    @par Coroutine Lifetime Requirement
45  
    @par Coroutine Lifetime Requirement
46  

46  

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

49  

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

54  

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

59  

60  
    @par Purpose
60  
    @par Purpose
61  

61  

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

67  

68  
    @par Usage
68  
    @par Usage
69  

69  

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

74  

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

89  

90  
    @par Virtual Interface Pattern
90  
    @par Virtual Interface Pattern
91  

91  

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

96  

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

114  

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

121  

122  
    @tparam BS The buffer sequence type. Must satisfy either
122  
    @tparam BS The buffer sequence type. Must satisfy either
123  
        ConstBufferSequence or MutableBufferSequence.
123  
        ConstBufferSequence or MutableBufferSequence.
124  

124  

125  
    @see ConstBufferSequence, MutableBufferSequence
125  
    @see ConstBufferSequence, MutableBufferSequence
126  
*/
126  
*/
127  
template<class BS>
127  
template<class BS>
128  
    requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
128  
    requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
129  
class buffer_param
129  
class buffer_param
130  
{
130  
{
131  
public:
131  
public:
132  
    /// The buffer type (const_buffer or mutable_buffer)
132  
    /// The buffer type (const_buffer or mutable_buffer)
133  
    using buffer_type = capy::buffer_type<BS>;
133  
    using buffer_type = capy::buffer_type<BS>;
134  

134  

135  
private:
135  
private:
136  
    decltype(begin(std::declval<BS const&>())) it_;
136  
    decltype(begin(std::declval<BS const&>())) it_;
137  
    decltype(end(std::declval<BS const&>())) end_;
137  
    decltype(end(std::declval<BS const&>())) end_;
138  
    buffer_type arr_[detail::max_iovec_];
138  
    buffer_type arr_[detail::max_iovec_];
139  
    std::size_t size_ = 0;
139  
    std::size_t size_ = 0;
140  
    std::size_t pos_ = 0;
140  
    std::size_t pos_ = 0;
141  

141  

142  
    void
142  
    void
143  
    refill()
143  
    refill()
144  
    {
144  
    {
145  
        pos_ = 0;
145  
        pos_ = 0;
146  
        size_ = 0;
146  
        size_ = 0;
147  
        for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
147  
        for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
148  
        {
148  
        {
149  
            buffer_type buf(*it_);
149  
            buffer_type buf(*it_);
150  
            if(buf.size() > 0)
150  
            if(buf.size() > 0)
151  
                arr_[size_++] = buf;
151  
                arr_[size_++] = buf;
152  
        }
152  
        }
153  
    }
153  
    }
154  

154  

155  
public:
155  
public:
156  
    /** Construct from a buffer sequence.
156  
    /** Construct from a buffer sequence.
157  

157  

158  
        @param bs The buffer sequence to wrap. The caller must
158  
        @param bs The buffer sequence to wrap. The caller must
159  
            ensure the buffer sequence remains valid for the
159  
            ensure the buffer sequence remains valid for the
160  
            lifetime of this object.
160  
            lifetime of this object.
161  
    */
161  
    */
162  
    explicit
162  
    explicit
163  
    buffer_param(BS const& bs)
163  
    buffer_param(BS const& bs)
164  
        : it_(begin(bs))
164  
        : it_(begin(bs))
165  
        , end_(end(bs))
165  
        , end_(end(bs))
166  
    {
166  
    {
167  
        refill();
167  
        refill();
168  
    }
168  
    }
169  

169  

170  
    /** Return the current window of buffer descriptors.
170  
    /** Return the current window of buffer descriptors.
171  

171  

172  
        Returns a span of buffer descriptors representing the
172  
        Returns a span of buffer descriptors representing the
173  
        currently available portion of the buffer sequence.
173  
        currently available portion of the buffer sequence.
174  
        The span contains at most @ref max_size buffers.
174  
        The span contains at most @ref max_size buffers.
175  

175  

176  
        When the current window is exhausted, this function
176  
        When the current window is exhausted, this function
177  
        automatically refills from the underlying sequence.
177  
        automatically refills from the underlying sequence.
178  

178  

179  
        @return A span of buffer descriptors. Empty span
179  
        @return A span of buffer descriptors. Empty span
180  
            indicates no more data is available.
180  
            indicates no more data is available.
181  
    */
181  
    */
182  
    std::span<buffer_type>
182  
    std::span<buffer_type>
183  
    data()
183  
    data()
184  
    {
184  
    {
185  
        if(pos_ >= size_)
185  
        if(pos_ >= size_)
186  
            refill();
186  
            refill();
187  
        if(size_ == 0)
187  
        if(size_ == 0)
188  
            return {};
188  
            return {};
189  
        return {arr_ + pos_, size_ - pos_};
189  
        return {arr_ + pos_, size_ - pos_};
190  
    }
190  
    }
191  

191  

192  
    /** Consume bytes from the buffer sequence.
192  
    /** Consume bytes from the buffer sequence.
193  

193  

194  
        Advances the current position by `n` bytes, consuming
194  
        Advances the current position by `n` bytes, consuming
195  
        data from the front of the sequence. Partially consumed
195  
        data from the front of the sequence. Partially consumed
196  
        buffers are adjusted in place.
196  
        buffers are adjusted in place.
197  

197  

198  
        @param n Number of bytes to consume.
198  
        @param n Number of bytes to consume.
199  
    */
199  
    */
200  
    void
200  
    void
201  
    consume(std::size_t n)
201  
    consume(std::size_t n)
202  
    {
202  
    {
203  
        while(n > 0 && pos_ < size_)
203  
        while(n > 0 && pos_ < size_)
204  
        {
204  
        {
205  
            auto avail = arr_[pos_].size();
205  
            auto avail = arr_[pos_].size();
206  
            if(n < avail)
206  
            if(n < avail)
207  
            {
207  
            {
208  
                arr_[pos_] += n;
208  
                arr_[pos_] += n;
209  
                n = 0;
209  
                n = 0;
210  
            }
210  
            }
211  
            else
211  
            else
212  
            {
212  
            {
213  
                n -= avail;
213  
                n -= avail;
214  
                ++pos_;
214  
                ++pos_;
215  
            }
215  
            }
216  
        }
216  
        }
217  
    }
217  
    }
218  
};
218  
};
219  

219  

220  
// CTAD deduction guide
220  
// CTAD deduction guide
221  
template<class BS>
221  
template<class BS>
222  
buffer_param(BS const&) -> buffer_param<BS>;
222  
buffer_param(BS const&) -> buffer_param<BS>;
223  

223  

224  
} // namespace capy
224  
} // namespace capy
225  
} // namespace boost
225  
} // namespace boost
226  

226  

227  
#endif
227  
#endif