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

36  

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

39  

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

41  

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

46  

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

48  

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

51  

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

56  

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

61  

61  
    @par Purpose
62  
    @par Purpose
62  

63  

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

69  

69  
    @par Usage
70  
    @par Usage
70  

71  

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

76  

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

91  

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

93  

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

98  

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

116  

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

123  

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

126  

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

139  

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

149  

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

162  

159  
public:
163  
public:
160  
    /** Construct from a buffer sequence.
164  
    /** Construct from a buffer sequence.
161  

165  

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

178  

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

180  

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

184  

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

187  

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

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

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

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

215  

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

217  

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

221  

202  
        @param n Number of bytes to consume.
222  
        @param n Number of bytes to consume.
203  
    */
223  
    */
204  
    void
224  
    void
205  
    consume(std::size_t n)
225  
    consume(std::size_t n)
206  
    {
226  
    {
207  
        while(n > 0 && pos_ < size_)
227  
        while(n > 0 && pos_ < size_)
208  
        {
228  
        {
209  
            auto avail = arr_[pos_].size();
229  
            auto avail = arr_[pos_].size();
210  
            if(n < avail)
230  
            if(n < avail)
211  
            {
231  
            {
212  
                arr_[pos_] += n;
232  
                arr_[pos_] += n;
213  
                n = 0;
233  
                n = 0;
214  
            }
234  
            }
215  
            else
235  
            else
216  
            {
236  
            {
217  
                n -= avail;
237  
                n -= avail;
218  
                ++pos_;
238  
                ++pos_;
219  
            }
239  
            }
220  
        }
240  
        }
221  
    }
241  
    }
222  
};
242  
};
223  

243  

224  
// CTAD deduction guide
244  
// CTAD deduction guide
225  
template<class BS>
245  
template<class BS>
226  
buffer_param(BS const&) -> buffer_param<BS>;
246  
buffer_param(BS const&) -> buffer_param<BS>;
227  

247  

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

251  

232  
} // namespace capy
252  
} // namespace capy
233  
} // namespace boost
253  
} // namespace boost
234  

254  

235  
#endif
255  
#endif