100.00% Lines (22/22) 100.00% Functions (3/3)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_DETAIL_BUFFER_PARAM_HPP 11   #ifndef BOOST_COROSIO_DETAIL_BUFFER_PARAM_HPP
12   #define BOOST_COROSIO_DETAIL_BUFFER_PARAM_HPP 12   #define BOOST_COROSIO_DETAIL_BUFFER_PARAM_HPP
13   13  
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   16  
17   #include <cstddef> 17   #include <cstddef>
18   18  
19   namespace boost::corosio { 19   namespace boost::corosio {
20   20  
21   /** A type-erased buffer sequence for I/O system call boundaries. 21   /** A type-erased buffer sequence for I/O system call boundaries.
22   22  
23   This class enables I/O objects to accept any buffer sequence type 23   This class enables I/O objects to accept any buffer sequence type
24   across a virtual function boundary, while preserving the caller's 24   across a virtual function boundary, while preserving the caller's
25   typed buffer sequence at the call site. The implementation can 25   typed buffer sequence at the call site. The implementation can
26   then unroll the type-erased sequence into platform-native 26   then unroll the type-erased sequence into platform-native
27   structures (e.g., `iovec` on POSIX, `WSABUF` on Windows) for the 27   structures (e.g., `iovec` on POSIX, `WSABUF` on Windows) for the
28   actual system call. 28   actual system call.
29   29  
30   @par Purpose 30   @par Purpose
31   31  
32   When building coroutine-based I/O abstractions, a common pattern 32   When building coroutine-based I/O abstractions, a common pattern
33   emerges: a templated awaitable captures the caller's buffer 33   emerges: a templated awaitable captures the caller's buffer
34   sequence, and at `await_suspend` time, must pass it across a 34   sequence, and at `await_suspend` time, must pass it across a
35   virtual interface to the I/O implementation. This class solves 35   virtual interface to the I/O implementation. This class solves
36   the type-erasure problem at that boundary without heap allocation. 36   the type-erasure problem at that boundary without heap allocation.
37   37  
38   @par Restricted Use Case 38   @par Restricted Use Case
39   39  
40   This is NOT a general-purpose composable abstraction. It exists 40   This is NOT a general-purpose composable abstraction. It exists
41   solely for the final step in a coroutine I/O call chain where: 41   solely for the final step in a coroutine I/O call chain where:
42   42  
43   @li A templated awaitable captures the caller's buffer sequence 43   @li A templated awaitable captures the caller's buffer sequence
44   @li The awaitable's `await_suspend` passes buffers across a 44   @li The awaitable's `await_suspend` passes buffers across a
45   virtual interface to an I/O object implementation 45   virtual interface to an I/O object implementation
46   @li The implementation immediately unrolls the buffers into 46   @li The implementation immediately unrolls the buffers into
47   platform-native structures for the system call 47   platform-native structures for the system call
48   48  
49   @par Lifetime Model 49   @par Lifetime Model
50   50  
51   The safety of this class depends entirely on coroutine parameter 51   The safety of this class depends entirely on coroutine parameter
52   lifetime extension. When a coroutine is suspended, parameters 52   lifetime extension. When a coroutine is suspended, parameters
53   passed to the awaitable remain valid until the coroutine resumes 53   passed to the awaitable remain valid until the coroutine resumes
54   or is destroyed. This class exploits that guarantee by holding 54   or is destroyed. This class exploits that guarantee by holding
55   only a pointer to the caller's buffer sequence. 55   only a pointer to the caller's buffer sequence.
56   56  
57   The referenced buffer sequence is valid ONLY while the calling 57   The referenced buffer sequence is valid ONLY while the calling
58   coroutine remains suspended at the exact suspension point where 58   coroutine remains suspended at the exact suspension point where
59   `buffer_param` was created. Once the coroutine resumes, 59   `buffer_param` was created. Once the coroutine resumes,
60   returns, or is destroyed, all referenced data becomes invalid. 60   returns, or is destroyed, all referenced data becomes invalid.
61   61  
62   @par Const Buffer Handling 62   @par Const Buffer Handling
63   63  
64   This class accepts both `ConstBufferSequence` and 64   This class accepts both `ConstBufferSequence` and
65   `MutableBufferSequence` types. However, `copy_to` always produces 65   `MutableBufferSequence` types. However, `copy_to` always produces
66   `mutable_buffer` descriptors, casting away constness for const 66   `mutable_buffer` descriptors, casting away constness for const
67   buffer sequences. This design matches platform I/O structures 67   buffer sequences. This design matches platform I/O structures
68   (`iovec`, `WSABUF`) which use non-const pointers regardless of 68   (`iovec`, `WSABUF`) which use non-const pointers regardless of
69   the operation direction. 69   the operation direction.
70   70  
71   @warning The caller is responsible for ensuring the type system 71   @warning The caller is responsible for ensuring the type system
72   is not violated. When the original buffer sequence was const 72   is not violated. When the original buffer sequence was const
73   (e.g., for a write operation), the implementation MUST NOT write 73   (e.g., for a write operation), the implementation MUST NOT write
74   to the buffers obtained from `copy_to`. The const-cast exists 74   to the buffers obtained from `copy_to`. The const-cast exists
75   solely to provide a uniform interface for platform I/O calls. 75   solely to provide a uniform interface for platform I/O calls.
76   76  
77   @note Do NOT `reinterpret_cast` the `mutable_buffer` array to 77   @note Do NOT `reinterpret_cast` the `mutable_buffer` array to
78   `iovec*`/`WSABUF*` and pass it to the OS, even when the layouts 78   `iovec*`/`WSABUF*` and pass it to the OS, even when the layouts
79   match: no object of the target type exists in that storage, so the 79   match: no object of the target type exists in that storage, so the
80   access is undefined behavior (and `mutable_buffer` is not an 80   access is undefined behavior (and `mutable_buffer` is not an
81   implicit-lifetime type, so `std::start_lifetime_as_array` cannot 81   implicit-lifetime type, so `std::start_lifetime_as_array` cannot
82   rescue it). Copy field by field into a real platform array instead. 82   rescue it). Copy field by field into a real platform array instead.
83   83  
84   @code 84   @code
85   // For write operations (const buffers): 85   // For write operations (const buffers):
86   void submit_write(buffer_param p) 86   void submit_write(buffer_param p)
87   { 87   {
88   capy::mutable_buffer bufs[8]; 88   capy::mutable_buffer bufs[8];
89   auto n = p.copy_to(bufs, 8); 89   auto n = p.copy_to(bufs, 8);
90   iovec iov[8]; 90   iovec iov[8];
91   for (std::size_t i = 0; i < n; ++i) 91   for (std::size_t i = 0; i < n; ++i)
92   { 92   {
93   // bufs[] may reference const data - DO NOT WRITE through iov 93   // bufs[] may reference const data - DO NOT WRITE through iov
94   iov[i].iov_base = bufs[i].data(); 94   iov[i].iov_base = bufs[i].data();
95   iov[i].iov_len = bufs[i].size(); 95   iov[i].iov_len = bufs[i].size();
96   } 96   }
97   writev(fd, iov, n); // read-only 97   writev(fd, iov, n); // read-only
98   } 98   }
99   99  
100   // For read operations (mutable buffers): 100   // For read operations (mutable buffers):
101   void submit_read(buffer_param p) 101   void submit_read(buffer_param p)
102   { 102   {
103   capy::mutable_buffer bufs[8]; 103   capy::mutable_buffer bufs[8];
104   auto n = p.copy_to(bufs, 8); 104   auto n = p.copy_to(bufs, 8);
105   iovec iov[8]; 105   iovec iov[8];
106   for (std::size_t i = 0; i < n; ++i) 106   for (std::size_t i = 0; i < n; ++i)
107   { 107   {
108   // bufs[] references mutable data - safe to write 108   // bufs[] references mutable data - safe to write
109   iov[i].iov_base = bufs[i].data(); 109   iov[i].iov_base = bufs[i].data();
110   iov[i].iov_len = bufs[i].size(); 110   iov[i].iov_len = bufs[i].size();
111   } 111   }
112   readv(fd, iov, n); // writing 112   readv(fd, iov, n); // writing
113   } 113   }
114   @endcode 114   @endcode
115   115  
116   @par Correct Usage 116   @par Correct Usage
117   117  
118   The implementation receiving `buffer_param` MUST: 118   The implementation receiving `buffer_param` MUST:
119   119  
120   @li Call `copy_to` immediately upon receiving the parameter 120   @li Call `copy_to` immediately upon receiving the parameter
121   @li Use the unrolled buffer descriptors for the I/O operation 121   @li Use the unrolled buffer descriptors for the I/O operation
122   @li Never store the `buffer_param` object itself 122   @li Never store the `buffer_param` object itself
123   @li Never store pointers obtained from `copy_to` beyond the 123   @li Never store pointers obtained from `copy_to` beyond the
124   immediate I/O operation 124   immediate I/O operation
125   125  
126   @par Example: Correct Usage 126   @par Example: Correct Usage
127   127  
128   @code 128   @code
129   // Templated awaitable at the call site 129   // Templated awaitable at the call site
130   template<class Buffers> 130   template<class Buffers>
131   struct write_awaitable 131   struct write_awaitable
132   { 132   {
133   Buffers bufs; 133   Buffers bufs;
134   io_stream* stream; 134   io_stream* stream;
135   135  
136   bool await_ready() { return false; } 136   bool await_ready() { return false; }
137   137  
138   void await_suspend(std::coroutine_handle<> h) 138   void await_suspend(std::coroutine_handle<> h)
139   { 139   {
140   // CORRECT: Pass to virtual interface while suspended. 140   // CORRECT: Pass to virtual interface while suspended.
141   // The buffer sequence 'bufs' remains valid because 141   // The buffer sequence 'bufs' remains valid because
142   // coroutine parameters live until resumption. 142   // coroutine parameters live until resumption.
143   stream->async_write_some_impl(bufs, h); 143   stream->async_write_some_impl(bufs, h);
144   } 144   }
145   145  
146   io_result await_resume() { return stream->get_result(); } 146   io_result await_resume() { return stream->get_result(); }
147   }; 147   };
148   148  
149   // Virtual implementation - unrolls immediately 149   // Virtual implementation - unrolls immediately
150   void stream_impl::async_write_some_impl( 150   void stream_impl::async_write_some_impl(
151   buffer_param p, 151   buffer_param p,
152   std::coroutine_handle<> h) 152   std::coroutine_handle<> h)
153   { 153   {
154   // CORRECT: Unroll immediately into platform structure. 154   // CORRECT: Unroll immediately into platform structure.
155   // Copy field by field; do NOT reinterpret_cast the 155   // Copy field by field; do NOT reinterpret_cast the
156   // mutable_buffer array to iovec* (see the @note above). 156   // mutable_buffer array to iovec* (see the @note above).
157   capy::mutable_buffer bufs[16]; 157   capy::mutable_buffer bufs[16];
158   std::size_t n = p.copy_to(bufs, 16); 158   std::size_t n = p.copy_to(bufs, 16);
159   iovec vecs[16]; 159   iovec vecs[16];
160   for (std::size_t i = 0; i < n; ++i) 160   for (std::size_t i = 0; i < n; ++i)
161   { 161   {
162   vecs[i].iov_base = bufs[i].data(); 162   vecs[i].iov_base = bufs[i].data();
163   vecs[i].iov_len = bufs[i].size(); 163   vecs[i].iov_len = bufs[i].size();
164   } 164   }
165   165  
166   // CORRECT: Use unrolled buffers for system call now 166   // CORRECT: Use unrolled buffers for system call now
167   submit_to_io_uring(vecs, n, h); 167   submit_to_io_uring(vecs, n, h);
168   168  
169   // After this function returns, 'p' must not be used again. 169   // After this function returns, 'p' must not be used again.
170   // The iovec array is safe because it contains copies of 170   // The iovec array is safe because it contains copies of
171   // the pointer/size pairs, not references to 'p'. 171   // the pointer/size pairs, not references to 'p'.
172   } 172   }
173   @endcode 173   @endcode
174   174  
175   @par UNSAFE USAGE: Storing buffer_param 175   @par UNSAFE USAGE: Storing buffer_param
176   176  
177   @warning Never store `buffer_param` for later use. 177   @warning Never store `buffer_param` for later use.
178   178  
179   @code 179   @code
180   class broken_stream 180   class broken_stream
181   { 181   {
182   buffer_param saved_param_; // UNSAFE: member storage 182   buffer_param saved_param_; // UNSAFE: member storage
183   183  
184   void async_write_impl(buffer_param p, ...) 184   void async_write_impl(buffer_param p, ...)
185   { 185   {
186   saved_param_ = p; // UNSAFE: storing for later 186   saved_param_ = p; // UNSAFE: storing for later
187   schedule_write_later(); 187   schedule_write_later();
188   } 188   }
189   189  
190   void do_write_later() 190   void do_write_later()
191   { 191   {
192   // UNSAFE: The calling coroutine may have resumed 192   // UNSAFE: The calling coroutine may have resumed
193   // or been destroyed. saved_param_ now references 193   // or been destroyed. saved_param_ now references
194   // invalid memory! 194   // invalid memory!
195   capy::mutable_buffer bufs[8]; 195   capy::mutable_buffer bufs[8];
196   saved_param_.copy_to(bufs, 8); // UNDEFINED BEHAVIOR 196   saved_param_.copy_to(bufs, 8); // UNDEFINED BEHAVIOR
197   } 197   }
198   }; 198   };
199   @endcode 199   @endcode
200   200  
201   @par UNSAFE USAGE: Storing Unrolled Pointers 201   @par UNSAFE USAGE: Storing Unrolled Pointers
202   202  
203   @warning The pointers obtained from `copy_to` point into the 203   @warning The pointers obtained from `copy_to` point into the
204   caller's buffer sequence. They become invalid when the caller 204   caller's buffer sequence. They become invalid when the caller
205   resumes. 205   resumes.
206   206  
207   @code 207   @code
208   class broken_stream 208   class broken_stream
209   { 209   {
210   capy::mutable_buffer saved_bufs_[8]; // UNSAFE 210   capy::mutable_buffer saved_bufs_[8]; // UNSAFE
211   std::size_t saved_count_; 211   std::size_t saved_count_;
212   212  
213   void async_write_impl(buffer_param p, ...) 213   void async_write_impl(buffer_param p, ...)
214   { 214   {
215   // This copies pointer/size pairs into saved_bufs_ 215   // This copies pointer/size pairs into saved_bufs_
216   saved_count_ = p.copy_to(saved_bufs_, 8); 216   saved_count_ = p.copy_to(saved_bufs_, 8);
217   217  
218   // UNSAFE: scheduling for later while storing the 218   // UNSAFE: scheduling for later while storing the
219   // buffer descriptors. The pointers in saved_bufs_ 219   // buffer descriptors. The pointers in saved_bufs_
220   // will dangle when the caller resumes! 220   // will dangle when the caller resumes!
221   schedule_for_later(); 221   schedule_for_later();
222   } 222   }
223   223  
224   void later() 224   void later()
225   { 225   {
226   // UNSAFE: saved_bufs_ contains dangling pointers 226   // UNSAFE: saved_bufs_ contains dangling pointers
227   for(std::size_t i = 0; i < saved_count_; ++i) 227   for(std::size_t i = 0; i < saved_count_; ++i)
228   write(fd_, saved_bufs_[i].data(), ...); // UB 228   write(fd_, saved_bufs_[i].data(), ...); // UB
229   } 229   }
230   }; 230   };
231   @endcode 231   @endcode
232   232  
233   @par UNSAFE USAGE: Using Outside a Coroutine 233   @par UNSAFE USAGE: Using Outside a Coroutine
234   234  
235   @warning This class relies on coroutine lifetime semantics. 235   @warning This class relies on coroutine lifetime semantics.
236   Using it with callbacks or non-coroutine async patterns is 236   Using it with callbacks or non-coroutine async patterns is
237   undefined behavior. 237   undefined behavior.
238   238  
239   @code 239   @code
240   // UNSAFE: No coroutine lifetime guarantee 240   // UNSAFE: No coroutine lifetime guarantee
241   void bad_callback_pattern(std::vector<char>& data) 241   void bad_callback_pattern(std::vector<char>& data)
242   { 242   {
243   capy::mutable_buffer buf(data.data(), data.size()); 243   capy::mutable_buffer buf(data.data(), data.size());
244   244  
245   // UNSAFE: In a callback model, 'buf' may go out of scope 245   // UNSAFE: In a callback model, 'buf' may go out of scope
246   // before the callback fires. There is no coroutine 246   // before the callback fires. There is no coroutine
247   // suspension to extend the lifetime. 247   // suspension to extend the lifetime.
248   stream.async_write(buf, [](error_code ec) { 248   stream.async_write(buf, [](error_code ec) {
249   // 'buf' is already destroyed! 249   // 'buf' is already destroyed!
250   }); 250   });
251   } 251   }
252   @endcode 252   @endcode
253   253  
254   @par UNSAFE USAGE: Passing to Another Coroutine 254   @par UNSAFE USAGE: Passing to Another Coroutine
255   255  
256   @warning Do not pass `buffer_param` to a different coroutine 256   @warning Do not pass `buffer_param` to a different coroutine
257   or spawn a new coroutine that captures it. 257   or spawn a new coroutine that captures it.
258   258  
259   @code 259   @code
260   void broken_impl(buffer_param p, std::coroutine_handle<> h) 260   void broken_impl(buffer_param p, std::coroutine_handle<> h)
261   { 261   {
262   // UNSAFE: Spawning a new coroutine that captures 'p'. 262   // UNSAFE: Spawning a new coroutine that captures 'p'.
263   // The original coroutine may resume before this new 263   // The original coroutine may resume before this new
264   // coroutine uses 'p'. 264   // coroutine uses 'p'.
265   co_spawn([p]() -> task<void> { 265   co_spawn([p]() -> task<void> {
266   capy::mutable_buffer bufs[8]; 266   capy::mutable_buffer bufs[8];
267   p.copy_to(bufs, 8); // UNSAFE: original caller may 267   p.copy_to(bufs, 8); // UNSAFE: original caller may
268   // have resumed already! 268   // have resumed already!
269   co_return; 269   co_return;
270   }); 270   });
271   } 271   }
272   @endcode 272   @endcode
273   273  
274   @par UNSAFE USAGE: Multiple Virtual Hops 274   @par UNSAFE USAGE: Multiple Virtual Hops
275   275  
276   @warning Minimize indirection. Each virtual call that passes 276   @warning Minimize indirection. Each virtual call that passes
277   `buffer_param` without immediately unrolling it increases 277   `buffer_param` without immediately unrolling it increases
278   the risk of misuse. 278   the risk of misuse.
279   279  
280   @code 280   @code
281   // Risky: multiple hops before unrolling 281   // Risky: multiple hops before unrolling
282   void layer1(buffer_param p) { 282   void layer1(buffer_param p) {
283   layer2(p); // Still haven't unrolled... 283   layer2(p); // Still haven't unrolled...
284   } 284   }
285   void layer2(buffer_param p) { 285   void layer2(buffer_param p) {
286   layer3(p); // Still haven't unrolled... 286   layer3(p); // Still haven't unrolled...
287   } 287   }
288   void layer3(buffer_param p) { 288   void layer3(buffer_param p) {
289   // Finally unrolling, but the chain is fragile. 289   // Finally unrolling, but the chain is fragile.
290   // Any intermediate layer storing 'p' breaks everything. 290   // Any intermediate layer storing 'p' breaks everything.
291   } 291   }
292   @endcode 292   @endcode
293   293  
294   @par UNSAFE USAGE: Fire-and-Forget Operations 294   @par UNSAFE USAGE: Fire-and-Forget Operations
295   295  
296   @warning Do not use with detached or fire-and-forget async 296   @warning Do not use with detached or fire-and-forget async
297   operations where there is no guarantee the caller remains 297   operations where there is no guarantee the caller remains
298   suspended. 298   suspended.
299   299  
300   @code 300   @code
301   task<void> caller() 301   task<void> caller()
302   { 302   {
303   char buf[1024]; 303   char buf[1024];
304   // UNSAFE: If async_write is fire-and-forget (doesn't 304   // UNSAFE: If async_write is fire-and-forget (doesn't
305   // actually suspend the caller), 'buf' may be destroyed 305   // actually suspend the caller), 'buf' may be destroyed
306   // before the I/O completes. 306   // before the I/O completes.
307   stream.async_write_detached(capy::mutable_buffer(buf, 1024)); 307   stream.async_write_detached(capy::mutable_buffer(buf, 1024));
308   // Returns immediately - 'buf' goes out of scope! 308   // Returns immediately - 'buf' goes out of scope!
309   } 309   }
310   @endcode 310   @endcode
311   311  
312   @par Passing Convention 312   @par Passing Convention
313   313  
314   Pass by value. The class contains only two pointers (16 bytes 314   Pass by value. The class contains only two pointers (16 bytes
315   on 64-bit systems), making copies trivial and clearly 315   on 64-bit systems), making copies trivial and clearly
316   communicating the lightweight, transient nature of this type. 316   communicating the lightweight, transient nature of this type.
317   317  
318   @code 318   @code
319   // Preferred: pass by value 319   // Preferred: pass by value
320   void process(buffer_param buffers); 320   void process(buffer_param buffers);
321   321  
322   // Also acceptable: pass by const reference 322   // Also acceptable: pass by const reference
323   void process(buffer_param const& buffers); 323   void process(buffer_param const& buffers);
324   @endcode 324   @endcode
325   325  
326   @see capy::ConstBufferSequence, capy::MutableBufferSequence 326   @see capy::ConstBufferSequence, capy::MutableBufferSequence
327   */ 327   */
328   class buffer_param 328   class buffer_param
329   { 329   {
330   public: 330   public:
331   /** Construct from a const buffer sequence. 331   /** Construct from a const buffer sequence.
332   332  
333   @param bs The buffer sequence to adapt. 333   @param bs The buffer sequence to adapt.
334   */ 334   */
335   template<capy::ConstBufferSequence BS> 335   template<capy::ConstBufferSequence BS>
HITCBC 336   553991 buffer_param(BS const& bs) noexcept : bs_(&bs) 336   420149 buffer_param(BS const& bs) noexcept : bs_(&bs)
HITCBC 337   553991 , fn_(&copy_impl<BS>) 337   420149 , fn_(&copy_impl<BS>)
338   { 338   {
HITCBC 339   553991 } 339   420149 }
340   340  
341   /** Fill an array with buffers from the sequence. 341   /** Fill an array with buffers from the sequence.
342   342  
343   Copies buffer descriptors from the sequence into the 343   Copies buffer descriptors from the sequence into the
344   destination array, skipping any zero-size buffers. 344   destination array, skipping any zero-size buffers.
345   This ensures the output contains only buffers with 345   This ensures the output contains only buffers with
346   actual data, suitable for direct use with system calls. 346   actual data, suitable for direct use with system calls.
347   347  
348   @param dest Pointer to array of mutable buffer descriptors. 348   @param dest Pointer to array of mutable buffer descriptors.
349   @param n Maximum number of buffers to copy. 349   @param n Maximum number of buffers to copy.
350   350  
351   @return The number of non-zero buffers copied. 351   @return The number of non-zero buffers copied.
352   */ 352   */
353   std::size_t 353   std::size_t
HITCBC 354   553991 copy_to(capy::mutable_buffer* dest, std::size_t n) const noexcept 354   420149 copy_to(capy::mutable_buffer* dest, std::size_t n) const noexcept
355   { 355   {
HITCBC 356   553991 return fn_(bs_, dest, n); 356   420149 return fn_(bs_, dest, n);
357   } 357   }
358   358  
359   private: 359   private:
360   template<capy::ConstBufferSequence BS> 360   template<capy::ConstBufferSequence BS>
361   static std::size_t 361   static std::size_t
HITCBC 362   553991 copy_impl(void const* p, capy::mutable_buffer* dest, std::size_t n) 362   420149 copy_impl(void const* p, capy::mutable_buffer* dest, std::size_t n)
363   { 363   {
HITCBC 364   553991 auto const& bs = *static_cast<BS const*>(p); 364   420149 auto const& bs = *static_cast<BS const*>(p);
HITCBC 365   553991 auto it = capy::begin(bs); 365   420149 auto it = capy::begin(bs);
HITCBC 366   553991 auto const end_it = capy::end(bs); 366   420149 auto const end_it = capy::end(bs);
367   367  
HITCBC 368   553991 std::size_t i = 0; 368   420149 std::size_t i = 0;
369   if constexpr (capy::MutableBufferSequence<BS>) 369   if constexpr (capy::MutableBufferSequence<BS>)
370   { 370   {
HITCBC 371   554856 for (; it != end_it && i < n; ++it) 371   421008 for (; it != end_it && i < n; ++it)
372   { 372   {
HITCBC 373   277433 capy::mutable_buffer buf(*it); 373   210509 capy::mutable_buffer buf(*it);
HITCBC 374   277433 if (buf.size() == 0) 374   210509 if (buf.size() == 0)
HITCBC 375   17 continue; 375   17 continue;
HITCBC 376   277416 dest[i++] = buf; 376   210492 dest[i++] = buf;
377   } 377   }
378   } 378   }
379   else 379   else
380   { 380   {
HITCBC 381   553158 for (; it != end_it && i < n; ++it) 381   419322 for (; it != end_it && i < n; ++it)
382   { 382   {
HITCBC 383   276590 capy::const_buffer buf(*it); 383   209672 capy::const_buffer buf(*it);
HITCBC 384   276590 if (buf.size() == 0) 384   209672 if (buf.size() == 0)
HITCBC 385   24 continue; 385   24 continue;
HITCBC 386   553132 dest[i++] = capy::mutable_buffer( 386   419296 dest[i++] = capy::mutable_buffer(
HITCBC 387   276566 const_cast<char*>(static_cast<char const*>(buf.data())), 387   209648 const_cast<char*>(static_cast<char const*>(buf.data())),
388   buf.size()); 388   buf.size());
389   } 389   }
390   } 390   }
HITCBC 391   553991 return i; 391   420149 return i;
392   } 392   }
393   393  
394   using fn_t = 394   using fn_t =
395   std::size_t (*)(void const*, capy::mutable_buffer*, std::size_t); 395   std::size_t (*)(void const*, capy::mutable_buffer*, std::size_t);
396   396  
397   void const* bs_; 397   void const* bs_;
398   fn_t fn_; 398   fn_t fn_;
399   }; 399   };
400   400  
401   } // namespace boost::corosio 401   } // namespace boost::corosio
402   402  
403   #endif 403   #endif