1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_IO_BUFFER_PARAM_HPP
10  
#ifndef BOOST_COROSIO_IO_BUFFER_PARAM_HPP
11  
#define BOOST_COROSIO_IO_BUFFER_PARAM_HPP
11  
#define BOOST_COROSIO_IO_BUFFER_PARAM_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/capy/buffers.hpp>
14  
#include <boost/capy/buffers.hpp>
15  

15  

16  
#include <cstddef>
16  
#include <cstddef>
17  

17  

18  
namespace boost::corosio {
18  
namespace boost::corosio {
19  

19  

20  
/** A type-erased buffer sequence for I/O system call boundaries.
20  
/** A type-erased buffer sequence for I/O system call boundaries.
21  

21  

22  
    This class enables I/O objects to accept any buffer sequence type
22  
    This class enables I/O objects to accept any buffer sequence type
23  
    across a virtual function boundary, while preserving the caller's
23  
    across a virtual function boundary, while preserving the caller's
24  
    typed buffer sequence at the call site. The implementation can
24  
    typed buffer sequence at the call site. The implementation can
25  
    then unroll the type-erased sequence into platform-native
25  
    then unroll the type-erased sequence into platform-native
26  
    structures (e.g., `iovec` on POSIX, `WSABUF` on Windows) for the
26  
    structures (e.g., `iovec` on POSIX, `WSABUF` on Windows) for the
27  
    actual system call.
27  
    actual system call.
28  

28  

29  
    @par Purpose
29  
    @par Purpose
30  

30  

31  
    When building coroutine-based I/O abstractions, a common pattern
31  
    When building coroutine-based I/O abstractions, a common pattern
32  
    emerges: a templated awaitable captures the caller's buffer
32  
    emerges: a templated awaitable captures the caller's buffer
33  
    sequence, and at `await_suspend` time, must pass it across a
33  
    sequence, and at `await_suspend` time, must pass it across a
34  
    virtual interface to the I/O implementation. This class solves
34  
    virtual interface to the I/O implementation. This class solves
35  
    the type-erasure problem at that boundary without heap allocation.
35  
    the type-erasure problem at that boundary without heap allocation.
36  

36  

37  
    @par Restricted Use Case
37  
    @par Restricted Use Case
38  

38  

39  
    This is NOT a general-purpose composable abstraction. It exists
39  
    This is NOT a general-purpose composable abstraction. It exists
40  
    solely for the final step in a coroutine I/O call chain where:
40  
    solely for the final step in a coroutine I/O call chain where:
41  

41  

42  
    @li A templated awaitable captures the caller's buffer sequence
42  
    @li A templated awaitable captures the caller's buffer sequence
43  
    @li The awaitable's `await_suspend` passes buffers across a
43  
    @li The awaitable's `await_suspend` passes buffers across a
44  
        virtual interface to an I/O object implementation
44  
        virtual interface to an I/O object implementation
45  
    @li The implementation immediately unrolls the buffers into
45  
    @li The implementation immediately unrolls the buffers into
46  
        platform-native structures for the system call
46  
        platform-native structures for the system call
47  

47  

48  
    @par Lifetime Model
48  
    @par Lifetime Model
49  

49  

50  
    The safety of this class depends entirely on coroutine parameter
50  
    The safety of this class depends entirely on coroutine parameter
51  
    lifetime extension. When a coroutine is suspended, parameters
51  
    lifetime extension. When a coroutine is suspended, parameters
52  
    passed to the awaitable remain valid until the coroutine resumes
52  
    passed to the awaitable remain valid until the coroutine resumes
53  
    or is destroyed. This class exploits that guarantee by holding
53  
    or is destroyed. This class exploits that guarantee by holding
54  
    only a pointer to the caller's buffer sequence.
54  
    only a pointer to the caller's buffer sequence.
55  

55  

56  
    The referenced buffer sequence is valid ONLY while the calling
56  
    The referenced buffer sequence is valid ONLY while the calling
57  
    coroutine remains suspended at the exact suspension point where
57  
    coroutine remains suspended at the exact suspension point where
58  
    `io_buffer_param` was created. Once the coroutine resumes,
58  
    `io_buffer_param` was created. Once the coroutine resumes,
59  
    returns, or is destroyed, all referenced data becomes invalid.
59  
    returns, or is destroyed, all referenced data becomes invalid.
60  

60  

61  
    @par Const Buffer Handling
61  
    @par Const Buffer Handling
62  

62  

63  
    This class accepts both `ConstBufferSequence` and
63  
    This class accepts both `ConstBufferSequence` and
64  
    `MutableBufferSequence` types. However, `copy_to` always produces
64  
    `MutableBufferSequence` types. However, `copy_to` always produces
65  
    `mutable_buffer` descriptors, casting away constness for const
65  
    `mutable_buffer` descriptors, casting away constness for const
66  
    buffer sequences. This design matches platform I/O structures
66  
    buffer sequences. This design matches platform I/O structures
67  
    (`iovec`, `WSABUF`) which use non-const pointers regardless of
67  
    (`iovec`, `WSABUF`) which use non-const pointers regardless of
68  
    the operation direction.
68  
    the operation direction.
69  

69  

70  
    @warning The caller is responsible for ensuring the type system
70  
    @warning The caller is responsible for ensuring the type system
71  
    is not violated. When the original buffer sequence was const
71  
    is not violated. When the original buffer sequence was const
72  
    (e.g., for a write operation), the implementation MUST NOT write
72  
    (e.g., for a write operation), the implementation MUST NOT write
73  
    to the buffers obtained from `copy_to`. The const-cast exists
73  
    to the buffers obtained from `copy_to`. The const-cast exists
74  
    solely to provide a uniform interface for platform I/O calls.
74  
    solely to provide a uniform interface for platform I/O calls.
75  

75  

76  
    @code
76  
    @code
77  
    // For write operations (const buffers):
77  
    // For write operations (const buffers):
78  
    void submit_write(io_buffer_param p)
78  
    void submit_write(io_buffer_param p)
79  
    {
79  
    {
80  
        capy::mutable_buffer bufs[8];
80  
        capy::mutable_buffer bufs[8];
81  
        auto n = p.copy_to(bufs, 8);
81  
        auto n = p.copy_to(bufs, 8);
82  
        // bufs[] may reference const data - DO NOT WRITE
82  
        // bufs[] may reference const data - DO NOT WRITE
83  
        writev(fd, reinterpret_cast<iovec*>(bufs), n);  // OK: read-only
83  
        writev(fd, reinterpret_cast<iovec*>(bufs), n);  // OK: read-only
84  
    }
84  
    }
85  

85  

86  
    // For read operations (mutable buffers):
86  
    // For read operations (mutable buffers):
87  
    void submit_read(io_buffer_param p)
87  
    void submit_read(io_buffer_param p)
88  
    {
88  
    {
89  
        capy::mutable_buffer bufs[8];
89  
        capy::mutable_buffer bufs[8];
90  
        auto n = p.copy_to(bufs, 8);
90  
        auto n = p.copy_to(bufs, 8);
91  
        // bufs[] references mutable data - safe to write
91  
        // bufs[] references mutable data - safe to write
92  
        readv(fd, reinterpret_cast<iovec*>(bufs), n);  // OK: writing
92  
        readv(fd, reinterpret_cast<iovec*>(bufs), n);  // OK: writing
93  
    }
93  
    }
94  
    @endcode
94  
    @endcode
95  

95  

96  
    @par Correct Usage
96  
    @par Correct Usage
97  

97  

98  
    The implementation receiving `io_buffer_param` MUST:
98  
    The implementation receiving `io_buffer_param` MUST:
99  

99  

100  
    @li Call `copy_to` immediately upon receiving the parameter
100  
    @li Call `copy_to` immediately upon receiving the parameter
101  
    @li Use the unrolled buffer descriptors for the I/O operation
101  
    @li Use the unrolled buffer descriptors for the I/O operation
102  
    @li Never store the `io_buffer_param` object itself
102  
    @li Never store the `io_buffer_param` object itself
103  
    @li Never store pointers obtained from `copy_to` beyond the
103  
    @li Never store pointers obtained from `copy_to` beyond the
104  
        immediate I/O operation
104  
        immediate I/O operation
105  

105  

106  
    @par Example: Correct Usage
106  
    @par Example: Correct Usage
107  

107  

108  
    @code
108  
    @code
109  
    // Templated awaitable at the call site
109  
    // Templated awaitable at the call site
110  
    template<class Buffers>
110  
    template<class Buffers>
111  
    struct write_awaitable
111  
    struct write_awaitable
112  
    {
112  
    {
113  
        Buffers bufs;
113  
        Buffers bufs;
114  
        io_stream* stream;
114  
        io_stream* stream;
115  

115  

116  
        bool await_ready() { return false; }
116  
        bool await_ready() { return false; }
117  

117  

118  
        void await_suspend(std::coroutine_handle<> h)
118  
        void await_suspend(std::coroutine_handle<> h)
119  
        {
119  
        {
120  
            // CORRECT: Pass to virtual interface while suspended.
120  
            // CORRECT: Pass to virtual interface while suspended.
121  
            // The buffer sequence 'bufs' remains valid because
121  
            // The buffer sequence 'bufs' remains valid because
122  
            // coroutine parameters live until resumption.
122  
            // coroutine parameters live until resumption.
123  
            stream->async_write_some_impl(bufs, h);
123  
            stream->async_write_some_impl(bufs, h);
124  
        }
124  
        }
125  

125  

126  
        io_result await_resume() { return stream->get_result(); }
126  
        io_result await_resume() { return stream->get_result(); }
127  
    };
127  
    };
128  

128  

129  
    // Virtual implementation - unrolls immediately
129  
    // Virtual implementation - unrolls immediately
130  
    void stream_impl::async_write_some_impl(
130  
    void stream_impl::async_write_some_impl(
131  
        io_buffer_param p,
131  
        io_buffer_param p,
132  
        std::coroutine_handle<> h)
132  
        std::coroutine_handle<> h)
133  
    {
133  
    {
134  
        // CORRECT: Unroll immediately into platform structure
134  
        // CORRECT: Unroll immediately into platform structure
135  
        iovec vecs[16];
135  
        iovec vecs[16];
136  
        std::size_t n = p.copy_to(
136  
        std::size_t n = p.copy_to(
137  
            reinterpret_cast<capy::mutable_buffer*>(vecs), 16);
137  
            reinterpret_cast<capy::mutable_buffer*>(vecs), 16);
138  

138  

139  
        // CORRECT: Use unrolled buffers for system call now
139  
        // CORRECT: Use unrolled buffers for system call now
140  
        submit_to_io_uring(vecs, n, h);
140  
        submit_to_io_uring(vecs, n, h);
141  

141  

142  
        // After this function returns, 'p' must not be used again.
142  
        // After this function returns, 'p' must not be used again.
143  
        // The iovec array is safe because it contains copies of
143  
        // The iovec array is safe because it contains copies of
144  
        // the pointer/size pairs, not references to 'p'.
144  
        // the pointer/size pairs, not references to 'p'.
145  
    }
145  
    }
146  
    @endcode
146  
    @endcode
147  

147  

148  
    @par UNSAFE USAGE: Storing io_buffer_param
148  
    @par UNSAFE USAGE: Storing io_buffer_param
149  

149  

150  
    @warning Never store `io_buffer_param` for later use.
150  
    @warning Never store `io_buffer_param` for later use.
151  

151  

152  
    @code
152  
    @code
153  
    class broken_stream
153  
    class broken_stream
154  
    {
154  
    {
155  
        io_buffer_param saved_param_;  // UNSAFE: member storage
155  
        io_buffer_param saved_param_;  // UNSAFE: member storage
156  

156  

157  
        void async_write_impl(io_buffer_param p, ...)
157  
        void async_write_impl(io_buffer_param p, ...)
158  
        {
158  
        {
159  
            saved_param_ = p;  // UNSAFE: storing for later
159  
            saved_param_ = p;  // UNSAFE: storing for later
160  
            schedule_write_later();
160  
            schedule_write_later();
161  
        }
161  
        }
162  

162  

163  
        void do_write_later()
163  
        void do_write_later()
164  
        {
164  
        {
165  
            // UNSAFE: The calling coroutine may have resumed
165  
            // UNSAFE: The calling coroutine may have resumed
166  
            // or been destroyed. saved_param_ now references
166  
            // or been destroyed. saved_param_ now references
167  
            // invalid memory!
167  
            // invalid memory!
168  
            capy::mutable_buffer bufs[8];
168  
            capy::mutable_buffer bufs[8];
169  
            saved_param_.copy_to(bufs, 8);  // UNDEFINED BEHAVIOR
169  
            saved_param_.copy_to(bufs, 8);  // UNDEFINED BEHAVIOR
170  
        }
170  
        }
171  
    };
171  
    };
172  
    @endcode
172  
    @endcode
173  

173  

174  
    @par UNSAFE USAGE: Storing Unrolled Pointers
174  
    @par UNSAFE USAGE: Storing Unrolled Pointers
175  

175  

176  
    @warning The pointers obtained from `copy_to` point into the
176  
    @warning The pointers obtained from `copy_to` point into the
177  
    caller's buffer sequence. They become invalid when the caller
177  
    caller's buffer sequence. They become invalid when the caller
178  
    resumes.
178  
    resumes.
179  

179  

180  
    @code
180  
    @code
181  
    class broken_stream
181  
    class broken_stream
182  
    {
182  
    {
183  
        capy::mutable_buffer saved_bufs_[8];  // UNSAFE
183  
        capy::mutable_buffer saved_bufs_[8];  // UNSAFE
184  
        std::size_t saved_count_;
184  
        std::size_t saved_count_;
185  

185  

186  
        void async_write_impl(io_buffer_param p, ...)
186  
        void async_write_impl(io_buffer_param p, ...)
187  
        {
187  
        {
188  
            // This copies pointer/size pairs into saved_bufs_
188  
            // This copies pointer/size pairs into saved_bufs_
189  
            saved_count_ = p.copy_to(saved_bufs_, 8);
189  
            saved_count_ = p.copy_to(saved_bufs_, 8);
190  

190  

191  
            // UNSAFE: scheduling for later while storing the
191  
            // UNSAFE: scheduling for later while storing the
192  
            // buffer descriptors. The pointers in saved_bufs_
192  
            // buffer descriptors. The pointers in saved_bufs_
193  
            // will dangle when the caller resumes!
193  
            // will dangle when the caller resumes!
194  
            schedule_for_later();
194  
            schedule_for_later();
195  
        }
195  
        }
196  

196  

197  
        void later()
197  
        void later()
198  
        {
198  
        {
199  
            // UNSAFE: saved_bufs_ contains dangling pointers
199  
            // UNSAFE: saved_bufs_ contains dangling pointers
200  
            for(std::size_t i = 0; i < saved_count_; ++i)
200  
            for(std::size_t i = 0; i < saved_count_; ++i)
201  
                write(fd_, saved_bufs_[i].data(), ...);  // UB
201  
                write(fd_, saved_bufs_[i].data(), ...);  // UB
202  
        }
202  
        }
203  
    };
203  
    };
204  
    @endcode
204  
    @endcode
205  

205  

206  
    @par UNSAFE USAGE: Using Outside a Coroutine
206  
    @par UNSAFE USAGE: Using Outside a Coroutine
207  

207  

208  
    @warning This class relies on coroutine lifetime semantics.
208  
    @warning This class relies on coroutine lifetime semantics.
209  
    Using it with callbacks or non-coroutine async patterns is
209  
    Using it with callbacks or non-coroutine async patterns is
210  
    undefined behavior.
210  
    undefined behavior.
211  

211  

212  
    @code
212  
    @code
213  
    // UNSAFE: No coroutine lifetime guarantee
213  
    // UNSAFE: No coroutine lifetime guarantee
214  
    void bad_callback_pattern(std::vector<char>& data)
214  
    void bad_callback_pattern(std::vector<char>& data)
215  
    {
215  
    {
216  
        capy::mutable_buffer buf(data.data(), data.size());
216  
        capy::mutable_buffer buf(data.data(), data.size());
217  

217  

218  
        // UNSAFE: In a callback model, 'buf' may go out of scope
218  
        // UNSAFE: In a callback model, 'buf' may go out of scope
219  
        // before the callback fires. There is no coroutine
219  
        // before the callback fires. There is no coroutine
220  
        // suspension to extend the lifetime.
220  
        // suspension to extend the lifetime.
221  
        stream.async_write(buf, [](error_code ec) {
221  
        stream.async_write(buf, [](error_code ec) {
222  
            // 'buf' is already destroyed!
222  
            // 'buf' is already destroyed!
223  
        });
223  
        });
224  
    }
224  
    }
225  
    @endcode
225  
    @endcode
226  

226  

227  
    @par UNSAFE USAGE: Passing to Another Coroutine
227  
    @par UNSAFE USAGE: Passing to Another Coroutine
228  

228  

229  
    @warning Do not pass `io_buffer_param` to a different coroutine
229  
    @warning Do not pass `io_buffer_param` to a different coroutine
230  
    or spawn a new coroutine that captures it.
230  
    or spawn a new coroutine that captures it.
231  

231  

232  
    @code
232  
    @code
233  
    void broken_impl(io_buffer_param p, std::coroutine_handle<> h)
233  
    void broken_impl(io_buffer_param p, std::coroutine_handle<> h)
234  
    {
234  
    {
235  
        // UNSAFE: Spawning a new coroutine that captures 'p'.
235  
        // UNSAFE: Spawning a new coroutine that captures 'p'.
236  
        // The original coroutine may resume before this new
236  
        // The original coroutine may resume before this new
237  
        // coroutine uses 'p'.
237  
        // coroutine uses 'p'.
238  
        co_spawn([p]() -> task<void> {
238  
        co_spawn([p]() -> task<void> {
239  
            capy::mutable_buffer bufs[8];
239  
            capy::mutable_buffer bufs[8];
240  
            p.copy_to(bufs, 8);  // UNSAFE: original caller may
240  
            p.copy_to(bufs, 8);  // UNSAFE: original caller may
241  
                                 // have resumed already!
241  
                                 // have resumed already!
242  
            co_return;
242  
            co_return;
243  
        });
243  
        });
244  
    }
244  
    }
245  
    @endcode
245  
    @endcode
246  

246  

247  
    @par UNSAFE USAGE: Multiple Virtual Hops
247  
    @par UNSAFE USAGE: Multiple Virtual Hops
248  

248  

249  
    @warning Minimize indirection. Each virtual call that passes
249  
    @warning Minimize indirection. Each virtual call that passes
250  
    `io_buffer_param` without immediately unrolling it increases
250  
    `io_buffer_param` without immediately unrolling it increases
251  
    the risk of misuse.
251  
    the risk of misuse.
252  

252  

253  
    @code
253  
    @code
254  
    // Risky: multiple hops before unrolling
254  
    // Risky: multiple hops before unrolling
255  
    void layer1(io_buffer_param p) {
255  
    void layer1(io_buffer_param p) {
256  
        layer2(p);  // Still haven't unrolled...
256  
        layer2(p);  // Still haven't unrolled...
257  
    }
257  
    }
258  
    void layer2(io_buffer_param p) {
258  
    void layer2(io_buffer_param p) {
259  
        layer3(p);  // Still haven't unrolled...
259  
        layer3(p);  // Still haven't unrolled...
260  
    }
260  
    }
261  
    void layer3(io_buffer_param p) {
261  
    void layer3(io_buffer_param p) {
262  
        // Finally unrolling, but the chain is fragile.
262  
        // Finally unrolling, but the chain is fragile.
263  
        // Any intermediate layer storing 'p' breaks everything.
263  
        // Any intermediate layer storing 'p' breaks everything.
264  
    }
264  
    }
265  
    @endcode
265  
    @endcode
266  

266  

267  
    @par UNSAFE USAGE: Fire-and-Forget Operations
267  
    @par UNSAFE USAGE: Fire-and-Forget Operations
268  

268  

269  
    @warning Do not use with detached or fire-and-forget async
269  
    @warning Do not use with detached or fire-and-forget async
270  
    operations where there is no guarantee the caller remains
270  
    operations where there is no guarantee the caller remains
271  
    suspended.
271  
    suspended.
272  

272  

273  
    @code
273  
    @code
274  
    task<void> caller()
274  
    task<void> caller()
275  
    {
275  
    {
276  
        char buf[1024];
276  
        char buf[1024];
277  
        // UNSAFE: If async_write is fire-and-forget (doesn't
277  
        // UNSAFE: If async_write is fire-and-forget (doesn't
278  
        // actually suspend the caller), 'buf' may be destroyed
278  
        // actually suspend the caller), 'buf' may be destroyed
279  
        // before the I/O completes.
279  
        // before the I/O completes.
280  
        stream.async_write_detached(capy::mutable_buffer(buf, 1024));
280  
        stream.async_write_detached(capy::mutable_buffer(buf, 1024));
281  
        // Returns immediately - 'buf' goes out of scope!
281  
        // Returns immediately - 'buf' goes out of scope!
282  
    }
282  
    }
283  
    @endcode
283  
    @endcode
284  

284  

285  
    @par Passing Convention
285  
    @par Passing Convention
286  

286  

287  
    Pass by value. The class contains only two pointers (16 bytes
287  
    Pass by value. The class contains only two pointers (16 bytes
288  
    on 64-bit systems), making copies trivial and clearly
288  
    on 64-bit systems), making copies trivial and clearly
289  
    communicating the lightweight, transient nature of this type.
289  
    communicating the lightweight, transient nature of this type.
290  

290  

291  
    @code
291  
    @code
292  
    // Preferred: pass by value
292  
    // Preferred: pass by value
293  
    void process(io_buffer_param buffers);
293  
    void process(io_buffer_param buffers);
294  

294  

295  
    // Also acceptable: pass by const reference
295  
    // Also acceptable: pass by const reference
296  
    void process(io_buffer_param const& buffers);
296  
    void process(io_buffer_param const& buffers);
297  
    @endcode
297  
    @endcode
298  

298  

299  
    @see capy::ConstBufferSequence, capy::MutableBufferSequence
299  
    @see capy::ConstBufferSequence, capy::MutableBufferSequence
300  
*/
300  
*/
301  
class io_buffer_param
301  
class io_buffer_param
302  
{
302  
{
303  
public:
303  
public:
304  
    /** Construct from a const buffer sequence.
304  
    /** Construct from a const buffer sequence.
305  

305  

306  
        @param bs The buffer sequence to adapt.
306  
        @param bs The buffer sequence to adapt.
307  
    */
307  
    */
308  
    template<capy::ConstBufferSequence BS>
308  
    template<capy::ConstBufferSequence BS>
309  
    io_buffer_param(BS const& bs) noexcept
309  
    io_buffer_param(BS const& bs) noexcept
310  
        : bs_(&bs)
310  
        : bs_(&bs)
311  
        , fn_(&copy_impl<BS>)
311  
        , fn_(&copy_impl<BS>)
312  
    {
312  
    {
313  
    }
313  
    }
314  

314  

315  
    /** Fill an array with buffers from the sequence.
315  
    /** Fill an array with buffers from the sequence.
316  

316  

317  
        Copies buffer descriptors from the sequence into the
317  
        Copies buffer descriptors from the sequence into the
318  
        destination array, skipping any zero-size buffers.
318  
        destination array, skipping any zero-size buffers.
319  
        This ensures the output contains only buffers with
319  
        This ensures the output contains only buffers with
320  
        actual data, suitable for direct use with system calls.
320  
        actual data, suitable for direct use with system calls.
321  

321  

322  
        @param dest Pointer to array of mutable buffer descriptors.
322  
        @param dest Pointer to array of mutable buffer descriptors.
323  
        @param n Maximum number of buffers to copy.
323  
        @param n Maximum number of buffers to copy.
324  

324  

325  
        @return The number of non-zero buffers copied.
325  
        @return The number of non-zero buffers copied.
326  
    */
326  
    */
327  
    std::size_t
327  
    std::size_t
328  
    copy_to(
328  
    copy_to(
329  
        capy::mutable_buffer* dest,
329  
        capy::mutable_buffer* dest,
330  
        std::size_t n) const noexcept
330  
        std::size_t n) const noexcept
331  
    {
331  
    {
332  
        return fn_(bs_, dest, n);
332  
        return fn_(bs_, dest, n);
333  
    }
333  
    }
334  

334  

335  
private:
335  
private:
336  
    template<capy::ConstBufferSequence BS>
336  
    template<capy::ConstBufferSequence BS>
337  
    static std::size_t
337  
    static std::size_t
338  
    copy_impl(
338  
    copy_impl(
339  
        void const* p,
339  
        void const* p,
340  
        capy::mutable_buffer* dest,
340  
        capy::mutable_buffer* dest,
341  
        std::size_t n)
341  
        std::size_t n)
342  
    {
342  
    {
343  
        auto const& bs = *static_cast<BS const*>(p);
343  
        auto const& bs = *static_cast<BS const*>(p);
344  
        auto it = capy::begin(bs);
344  
        auto it = capy::begin(bs);
345  
        auto const end_it = capy::end(bs);
345  
        auto const end_it = capy::end(bs);
346  

346  

347  
        std::size_t i = 0;
347  
        std::size_t i = 0;
348  
        if constexpr (capy::MutableBufferSequence<BS>)
348  
        if constexpr (capy::MutableBufferSequence<BS>)
349  
        {
349  
        {
350  
            for(; it != end_it && i < n; ++it)
350  
            for(; it != end_it && i < n; ++it)
351  
            {
351  
            {
352  
                capy::mutable_buffer buf(*it);
352  
                capy::mutable_buffer buf(*it);
353  
                if(buf.size() == 0)
353  
                if(buf.size() == 0)
354  
                    continue;
354  
                    continue;
355  
                dest[i++] = buf;
355  
                dest[i++] = buf;
356  
            }
356  
            }
357  
        }
357  
        }
358  
        else
358  
        else
359  
        {
359  
        {
360  
            for(; it != end_it && i < n; ++it)
360  
            for(; it != end_it && i < n; ++it)
361  
            {
361  
            {
362  
                capy::const_buffer buf(*it);
362  
                capy::const_buffer buf(*it);
363  
                if(buf.size() == 0)
363  
                if(buf.size() == 0)
364  
                    continue;
364  
                    continue;
365  
                dest[i++] = capy::mutable_buffer(
365  
                dest[i++] = capy::mutable_buffer(
366  
                    const_cast<char*>(
366  
                    const_cast<char*>(
367  
                        static_cast<char const*>(buf.data())),
367  
                        static_cast<char const*>(buf.data())),
368  
                    buf.size());
368  
                    buf.size());
369  
            }
369  
            }
370  
        }
370  
        }
371  
        return i;
371  
        return i;
372  
    }
372  
    }
373  

373  

374  
    using fn_t = std::size_t(*)(void const*,
374  
    using fn_t = std::size_t(*)(void const*,
375  
        capy::mutable_buffer*, std::size_t);
375  
        capy::mutable_buffer*, std::size_t);
376  

376  

377  
    void const* bs_;
377  
    void const* bs_;
378  
    fn_t fn_;
378  
    fn_t fn_;
379  
};
379  
};
380  

380  

381  
} // namespace boost::corosio
381  
} // namespace boost::corosio
382  

382  

383  
#endif
383  
#endif