1 -
//
1 +
//
2 -
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/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