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

34  

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

37  

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

39  

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

44  

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

46  

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

49  

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

54  

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

59  

61  
    @par Purpose
60  
    @par Purpose
62  

61  

63  
    When iterating through large buffer sequences, it is often
62  
    When iterating through large buffer sequences, it is often
64  
    more efficient to process buffers in batches rather than
63  
    more efficient to process buffers in batches rather than
65  
    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
66  
    @ref max_size buffer descriptors, automatically refilling
65  
    @ref max_size buffer descriptors, automatically refilling
67  
    from the underlying sequence as buffers are consumed.
66  
    from the underlying sequence as buffers are consumed.
68  

67  

69  
    @par Usage
68  
    @par Usage
70  

69  

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

74  

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

89  

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

91  

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

96  

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

114  

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

121  

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

124  

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

134  

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

141  

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

154  

159  
public:
155  
public:
160  
    /** Construct from a buffer sequence.
156  
    /** Construct from a buffer sequence.
161  

157  

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

169  

174  
    /** Return the current window of buffer descriptors.
170  
    /** Return the current window of buffer descriptors.
175  

171  

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

175  

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

178  

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

191  

196  
    /** Consume bytes from the buffer sequence.
192  
    /** Consume bytes from the buffer sequence.
197  

193  

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

197  

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

219  

224  
// CTAD deduction guide
220  
// CTAD deduction guide
225  
template<class BS>
221  
template<class BS>
226 -

 
227 -
/// Alias for buffer_param that always uses const_buffer storage.
 
228 -
template<class BS>
 
229 -
using const_buffer_param = buffer_param<BS, true>;
 
230  
buffer_param(BS const&) -> buffer_param<BS>;
222  
buffer_param(BS const&) -> buffer_param<BS>;
231  

223  

232  
} // namespace capy
224  
} // namespace capy
233  
} // namespace boost
225  
} // namespace boost
234  

226  

235  
#endif
227  
#endif