100.00% Lines (282/282) 100.00% Functions (74/74)
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   // 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   #ifndef BOOST_CAPY_IO_ANY_BUFFER_SINK_HPP 10   #ifndef BOOST_CAPY_IO_ANY_BUFFER_SINK_HPP
11   #define BOOST_CAPY_IO_ANY_BUFFER_SINK_HPP 11   #define BOOST_CAPY_IO_ANY_BUFFER_SINK_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/detail/await_suspend_helper.hpp> 14   #include <boost/capy/detail/await_suspend_helper.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/buffers/buffer_copy.hpp> 16   #include <boost/capy/buffers/buffer_copy.hpp>
17   #include <boost/capy/buffers/buffer_param.hpp> 17   #include <boost/capy/buffers/buffer_param.hpp>
18   #include <boost/capy/concept/buffer_sink.hpp> 18   #include <boost/capy/concept/buffer_sink.hpp>
19   #include <boost/capy/concept/io_awaitable.hpp> 19   #include <boost/capy/concept/io_awaitable.hpp>
20   #include <boost/capy/concept/write_sink.hpp> 20   #include <boost/capy/concept/write_sink.hpp>
21   #include <boost/capy/ex/io_env.hpp> 21   #include <boost/capy/ex/io_env.hpp>
22   #include <boost/capy/io_result.hpp> 22   #include <boost/capy/io_result.hpp>
23   #include <boost/capy/io_task.hpp> 23   #include <boost/capy/io_task.hpp>
24   24  
25   #include <concepts> 25   #include <concepts>
26   #include <coroutine> 26   #include <coroutine>
27   #include <cstddef> 27   #include <cstddef>
28   #include <exception> 28   #include <exception>
29   #include <new> 29   #include <new>
30   #include <span> 30   #include <span>
31   #include <stop_token> 31   #include <stop_token>
32   #include <system_error> 32   #include <system_error>
33   #include <utility> 33   #include <utility>
34   34  
35   namespace boost { 35   namespace boost {
36   namespace capy { 36   namespace capy {
37   37  
38   /** Type-erased wrapper for any BufferSink. 38   /** Type-erased wrapper for any BufferSink.
39   39  
40   This class provides type erasure for any type satisfying the 40   This class provides type erasure for any type satisfying the
41   @ref BufferSink concept, enabling runtime polymorphism for 41   @ref BufferSink concept, enabling runtime polymorphism for
42   buffer sink operations. It uses cached awaitable storage to achieve 42   buffer sink operations. It uses cached awaitable storage to achieve
43   zero steady-state allocation after construction. 43   zero steady-state allocation after construction.
44   44  
45   The wrapper exposes two interfaces for producing data: 45   The wrapper exposes two interfaces for producing data:
46   the @ref BufferSink interface (`prepare`, `commit`, `commit_eof`) 46   the @ref BufferSink interface (`prepare`, `commit`, `commit_eof`)
47   and the @ref WriteSink interface (`write_some`, `write`, 47   and the @ref WriteSink interface (`write_some`, `write`,
48   `write_eof`). Choose the interface that matches how your data 48   `write_eof`). Choose the interface that matches how your data
49   is produced: 49   is produced:
50   50  
51   @par Choosing an Interface 51   @par Choosing an Interface
52   52  
53   Use the **BufferSink** interface when you are a generator that 53   Use the **BufferSink** interface when you are a generator that
54   produces data into externally-provided buffers. The sink owns 54   produces data into externally-provided buffers. The sink owns
55   the memory; you call @ref prepare to obtain writable buffers, 55   the memory; you call @ref prepare to obtain writable buffers,
56   fill them, then call @ref commit or @ref commit_eof. 56   fill them, then call @ref commit or @ref commit_eof.
57   57  
58   Use the **WriteSink** interface when you already have buffers 58   Use the **WriteSink** interface when you already have buffers
59   containing the data to write: 59   containing the data to write:
60   - If the entire body is available up front, call 60   - If the entire body is available up front, call
61   @ref write_eof(buffers) to send everything atomically. 61   @ref write_eof(buffers) to send everything atomically.
62   - If data arrives incrementally, call @ref write or 62   - If data arrives incrementally, call @ref write or
63   @ref write_some in a loop, then @ref write_eof() when done. 63   @ref write_some in a loop, then @ref write_eof() when done.
64   Prefer `write` (complete) unless your streaming pattern 64   Prefer `write` (complete) unless your streaming pattern
65   benefits from partial writes via `write_some`. 65   benefits from partial writes via `write_some`.
66   66  
67   If the wrapped type only satisfies @ref BufferSink, the 67   If the wrapped type only satisfies @ref BufferSink, the
68   @ref WriteSink operations are provided automatically. 68   @ref WriteSink operations are provided automatically.
69   69  
70   @par Construction Modes 70   @par Construction Modes
71   71  
72   - **Owning**: Pass by value to transfer ownership. The wrapper 72   - **Owning**: Pass by value to transfer ownership. The wrapper
73   allocates storage and owns the sink. 73   allocates storage and owns the sink.
74   - **Reference**: Pass a pointer to wrap without ownership. The 74   - **Reference**: Pass a pointer to wrap without ownership. The
75   pointed-to sink must outlive this wrapper. 75   pointed-to sink must outlive this wrapper.
76   76  
77   @par Awaitable Preallocation 77   @par Awaitable Preallocation
78   The constructor preallocates storage for the type-erased awaitable. 78   The constructor preallocates storage for the type-erased awaitable.
79   This reserves all virtual address space at server startup 79   This reserves all virtual address space at server startup
80   so memory usage can be measured up front, rather than 80   so memory usage can be measured up front, rather than
81   allocating piecemeal as traffic arrives. 81   allocating piecemeal as traffic arrives.
82   82  
83   @par Thread Safety 83   @par Thread Safety
84   Not thread-safe. Concurrent operations on the same wrapper 84   Not thread-safe. Concurrent operations on the same wrapper
85   are undefined behavior. 85   are undefined behavior.
86   86  
87   @par Example 87   @par Example
88   @code 88   @code
89   // Owning - takes ownership of the sink 89   // Owning - takes ownership of the sink
90   any_buffer_sink abs(some_buffer_sink{args...}); 90   any_buffer_sink abs(some_buffer_sink{args...});
91   91  
92   // Reference - wraps without ownership 92   // Reference - wraps without ownership
93   some_buffer_sink sink; 93   some_buffer_sink sink;
94   any_buffer_sink abs(&sink); 94   any_buffer_sink abs(&sink);
95   95  
96   // BufferSink interface: generate into callee-owned buffers 96   // BufferSink interface: generate into callee-owned buffers
97   mutable_buffer arr[16]; 97   mutable_buffer arr[16];
98   auto bufs = abs.prepare(arr); 98   auto bufs = abs.prepare(arr);
99   // Write data into bufs[0..bufs.size()) 99   // Write data into bufs[0..bufs.size())
100   auto [ec] = co_await abs.commit(bytes_written); 100   auto [ec] = co_await abs.commit(bytes_written);
101   auto [ec2] = co_await abs.commit_eof(0); 101   auto [ec2] = co_await abs.commit_eof(0);
102   102  
103   // WriteSink interface: send caller-owned buffers 103   // WriteSink interface: send caller-owned buffers
104   auto [ec3, n] = co_await abs.write(make_buffer("hello", 5)); 104   auto [ec3, n] = co_await abs.write(make_buffer("hello", 5));
105   auto [ec4] = co_await abs.write_eof(); 105   auto [ec4] = co_await abs.write_eof();
106   106  
107   // Or send everything at once 107   // Or send everything at once
108   auto [ec5, n2] = co_await abs.write_eof( 108   auto [ec5, n2] = co_await abs.write_eof(
109   make_buffer(body_data)); 109   make_buffer(body_data));
110   @endcode 110   @endcode
111   111  
112   @see any_buffer_source, BufferSink, WriteSink 112   @see any_buffer_source, BufferSink, WriteSink
113   */ 113   */
114   class any_buffer_sink 114   class any_buffer_sink
115   { 115   {
116   struct vtable; 116   struct vtable;
117   struct awaitable_ops; 117   struct awaitable_ops;
118   struct write_awaitable_ops; 118   struct write_awaitable_ops;
119   119  
120   template<BufferSink S> 120   template<BufferSink S>
121   struct vtable_for_impl; 121   struct vtable_for_impl;
122   122  
123   // hot-path members first for cache locality 123   // hot-path members first for cache locality
124   void* sink_ = nullptr; 124   void* sink_ = nullptr;
125   vtable const* vt_ = nullptr; 125   vtable const* vt_ = nullptr;
126   void* cached_awaitable_ = nullptr; 126   void* cached_awaitable_ = nullptr;
127   awaitable_ops const* active_ops_ = nullptr; 127   awaitable_ops const* active_ops_ = nullptr;
128   write_awaitable_ops const* active_write_ops_ = nullptr; 128   write_awaitable_ops const* active_write_ops_ = nullptr;
129   void* storage_ = nullptr; 129   void* storage_ = nullptr;
130   130  
131   public: 131   public:
132   /** Destructor. 132   /** Destructor.
133   133  
134   Destroys the owned sink (if any) and releases the cached 134   Destroys the owned sink (if any) and releases the cached
135   awaitable storage. 135   awaitable storage.
136   */ 136   */
137   ~any_buffer_sink(); 137   ~any_buffer_sink();
138   138  
139   /** Construct a default instance. 139   /** Construct a default instance.
140   140  
141   Constructs an empty wrapper. Operations on a default-constructed 141   Constructs an empty wrapper. Operations on a default-constructed
142   wrapper result in undefined behavior. 142   wrapper result in undefined behavior.
143   */ 143   */
144   any_buffer_sink() = default; 144   any_buffer_sink() = default;
145   145  
146   /** Non-copyable. 146   /** Non-copyable.
147   147  
148   The awaitable cache is per-instance and cannot be shared. 148   The awaitable cache is per-instance and cannot be shared.
149   */ 149   */
150   any_buffer_sink(any_buffer_sink const&) = delete; 150   any_buffer_sink(any_buffer_sink const&) = delete;
151   any_buffer_sink& operator=(any_buffer_sink const&) = delete; 151   any_buffer_sink& operator=(any_buffer_sink const&) = delete;
152   152  
153   /** Construct by moving. 153   /** Construct by moving.
154   154  
155   Transfers ownership of the wrapped sink (if owned) and 155   Transfers ownership of the wrapped sink (if owned) and
156   cached awaitable storage from `other`. After the move, `other` is 156   cached awaitable storage from `other`. After the move, `other` is
157   in a default-constructed state. 157   in a default-constructed state.
158   158  
159   @param other The wrapper to move from. 159   @param other The wrapper to move from.
160   */ 160   */
HITCBC 161   2 any_buffer_sink(any_buffer_sink&& other) noexcept 161   2 any_buffer_sink(any_buffer_sink&& other) noexcept
HITCBC 162   2 : sink_(std::exchange(other.sink_, nullptr)) 162   2 : sink_(std::exchange(other.sink_, nullptr))
HITCBC 163   2 , vt_(std::exchange(other.vt_, nullptr)) 163   2 , vt_(std::exchange(other.vt_, nullptr))
HITCBC 164   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr)) 164   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr))
HITCBC 165   2 , active_ops_(std::exchange(other.active_ops_, nullptr)) 165   2 , active_ops_(std::exchange(other.active_ops_, nullptr))
HITCBC 166   2 , active_write_ops_(std::exchange(other.active_write_ops_, nullptr)) 166   2 , active_write_ops_(std::exchange(other.active_write_ops_, nullptr))
HITCBC 167   2 , storage_(std::exchange(other.storage_, nullptr)) 167   2 , storage_(std::exchange(other.storage_, nullptr))
168   { 168   {
HITCBC 169   2 } 169   2 }
170   170  
171   /** Assign by moving. 171   /** Assign by moving.
172   172  
173   Destroys any owned sink and releases existing resources, 173   Destroys any owned sink and releases existing resources,
174   then transfers ownership from `other`. 174   then transfers ownership from `other`.
175   175  
176   @param other The wrapper to move from. 176   @param other The wrapper to move from.
177   @return Reference to this wrapper. 177   @return Reference to this wrapper.
178   */ 178   */
179   any_buffer_sink& 179   any_buffer_sink&
180   operator=(any_buffer_sink&& other) noexcept; 180   operator=(any_buffer_sink&& other) noexcept;
181   181  
182   /** Construct by taking ownership of a BufferSink. 182   /** Construct by taking ownership of a BufferSink.
183   183  
184   Allocates storage and moves the sink into this wrapper. 184   Allocates storage and moves the sink into this wrapper.
185   The wrapper owns the sink and will destroy it. If `S` also 185   The wrapper owns the sink and will destroy it. If `S` also
186   satisfies @ref WriteSink, native write operations are 186   satisfies @ref WriteSink, native write operations are
187   forwarded through the virtual boundary. 187   forwarded through the virtual boundary.
188   188  
189   @param s The sink to take ownership of. 189   @param s The sink to take ownership of.
190   */ 190   */
191   template<BufferSink S> 191   template<BufferSink S>
192   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>) 192   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>)
193   any_buffer_sink(S s); 193   any_buffer_sink(S s);
194   194  
195   /** Construct by wrapping a BufferSink without ownership. 195   /** Construct by wrapping a BufferSink without ownership.
196   196  
197   Wraps the given sink by pointer. The sink must remain 197   Wraps the given sink by pointer. The sink must remain
198   valid for the lifetime of this wrapper. If `S` also 198   valid for the lifetime of this wrapper. If `S` also
199   satisfies @ref WriteSink, native write operations are 199   satisfies @ref WriteSink, native write operations are
200   forwarded through the virtual boundary. 200   forwarded through the virtual boundary.
201   201  
202   @param s Pointer to the sink to wrap. 202   @param s Pointer to the sink to wrap.
203   */ 203   */
204   template<BufferSink S> 204   template<BufferSink S>
205   any_buffer_sink(S* s); 205   any_buffer_sink(S* s);
206   206  
207   /** Check if the wrapper contains a valid sink. 207   /** Check if the wrapper contains a valid sink.
208   208  
209   @return `true` if wrapping a sink, `false` if default-constructed 209   @return `true` if wrapping a sink, `false` if default-constructed
210   or moved-from. 210   or moved-from.
211   */ 211   */
212   bool 212   bool
HITCBC 213   26 has_value() const noexcept 213   26 has_value() const noexcept
214   { 214   {
HITCBC 215   26 return sink_ != nullptr; 215   26 return sink_ != nullptr;
216   } 216   }
217   217  
218   /** Check if the wrapper contains a valid sink. 218   /** Check if the wrapper contains a valid sink.
219   219  
220   @return `true` if wrapping a sink, `false` if default-constructed 220   @return `true` if wrapping a sink, `false` if default-constructed
221   or moved-from. 221   or moved-from.
222   */ 222   */
223   explicit 223   explicit
HITCBC 224   3 operator bool() const noexcept 224   3 operator bool() const noexcept
225   { 225   {
HITCBC 226   3 return has_value(); 226   3 return has_value();
227   } 227   }
228   228  
229   /** Prepare writable buffers. 229   /** Prepare writable buffers.
230   230  
231   Fills the provided span with mutable buffer descriptors 231   Fills the provided span with mutable buffer descriptors
232   pointing to the underlying sink's internal storage. This 232   pointing to the underlying sink's internal storage. This
233   operation is synchronous. 233   operation is synchronous.
234   234  
235   @param dest Span of mutable_buffer to fill. 235   @param dest Span of mutable_buffer to fill.
236   236  
237   @return A span of filled buffers. 237   @return A span of filled buffers.
238   238  
239   @par Preconditions 239   @par Preconditions
240   The wrapper must contain a valid sink (`has_value() == true`). 240   The wrapper must contain a valid sink (`has_value() == true`).
241   */ 241   */
242   std::span<mutable_buffer> 242   std::span<mutable_buffer>
243   prepare(std::span<mutable_buffer> dest); 243   prepare(std::span<mutable_buffer> dest);
244   244  
245   /** Commit bytes written to the prepared buffers. 245   /** Commit bytes written to the prepared buffers.
246   246  
247   Commits `n` bytes written to the buffers returned by the 247   Commits `n` bytes written to the buffers returned by the
248   most recent call to @ref prepare. The operation may trigger 248   most recent call to @ref prepare. The operation may trigger
249   underlying I/O. 249   underlying I/O.
250   250  
251   @param n The number of bytes to commit. 251   @param n The number of bytes to commit.
252   252  
253   @return An awaitable that await-returns `(error_code)`. 253   @return An awaitable that await-returns `(error_code)`.
254   254  
255   @par Preconditions 255   @par Preconditions
256   The wrapper must contain a valid sink (`has_value() == true`). 256   The wrapper must contain a valid sink (`has_value() == true`).
257   */ 257   */
258   auto 258   auto
259   commit(std::size_t n); 259   commit(std::size_t n);
260   260  
261   /** Commit final bytes and signal end-of-stream. 261   /** Commit final bytes and signal end-of-stream.
262   262  
263   Commits `n` bytes written to the buffers returned by the 263   Commits `n` bytes written to the buffers returned by the
264   most recent call to @ref prepare and finalizes the sink. 264   most recent call to @ref prepare and finalizes the sink.
265   After success, no further operations are permitted. 265   After success, no further operations are permitted.
266   266  
267   @param n The number of bytes to commit. 267   @param n The number of bytes to commit.
268   268  
269   @return An awaitable that await-returns `(error_code)`. 269   @return An awaitable that await-returns `(error_code)`.
270   270  
271   @par Preconditions 271   @par Preconditions
272   The wrapper must contain a valid sink (`has_value() == true`). 272   The wrapper must contain a valid sink (`has_value() == true`).
273   */ 273   */
274   auto 274   auto
275   commit_eof(std::size_t n); 275   commit_eof(std::size_t n);
276   276  
277   /** Write some data from a buffer sequence. 277   /** Write some data from a buffer sequence.
278   278  
279   Attempt to write up to `buffer_size( buffers )` bytes from 279   Attempt to write up to `buffer_size( buffers )` bytes from
280   the buffer sequence to the underlying sink. May consume less 280   the buffer sequence to the underlying sink. May consume less
281   than the full sequence. 281   than the full sequence.
282   282  
283   When the wrapped type provides native @ref WriteSink support, 283   When the wrapped type provides native @ref WriteSink support,
284   the operation forwards directly. Otherwise it is synthesized 284   the operation forwards directly. Otherwise it is synthesized
285   from @ref prepare and @ref commit with a buffer copy. 285   from @ref prepare and @ref commit with a buffer copy.
286   286  
287   @param buffers The buffer sequence to write. 287   @param buffers The buffer sequence to write.
288   288  
289   @return An awaitable that await-returns `(error_code,std::size_t)`. 289   @return An awaitable that await-returns `(error_code,std::size_t)`.
290   290  
291   @par Preconditions 291   @par Preconditions
292   The wrapper must contain a valid sink (`has_value() == true`). 292   The wrapper must contain a valid sink (`has_value() == true`).
293   */ 293   */
294   template<ConstBufferSequence CB> 294   template<ConstBufferSequence CB>
295   io_task<std::size_t> 295   io_task<std::size_t>
296   write_some(CB buffers); 296   write_some(CB buffers);
297   297  
298   /** Write all data from a buffer sequence. 298   /** Write all data from a buffer sequence.
299   299  
300   Writes all data from the buffer sequence to the underlying 300   Writes all data from the buffer sequence to the underlying
301   sink. This method satisfies the @ref WriteSink concept. 301   sink. This method satisfies the @ref WriteSink concept.
302   302  
303   When the wrapped type provides native @ref WriteSink support, 303   When the wrapped type provides native @ref WriteSink support,
304   each window is forwarded directly. Otherwise the data is 304   each window is forwarded directly. Otherwise the data is
305   copied into the sink via @ref prepare and @ref commit. 305   copied into the sink via @ref prepare and @ref commit.
306   306  
307   @param buffers The buffer sequence to write. 307   @param buffers The buffer sequence to write.
308   308  
309   @return An awaitable that await-returns `(error_code,std::size_t)`. 309   @return An awaitable that await-returns `(error_code,std::size_t)`.
310   310  
311   @par Preconditions 311   @par Preconditions
312   The wrapper must contain a valid sink (`has_value() == true`). 312   The wrapper must contain a valid sink (`has_value() == true`).
313   */ 313   */
314   template<ConstBufferSequence CB> 314   template<ConstBufferSequence CB>
315   io_task<std::size_t> 315   io_task<std::size_t>
316   write(CB buffers); 316   write(CB buffers);
317   317  
318   /** Atomically write data and signal end-of-stream. 318   /** Atomically write data and signal end-of-stream.
319   319  
320   Writes all data from the buffer sequence to the underlying 320   Writes all data from the buffer sequence to the underlying
321   sink and then signals end-of-stream. 321   sink and then signals end-of-stream.
322   322  
323   When the wrapped type provides native @ref WriteSink support, 323   When the wrapped type provides native @ref WriteSink support,
324   the final window is sent atomically via the underlying 324   the final window is sent atomically via the underlying
325   `write_eof(buffers)`. Otherwise the data is synthesized 325   `write_eof(buffers)`. Otherwise the data is synthesized
326   through @ref prepare, @ref commit, and @ref commit_eof. 326   through @ref prepare, @ref commit, and @ref commit_eof.
327   327  
328   @param buffers The buffer sequence to write. 328   @param buffers The buffer sequence to write.
329   329  
330   @return An awaitable that await-returns `(error_code,std::size_t)`. 330   @return An awaitable that await-returns `(error_code,std::size_t)`.
331   331  
332   @par Preconditions 332   @par Preconditions
333   The wrapper must contain a valid sink (`has_value() == true`). 333   The wrapper must contain a valid sink (`has_value() == true`).
334   */ 334   */
335   template<ConstBufferSequence CB> 335   template<ConstBufferSequence CB>
336   io_task<std::size_t> 336   io_task<std::size_t>
337   write_eof(CB buffers); 337   write_eof(CB buffers);
338   338  
339   /** Signal end-of-stream. 339   /** Signal end-of-stream.
340   340  
341   Indicates that no more data will be written to the sink. 341   Indicates that no more data will be written to the sink.
342   This method satisfies the @ref WriteSink concept. 342   This method satisfies the @ref WriteSink concept.
343   343  
344   When the wrapped type provides native @ref WriteSink support, 344   When the wrapped type provides native @ref WriteSink support,
345   the underlying `write_eof()` is called. Otherwise the 345   the underlying `write_eof()` is called. Otherwise the
346   operation is implemented as `commit_eof(0)`. 346   operation is implemented as `commit_eof(0)`.
347   347  
348   @return An awaitable that await-returns `(error_code)`. 348   @return An awaitable that await-returns `(error_code)`.
349   349  
350   @par Preconditions 350   @par Preconditions
351   The wrapper must contain a valid sink (`has_value() == true`). 351   The wrapper must contain a valid sink (`has_value() == true`).
352   */ 352   */
353   auto 353   auto
354   write_eof(); 354   write_eof();
355   355  
356   protected: 356   protected:
357   /** Rebind to a new sink after move. 357   /** Rebind to a new sink after move.
358   358  
359   Updates the internal pointer to reference a new sink object. 359   Updates the internal pointer to reference a new sink object.
360   Used by owning wrappers after move assignment when the owned 360   Used by owning wrappers after move assignment when the owned
361   object has moved to a new location. 361   object has moved to a new location.
362   362  
363   @param new_sink The new sink to bind to. Must be the same 363   @param new_sink The new sink to bind to. Must be the same
364   type as the original sink. 364   type as the original sink.
365   365  
366   @note Terminates if called with a sink of different type 366   @note Terminates if called with a sink of different type
367   than the original. 367   than the original.
368   */ 368   */
369   template<BufferSink S> 369   template<BufferSink S>
370   void 370   void
371   rebind(S& new_sink) noexcept 371   rebind(S& new_sink) noexcept
372   { 372   {
373   if(vt_ != &vtable_for_impl<S>::value) 373   if(vt_ != &vtable_for_impl<S>::value)
374   std::terminate(); 374   std::terminate();
375   sink_ = &new_sink; 375   sink_ = &new_sink;
376   } 376   }
377   377  
378   private: 378   private:
379   /** Forward a partial write through the vtable. 379   /** Forward a partial write through the vtable.
380   380  
381   Constructs the underlying `write_some` awaitable in 381   Constructs the underlying `write_some` awaitable in
382   cached storage and returns a type-erased awaitable. 382   cached storage and returns a type-erased awaitable.
383   */ 383   */
384   auto 384   auto
385   write_some_(std::span<const_buffer const> buffers); 385   write_some_(std::span<const_buffer const> buffers);
386   386  
387   /** Forward a complete write through the vtable. 387   /** Forward a complete write through the vtable.
388   388  
389   Constructs the underlying `write` awaitable in 389   Constructs the underlying `write` awaitable in
390   cached storage and returns a type-erased awaitable. 390   cached storage and returns a type-erased awaitable.
391   */ 391   */
392   auto 392   auto
393   write_(std::span<const_buffer const> buffers); 393   write_(std::span<const_buffer const> buffers);
394   394  
395   /** Forward an atomic write-with-EOF through the vtable. 395   /** Forward an atomic write-with-EOF through the vtable.
396   396  
397   Constructs the underlying `write_eof(buffers)` awaitable 397   Constructs the underlying `write_eof(buffers)` awaitable
398   in cached storage and returns a type-erased awaitable. 398   in cached storage and returns a type-erased awaitable.
399   */ 399   */
400   auto 400   auto
401   write_eof_buffers_(std::span<const_buffer const> buffers); 401   write_eof_buffers_(std::span<const_buffer const> buffers);
402   }; 402   };
403   403  
404   /** Type-erased ops for awaitables that await-return `io_result<>`. */ 404   /** Type-erased ops for awaitables that await-return `io_result<>`. */
405   struct any_buffer_sink::awaitable_ops 405   struct any_buffer_sink::awaitable_ops
406   { 406   {
407   bool (*await_ready)(void*); 407   bool (*await_ready)(void*);
408   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 408   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
409   io_result<> (*await_resume)(void*); 409   io_result<> (*await_resume)(void*);
410   void (*destroy)(void*) noexcept; 410   void (*destroy)(void*) noexcept;
411   }; 411   };
412   412  
413   /** Type-erased ops for awaitables that await-return `io_result<std::size_t>`. */ 413   /** Type-erased ops for awaitables that await-return `io_result<std::size_t>`. */
414   struct any_buffer_sink::write_awaitable_ops 414   struct any_buffer_sink::write_awaitable_ops
415   { 415   {
416   bool (*await_ready)(void*); 416   bool (*await_ready)(void*);
417   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 417   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
418   io_result<std::size_t> (*await_resume)(void*); 418   io_result<std::size_t> (*await_resume)(void*);
419   void (*destroy)(void*) noexcept; 419   void (*destroy)(void*) noexcept;
420   }; 420   };
421   421  
422   struct any_buffer_sink::vtable 422   struct any_buffer_sink::vtable
423   { 423   {
424   void (*destroy)(void*) noexcept; 424   void (*destroy)(void*) noexcept;
425   std::span<mutable_buffer> (*do_prepare)( 425   std::span<mutable_buffer> (*do_prepare)(
426   void* sink, 426   void* sink,
427   std::span<mutable_buffer> dest); 427   std::span<mutable_buffer> dest);
428   std::size_t awaitable_size; 428   std::size_t awaitable_size;
429   std::size_t awaitable_align; 429   std::size_t awaitable_align;
430   awaitable_ops const* (*construct_commit_awaitable)( 430   awaitable_ops const* (*construct_commit_awaitable)(
431   void* sink, 431   void* sink,
432   void* storage, 432   void* storage,
433   std::size_t n); 433   std::size_t n);
434   awaitable_ops const* (*construct_commit_eof_awaitable)( 434   awaitable_ops const* (*construct_commit_eof_awaitable)(
435   void* sink, 435   void* sink,
436   void* storage, 436   void* storage,
437   std::size_t n); 437   std::size_t n);
438   438  
439   // WriteSink forwarding (null when wrapped type is BufferSink-only) 439   // WriteSink forwarding (null when wrapped type is BufferSink-only)
440   write_awaitable_ops const* (*construct_write_some_awaitable)( 440   write_awaitable_ops const* (*construct_write_some_awaitable)(
441   void* sink, 441   void* sink,
442   void* storage, 442   void* storage,
443   std::span<const_buffer const> buffers); 443   std::span<const_buffer const> buffers);
444   write_awaitable_ops const* (*construct_write_awaitable)( 444   write_awaitable_ops const* (*construct_write_awaitable)(
445   void* sink, 445   void* sink,
446   void* storage, 446   void* storage,
447   std::span<const_buffer const> buffers); 447   std::span<const_buffer const> buffers);
448   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)( 448   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)(
449   void* sink, 449   void* sink,
450   void* storage, 450   void* storage,
451   std::span<const_buffer const> buffers); 451   std::span<const_buffer const> buffers);
452   awaitable_ops const* (*construct_write_eof_awaitable)( 452   awaitable_ops const* (*construct_write_eof_awaitable)(
453   void* sink, 453   void* sink,
454   void* storage); 454   void* storage);
455   }; 455   };
456   456  
457   template<BufferSink S> 457   template<BufferSink S>
458   struct any_buffer_sink::vtable_for_impl 458   struct any_buffer_sink::vtable_for_impl
459   { 459   {
460   using CommitAwaitable = decltype(std::declval<S&>().commit( 460   using CommitAwaitable = decltype(std::declval<S&>().commit(
461   std::size_t{})); 461   std::size_t{}));
462   using CommitEofAwaitable = decltype(std::declval<S&>().commit_eof( 462   using CommitEofAwaitable = decltype(std::declval<S&>().commit_eof(
463   std::size_t{})); 463   std::size_t{}));
464   464  
465   static void 465   static void
HITCBC 466   19 do_destroy_impl(void* sink) noexcept 466   19 do_destroy_impl(void* sink) noexcept
467   { 467   {
HITCBC 468   19 static_cast<S*>(sink)->~S(); 468   19 static_cast<S*>(sink)->~S();
HITCBC 469   19 } 469   19 }
470   470  
471   static std::span<mutable_buffer> 471   static std::span<mutable_buffer>
HITCBC 472   132 do_prepare_impl( 472   132 do_prepare_impl(
473   void* sink, 473   void* sink,
474   std::span<mutable_buffer> dest) 474   std::span<mutable_buffer> dest)
475   { 475   {
HITCBC 476   132 auto& s = *static_cast<S*>(sink); 476   132 auto& s = *static_cast<S*>(sink);
HITCBC 477   132 return s.prepare(dest); 477   132 return s.prepare(dest);
478   } 478   }
479   479  
480   static awaitable_ops const* 480   static awaitable_ops const*
HITCBC 481   110 construct_commit_awaitable_impl( 481   110 construct_commit_awaitable_impl(
482   void* sink, 482   void* sink,
483   void* storage, 483   void* storage,
484   std::size_t n) 484   std::size_t n)
485   { 485   {
HITCBC 486   110 auto& s = *static_cast<S*>(sink); 486   110 auto& s = *static_cast<S*>(sink);
HITCBC 487   110 ::new(storage) CommitAwaitable(s.commit(n)); 487   110 ::new(storage) CommitAwaitable(s.commit(n));
488   488  
489   static constexpr awaitable_ops ops = { 489   static constexpr awaitable_ops ops = {
HITCBC 490   110 +[](void* p) { 490   110 +[](void* p) {
HITCBC 491   110 return static_cast<CommitAwaitable*>(p)->await_ready(); 491   110 return static_cast<CommitAwaitable*>(p)->await_ready();
492   }, 492   },
HITCBC 493   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 493   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 494   1 return detail::call_await_suspend( 494   1 return detail::call_await_suspend(
HITCBC 495   1 static_cast<CommitAwaitable*>(p), h, env); 495   1 static_cast<CommitAwaitable*>(p), h, env);
496   }, 496   },
HITCBC 497   110 +[](void* p) { 497   110 +[](void* p) {
HITCBC 498   110 return static_cast<CommitAwaitable*>(p)->await_resume(); 498   110 return static_cast<CommitAwaitable*>(p)->await_resume();
499   }, 499   },
HITCBC 500   110 +[](void* p) noexcept { 500   110 +[](void* p) noexcept {
HITCBC 501   110 static_cast<CommitAwaitable*>(p)->~CommitAwaitable(); 501   110 static_cast<CommitAwaitable*>(p)->~CommitAwaitable();
502   } 502   }
503   }; 503   };
HITCBC 504   110 return &ops; 504   110 return &ops;
505   } 505   }
506   506  
507   static awaitable_ops const* 507   static awaitable_ops const*
HITCBC 508   71 construct_commit_eof_awaitable_impl( 508   71 construct_commit_eof_awaitable_impl(
509   void* sink, 509   void* sink,
510   void* storage, 510   void* storage,
511   std::size_t n) 511   std::size_t n)
512   { 512   {
HITCBC 513   71 auto& s = *static_cast<S*>(sink); 513   71 auto& s = *static_cast<S*>(sink);
HITCBC 514   71 ::new(storage) CommitEofAwaitable(s.commit_eof(n)); 514   71 ::new(storage) CommitEofAwaitable(s.commit_eof(n));
515   515  
516   static constexpr awaitable_ops ops = { 516   static constexpr awaitable_ops ops = {
HITCBC 517   71 +[](void* p) { 517   71 +[](void* p) {
HITCBC 518   71 return static_cast<CommitEofAwaitable*>(p)->await_ready(); 518   71 return static_cast<CommitEofAwaitable*>(p)->await_ready();
519   }, 519   },
HITCBC 520   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 520   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 521   1 return detail::call_await_suspend( 521   1 return detail::call_await_suspend(
HITCBC 522   1 static_cast<CommitEofAwaitable*>(p), h, env); 522   1 static_cast<CommitEofAwaitable*>(p), h, env);
523   }, 523   },
HITCBC 524   71 +[](void* p) { 524   71 +[](void* p) {
HITCBC 525   71 return static_cast<CommitEofAwaitable*>(p)->await_resume(); 525   71 return static_cast<CommitEofAwaitable*>(p)->await_resume();
526   }, 526   },
HITCBC 527   71 +[](void* p) noexcept { 527   71 +[](void* p) noexcept {
HITCBC 528   71 static_cast<CommitEofAwaitable*>(p)->~CommitEofAwaitable(); 528   71 static_cast<CommitEofAwaitable*>(p)->~CommitEofAwaitable();
529   } 529   }
530   }; 530   };
HITCBC 531   71 return &ops; 531   71 return &ops;
532   } 532   }
533   533  
534   static write_awaitable_ops const* 534   static write_awaitable_ops const*
HITCBC 535   7 construct_write_some_awaitable_impl( 535   7 construct_write_some_awaitable_impl(
536   void* sink, 536   void* sink,
537   void* storage, 537   void* storage,
538   std::span<const_buffer const> buffers) 538   std::span<const_buffer const> buffers)
539   requires WriteSink<S> 539   requires WriteSink<S>
540   { 540   {
541   using Aw = decltype(std::declval<S&>().write_some( 541   using Aw = decltype(std::declval<S&>().write_some(
542   std::span<const_buffer const>{})); 542   std::span<const_buffer const>{}));
HITCBC 543   7 auto& s = *static_cast<S*>(sink); 543   7 auto& s = *static_cast<S*>(sink);
HITCBC 544   7 ::new(storage) Aw(s.write_some(buffers)); 544   7 ::new(storage) Aw(s.write_some(buffers));
545   545  
546   static constexpr write_awaitable_ops ops = { 546   static constexpr write_awaitable_ops ops = {
HITCBC 547   7 +[](void* p) { 547   7 +[](void* p) {
HITCBC 548   7 return static_cast<Aw*>(p)->await_ready(); 548   7 return static_cast<Aw*>(p)->await_ready();
549   }, 549   },
HITCBC 550   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 550   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 551   1 return detail::call_await_suspend( 551   1 return detail::call_await_suspend(
HITCBC 552   1 static_cast<Aw*>(p), h, env); 552   1 static_cast<Aw*>(p), h, env);
553   }, 553   },
HITCBC 554   7 +[](void* p) { 554   7 +[](void* p) {
HITCBC 555   7 return static_cast<Aw*>(p)->await_resume(); 555   7 return static_cast<Aw*>(p)->await_resume();
556   }, 556   },
HITCBC 557   7 +[](void* p) noexcept { 557   7 +[](void* p) noexcept {
HITCBC 558   7 static_cast<Aw*>(p)->~Aw(); 558   7 static_cast<Aw*>(p)->~Aw();
559   } 559   }
560   }; 560   };
HITCBC 561   7 return &ops; 561   7 return &ops;
562   } 562   }
563   563  
564   static write_awaitable_ops const* 564   static write_awaitable_ops const*
HITCBC 565   15 construct_write_awaitable_impl( 565   15 construct_write_awaitable_impl(
566   void* sink, 566   void* sink,
567   void* storage, 567   void* storage,
568   std::span<const_buffer const> buffers) 568   std::span<const_buffer const> buffers)
569   requires WriteSink<S> 569   requires WriteSink<S>
570   { 570   {
571   using Aw = decltype(std::declval<S&>().write( 571   using Aw = decltype(std::declval<S&>().write(
572   std::span<const_buffer const>{})); 572   std::span<const_buffer const>{}));
HITCBC 573   15 auto& s = *static_cast<S*>(sink); 573   15 auto& s = *static_cast<S*>(sink);
HITCBC 574   15 ::new(storage) Aw(s.write(buffers)); 574   15 ::new(storage) Aw(s.write(buffers));
575   575  
576   static constexpr write_awaitable_ops ops = { 576   static constexpr write_awaitable_ops ops = {
HITCBC 577   15 +[](void* p) { 577   15 +[](void* p) {
HITCBC 578   15 return static_cast<Aw*>(p)->await_ready(); 578   15 return static_cast<Aw*>(p)->await_ready();
579   }, 579   },
HITCBC 580   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 580   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 581   1 return detail::call_await_suspend( 581   1 return detail::call_await_suspend(
HITCBC 582   1 static_cast<Aw*>(p), h, env); 582   1 static_cast<Aw*>(p), h, env);
583   }, 583   },
HITCBC 584   15 +[](void* p) { 584   15 +[](void* p) {
HITCBC 585   15 return static_cast<Aw*>(p)->await_resume(); 585   15 return static_cast<Aw*>(p)->await_resume();
586   }, 586   },
HITCBC 587   15 +[](void* p) noexcept { 587   15 +[](void* p) noexcept {
HITCBC 588   15 static_cast<Aw*>(p)->~Aw(); 588   15 static_cast<Aw*>(p)->~Aw();
589   } 589   }
590   }; 590   };
HITCBC 591   15 return &ops; 591   15 return &ops;
592   } 592   }
593   593  
594   static write_awaitable_ops const* 594   static write_awaitable_ops const*
HITCBC 595   13 construct_write_eof_buffers_awaitable_impl( 595   13 construct_write_eof_buffers_awaitable_impl(
596   void* sink, 596   void* sink,
597   void* storage, 597   void* storage,
598   std::span<const_buffer const> buffers) 598   std::span<const_buffer const> buffers)
599   requires WriteSink<S> 599   requires WriteSink<S>
600   { 600   {
601   using Aw = decltype(std::declval<S&>().write_eof( 601   using Aw = decltype(std::declval<S&>().write_eof(
602   std::span<const_buffer const>{})); 602   std::span<const_buffer const>{}));
HITCBC 603   13 auto& s = *static_cast<S*>(sink); 603   13 auto& s = *static_cast<S*>(sink);
HITCBC 604   13 ::new(storage) Aw(s.write_eof(buffers)); 604   13 ::new(storage) Aw(s.write_eof(buffers));
605   605  
606   static constexpr write_awaitable_ops ops = { 606   static constexpr write_awaitable_ops ops = {
HITCBC 607   13 +[](void* p) { 607   13 +[](void* p) {
HITCBC 608   13 return static_cast<Aw*>(p)->await_ready(); 608   13 return static_cast<Aw*>(p)->await_ready();
609   }, 609   },
HITCBC 610   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 610   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 611   1 return detail::call_await_suspend( 611   1 return detail::call_await_suspend(
HITCBC 612   1 static_cast<Aw*>(p), h, env); 612   1 static_cast<Aw*>(p), h, env);
613   }, 613   },
HITCBC 614   13 +[](void* p) { 614   13 +[](void* p) {
HITCBC 615   13 return static_cast<Aw*>(p)->await_resume(); 615   13 return static_cast<Aw*>(p)->await_resume();
616   }, 616   },
HITCBC 617   13 +[](void* p) noexcept { 617   13 +[](void* p) noexcept {
HITCBC 618   13 static_cast<Aw*>(p)->~Aw(); 618   13 static_cast<Aw*>(p)->~Aw();
619   } 619   }
620   }; 620   };
HITCBC 621   13 return &ops; 621   13 return &ops;
622   } 622   }
623   623  
624   static awaitable_ops const* 624   static awaitable_ops const*
HITCBC 625   17 construct_write_eof_awaitable_impl( 625   17 construct_write_eof_awaitable_impl(
626   void* sink, 626   void* sink,
627   void* storage) 627   void* storage)
628   requires WriteSink<S> 628   requires WriteSink<S>
629   { 629   {
630   using Aw = decltype(std::declval<S&>().write_eof()); 630   using Aw = decltype(std::declval<S&>().write_eof());
HITCBC 631   17 auto& s = *static_cast<S*>(sink); 631   17 auto& s = *static_cast<S*>(sink);
HITCBC 632   17 ::new(storage) Aw(s.write_eof()); 632   17 ::new(storage) Aw(s.write_eof());
633   633  
634   static constexpr awaitable_ops ops = { 634   static constexpr awaitable_ops ops = {
HITCBC 635   17 +[](void* p) { 635   17 +[](void* p) {
HITCBC 636   17 return static_cast<Aw*>(p)->await_ready(); 636   17 return static_cast<Aw*>(p)->await_ready();
637   }, 637   },
HITCBC 638   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 638   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 639   1 return detail::call_await_suspend( 639   1 return detail::call_await_suspend(
HITCBC 640   1 static_cast<Aw*>(p), h, env); 640   1 static_cast<Aw*>(p), h, env);
641   }, 641   },
HITCBC 642   17 +[](void* p) { 642   17 +[](void* p) {
HITCBC 643   17 return static_cast<Aw*>(p)->await_resume(); 643   17 return static_cast<Aw*>(p)->await_resume();
644   }, 644   },
HITCBC 645   17 +[](void* p) noexcept { 645   17 +[](void* p) noexcept {
HITCBC 646   17 static_cast<Aw*>(p)->~Aw(); 646   17 static_cast<Aw*>(p)->~Aw();
647   } 647   }
648   }; 648   };
HITCBC 649   17 return &ops; 649   17 return &ops;
650   } 650   }
651   651  
652   static consteval std::size_t 652   static consteval std::size_t
653   compute_max_size() noexcept 653   compute_max_size() noexcept
654   { 654   {
655   std::size_t s = sizeof(CommitAwaitable) > sizeof(CommitEofAwaitable) 655   std::size_t s = sizeof(CommitAwaitable) > sizeof(CommitEofAwaitable)
656   ? sizeof(CommitAwaitable) 656   ? sizeof(CommitAwaitable)
657   : sizeof(CommitEofAwaitable); 657   : sizeof(CommitEofAwaitable);
658   if constexpr (WriteSink<S>) 658   if constexpr (WriteSink<S>)
659   { 659   {
660   using WS = decltype(std::declval<S&>().write_some( 660   using WS = decltype(std::declval<S&>().write_some(
661   std::span<const_buffer const>{})); 661   std::span<const_buffer const>{}));
662   using W = decltype(std::declval<S&>().write( 662   using W = decltype(std::declval<S&>().write(
663   std::span<const_buffer const>{})); 663   std::span<const_buffer const>{}));
664   using WEB = decltype(std::declval<S&>().write_eof( 664   using WEB = decltype(std::declval<S&>().write_eof(
665   std::span<const_buffer const>{})); 665   std::span<const_buffer const>{}));
666   using WE = decltype(std::declval<S&>().write_eof()); 666   using WE = decltype(std::declval<S&>().write_eof());
667   667  
668   if(sizeof(WS) > s) s = sizeof(WS); 668   if(sizeof(WS) > s) s = sizeof(WS);
669   if(sizeof(W) > s) s = sizeof(W); 669   if(sizeof(W) > s) s = sizeof(W);
670   if(sizeof(WEB) > s) s = sizeof(WEB); 670   if(sizeof(WEB) > s) s = sizeof(WEB);
671   if(sizeof(WE) > s) s = sizeof(WE); 671   if(sizeof(WE) > s) s = sizeof(WE);
672   } 672   }
673   return s; 673   return s;
674   } 674   }
675   675  
676   static consteval std::size_t 676   static consteval std::size_t
677   compute_max_align() noexcept 677   compute_max_align() noexcept
678   { 678   {
679   std::size_t a = alignof(CommitAwaitable) > alignof(CommitEofAwaitable) 679   std::size_t a = alignof(CommitAwaitable) > alignof(CommitEofAwaitable)
680   ? alignof(CommitAwaitable) 680   ? alignof(CommitAwaitable)
681   : alignof(CommitEofAwaitable); 681   : alignof(CommitEofAwaitable);
682   if constexpr (WriteSink<S>) 682   if constexpr (WriteSink<S>)
683   { 683   {
684   using WS = decltype(std::declval<S&>().write_some( 684   using WS = decltype(std::declval<S&>().write_some(
685   std::span<const_buffer const>{})); 685   std::span<const_buffer const>{}));
686   using W = decltype(std::declval<S&>().write( 686   using W = decltype(std::declval<S&>().write(
687   std::span<const_buffer const>{})); 687   std::span<const_buffer const>{}));
688   using WEB = decltype(std::declval<S&>().write_eof( 688   using WEB = decltype(std::declval<S&>().write_eof(
689   std::span<const_buffer const>{})); 689   std::span<const_buffer const>{}));
690   using WE = decltype(std::declval<S&>().write_eof()); 690   using WE = decltype(std::declval<S&>().write_eof());
691   691  
692   if(alignof(WS) > a) a = alignof(WS); 692   if(alignof(WS) > a) a = alignof(WS);
693   if(alignof(W) > a) a = alignof(W); 693   if(alignof(W) > a) a = alignof(W);
694   if(alignof(WEB) > a) a = alignof(WEB); 694   if(alignof(WEB) > a) a = alignof(WEB);
695   if(alignof(WE) > a) a = alignof(WE); 695   if(alignof(WE) > a) a = alignof(WE);
696   } 696   }
697   return a; 697   return a;
698   } 698   }
699   699  
700   static consteval vtable 700   static consteval vtable
701   make_vtable() noexcept 701   make_vtable() noexcept
702   { 702   {
703   vtable v{}; 703   vtable v{};
704   v.destroy = &do_destroy_impl; 704   v.destroy = &do_destroy_impl;
705   v.do_prepare = &do_prepare_impl; 705   v.do_prepare = &do_prepare_impl;
706   v.awaitable_size = compute_max_size(); 706   v.awaitable_size = compute_max_size();
707   v.awaitable_align = compute_max_align(); 707   v.awaitable_align = compute_max_align();
708   v.construct_commit_awaitable = &construct_commit_awaitable_impl; 708   v.construct_commit_awaitable = &construct_commit_awaitable_impl;
709   v.construct_commit_eof_awaitable = &construct_commit_eof_awaitable_impl; 709   v.construct_commit_eof_awaitable = &construct_commit_eof_awaitable_impl;
710   v.construct_write_some_awaitable = nullptr; 710   v.construct_write_some_awaitable = nullptr;
711   v.construct_write_awaitable = nullptr; 711   v.construct_write_awaitable = nullptr;
712   v.construct_write_eof_buffers_awaitable = nullptr; 712   v.construct_write_eof_buffers_awaitable = nullptr;
713   v.construct_write_eof_awaitable = nullptr; 713   v.construct_write_eof_awaitable = nullptr;
714   714  
715   if constexpr (WriteSink<S>) 715   if constexpr (WriteSink<S>)
716   { 716   {
717   v.construct_write_some_awaitable = 717   v.construct_write_some_awaitable =
718   &construct_write_some_awaitable_impl; 718   &construct_write_some_awaitable_impl;
719   v.construct_write_awaitable = 719   v.construct_write_awaitable =
720   &construct_write_awaitable_impl; 720   &construct_write_awaitable_impl;
721   v.construct_write_eof_buffers_awaitable = 721   v.construct_write_eof_buffers_awaitable =
722   &construct_write_eof_buffers_awaitable_impl; 722   &construct_write_eof_buffers_awaitable_impl;
723   v.construct_write_eof_awaitable = 723   v.construct_write_eof_awaitable =
724   &construct_write_eof_awaitable_impl; 724   &construct_write_eof_awaitable_impl;
725   } 725   }
726   return v; 726   return v;
727   } 727   }
728   728  
729   static constexpr vtable value = make_vtable(); 729   static constexpr vtable value = make_vtable();
730   }; 730   };
731   731  
732   inline 732   inline
HITCBC 733   218 any_buffer_sink::~any_buffer_sink() 733   218 any_buffer_sink::~any_buffer_sink()
734   { 734   {
HITCBC 735   218 if(storage_) 735   218 if(storage_)
736   { 736   {
HITCBC 737   17 vt_->destroy(sink_); 737   17 vt_->destroy(sink_);
HITCBC 738   17 ::operator delete(storage_); 738   17 ::operator delete(storage_);
739   } 739   }
HITCBC 740   218 if(cached_awaitable_) 740   218 if(cached_awaitable_)
HITCBC 741   211 ::operator delete(cached_awaitable_); 741   211 ::operator delete(cached_awaitable_);
HITCBC 742   218 } 742   218 }
743   743  
744   inline any_buffer_sink& 744   inline any_buffer_sink&
HITCBC 745   5 any_buffer_sink::operator=(any_buffer_sink&& other) noexcept 745   5 any_buffer_sink::operator=(any_buffer_sink&& other) noexcept
746   { 746   {
HITCBC 747   5 if(this != &other) 747   5 if(this != &other)
748   { 748   {
HITCBC 749   4 if(storage_) 749   4 if(storage_)
750   { 750   {
HITCBC 751   1 vt_->destroy(sink_); 751   1 vt_->destroy(sink_);
HITCBC 752   1 ::operator delete(storage_); 752   1 ::operator delete(storage_);
753   } 753   }
HITCBC 754   4 if(cached_awaitable_) 754   4 if(cached_awaitable_)
HITCBC 755   2 ::operator delete(cached_awaitable_); 755   2 ::operator delete(cached_awaitable_);
HITCBC 756   4 sink_ = std::exchange(other.sink_, nullptr); 756   4 sink_ = std::exchange(other.sink_, nullptr);
HITCBC 757   4 vt_ = std::exchange(other.vt_, nullptr); 757   4 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 758   4 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 758   4 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 759   4 storage_ = std::exchange(other.storage_, nullptr); 759   4 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 760   4 active_ops_ = std::exchange(other.active_ops_, nullptr); 760   4 active_ops_ = std::exchange(other.active_ops_, nullptr);
HITCBC 761   4 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr); 761   4 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr);
762   } 762   }
HITCBC 763   5 return *this; 763   5 return *this;
764   } 764   }
765   765  
766   template<BufferSink S> 766   template<BufferSink S>
767   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>) 767   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>)
HITCBC 768   20 any_buffer_sink::any_buffer_sink(S s) 768   20 any_buffer_sink::any_buffer_sink(S s)
HITCBC 769   20 : vt_(&vtable_for_impl<S>::value) 769   20 : vt_(&vtable_for_impl<S>::value)
770   { 770   {
771   struct guard { 771   struct guard {
772   any_buffer_sink* self; 772   any_buffer_sink* self;
773   bool committed = false; 773   bool committed = false;
HITCBC 774   20 ~guard() { 774   20 ~guard() {
HITCBC 775   20 if(!committed && self->storage_) { 775   20 if(!committed && self->storage_) {
776   // sink_ is null if the sink move-ctor threw before 776   // sink_ is null if the sink move-ctor threw before
777   // the placement-new assigned it. 777   // the placement-new assigned it.
HITCBC 778   2 if(self->sink_) 778   2 if(self->sink_)
HITCBC 779   1 self->vt_->destroy(self->sink_); 779   1 self->vt_->destroy(self->sink_);
HITCBC 780   2 ::operator delete(self->storage_); 780   2 ::operator delete(self->storage_);
HITCBC 781   2 self->storage_ = nullptr; 781   2 self->storage_ = nullptr;
HITCBC 782   2 self->sink_ = nullptr; 782   2 self->sink_ = nullptr;
783   } 783   }
HITCBC 784   20 } 784   20 }
HITCBC 785   20 } g{this}; 785   20 } g{this};
786   786  
HITCBC 787   20 storage_ = ::operator new(sizeof(S)); 787   20 storage_ = ::operator new(sizeof(S));
HITCBC 788   20 sink_ = ::new(storage_) S(std::move(s)); 788   20 sink_ = ::new(storage_) S(std::move(s));
789   789  
HITCBC 790   19 cached_awaitable_ = ::operator new(vt_->awaitable_size); 790   19 cached_awaitable_ = ::operator new(vt_->awaitable_size);
791   791  
HITCBC 792   18 g.committed = true; 792   18 g.committed = true;
HITCBC 793   20 } 793   20 }
794   794  
795   template<BufferSink S> 795   template<BufferSink S>
HITCBC 796   195 any_buffer_sink::any_buffer_sink(S* s) 796   195 any_buffer_sink::any_buffer_sink(S* s)
HITCBC 797   195 : sink_(s) 797   195 : sink_(s)
HITCBC 798   195 , vt_(&vtable_for_impl<S>::value) 798   195 , vt_(&vtable_for_impl<S>::value)
799   { 799   {
HITCBC 800   195 cached_awaitable_ = ::operator new(vt_->awaitable_size); 800   195 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 801   195 } 801   195 }
802   802  
803   inline std::span<mutable_buffer> 803   inline std::span<mutable_buffer>
HITCBC 804   132 any_buffer_sink::prepare(std::span<mutable_buffer> dest) 804   132 any_buffer_sink::prepare(std::span<mutable_buffer> dest)
805   { 805   {
HITCBC 806   132 return vt_->do_prepare(sink_, dest); 806   132 return vt_->do_prepare(sink_, dest);
807   } 807   }
808   808  
809   inline auto 809   inline auto
HITCBC 810   110 any_buffer_sink::commit(std::size_t n) 810   110 any_buffer_sink::commit(std::size_t n)
811   { 811   {
812   struct awaitable 812   struct awaitable
813   { 813   {
814   any_buffer_sink* self_; 814   any_buffer_sink* self_;
815   std::size_t n_; 815   std::size_t n_;
816   816  
817   bool 817   bool
HITCBC 818   110 await_ready() 818   110 await_ready()
819   { 819   {
HITCBC 820   220 self_->active_ops_ = self_->vt_->construct_commit_awaitable( 820   220 self_->active_ops_ = self_->vt_->construct_commit_awaitable(
HITCBC 821   110 self_->sink_, 821   110 self_->sink_,
HITCBC 822   110 self_->cached_awaitable_, 822   110 self_->cached_awaitable_,
823   n_); 823   n_);
HITCBC 824   110 return self_->active_ops_->await_ready(self_->cached_awaitable_); 824   110 return self_->active_ops_->await_ready(self_->cached_awaitable_);
825   } 825   }
826   826  
827   std::coroutine_handle<> 827   std::coroutine_handle<>
HITCBC 828   1 await_suspend(std::coroutine_handle<> h, io_env const* env) 828   1 await_suspend(std::coroutine_handle<> h, io_env const* env)
829   { 829   {
HITCBC 830   1 return self_->active_ops_->await_suspend( 830   1 return self_->active_ops_->await_suspend(
HITCBC 831   1 self_->cached_awaitable_, h, env); 831   1 self_->cached_awaitable_, h, env);
832   } 832   }
833   833  
834   io_result<> 834   io_result<>
HITCBC 835   110 await_resume() 835   110 await_resume()
836   { 836   {
837   struct guard { 837   struct guard {
838   any_buffer_sink* self; 838   any_buffer_sink* self;
HITCBC 839   110 ~guard() { 839   110 ~guard() {
HITCBC 840   110 self->active_ops_->destroy(self->cached_awaitable_); 840   110 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 841   110 self->active_ops_ = nullptr; 841   110 self->active_ops_ = nullptr;
HITCBC 842   110 } 842   110 }
HITCBC 843   110 } g{self_}; 843   110 } g{self_};
HITCBC 844   110 return self_->active_ops_->await_resume( 844   110 return self_->active_ops_->await_resume(
HITCBC 845   193 self_->cached_awaitable_); 845   193 self_->cached_awaitable_);
HITCBC 846   110 } 846   110 }
847   }; 847   };
HITCBC 848   110 return awaitable{this, n}; 848   110 return awaitable{this, n};
849   } 849   }
850   850  
851   inline auto 851   inline auto
HITCBC 852   55 any_buffer_sink::commit_eof(std::size_t n) 852   55 any_buffer_sink::commit_eof(std::size_t n)
853   { 853   {
854   struct awaitable 854   struct awaitable
855   { 855   {
856   any_buffer_sink* self_; 856   any_buffer_sink* self_;
857   std::size_t n_; 857   std::size_t n_;
858   858  
859   bool 859   bool
HITCBC 860   55 await_ready() 860   55 await_ready()
861   { 861   {
HITCBC 862   110 self_->active_ops_ = self_->vt_->construct_commit_eof_awaitable( 862   110 self_->active_ops_ = self_->vt_->construct_commit_eof_awaitable(
HITCBC 863   55 self_->sink_, 863   55 self_->sink_,
HITCBC 864   55 self_->cached_awaitable_, 864   55 self_->cached_awaitable_,
865   n_); 865   n_);
HITCBC 866   55 return self_->active_ops_->await_ready(self_->cached_awaitable_); 866   55 return self_->active_ops_->await_ready(self_->cached_awaitable_);
867   } 867   }
868   868  
869   std::coroutine_handle<> 869   std::coroutine_handle<>
HITCBC 870   1 await_suspend(std::coroutine_handle<> h, io_env const* env) 870   1 await_suspend(std::coroutine_handle<> h, io_env const* env)
871   { 871   {
HITCBC 872   1 return self_->active_ops_->await_suspend( 872   1 return self_->active_ops_->await_suspend(
HITCBC 873   1 self_->cached_awaitable_, h, env); 873   1 self_->cached_awaitable_, h, env);
874   } 874   }
875   875  
876   io_result<> 876   io_result<>
HITCBC 877   55 await_resume() 877   55 await_resume()
878   { 878   {
879   struct guard { 879   struct guard {
880   any_buffer_sink* self; 880   any_buffer_sink* self;
HITCBC 881   55 ~guard() { 881   55 ~guard() {
HITCBC 882   55 self->active_ops_->destroy(self->cached_awaitable_); 882   55 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 883   55 self->active_ops_ = nullptr; 883   55 self->active_ops_ = nullptr;
HITCBC 884   55 } 884   55 }
HITCBC 885   55 } g{self_}; 885   55 } g{self_};
HITCBC 886   55 return self_->active_ops_->await_resume( 886   55 return self_->active_ops_->await_resume(
HITCBC 887   94 self_->cached_awaitable_); 887   94 self_->cached_awaitable_);
HITCBC 888   55 } 888   55 }
889   }; 889   };
HITCBC 890   55 return awaitable{this, n}; 890   55 return awaitable{this, n};
891   } 891   }
892   892  
893   inline auto 893   inline auto
HITCBC 894   7 any_buffer_sink::write_some_( 894   7 any_buffer_sink::write_some_(
895   std::span<const_buffer const> buffers) 895   std::span<const_buffer const> buffers)
896   { 896   {
897   struct awaitable 897   struct awaitable
898   { 898   {
899   any_buffer_sink* self_; 899   any_buffer_sink* self_;
900   std::span<const_buffer const> buffers_; 900   std::span<const_buffer const> buffers_;
901   901  
902   bool 902   bool
HITCBC 903   7 await_ready() const noexcept 903   7 await_ready() const noexcept
904   { 904   {
HITCBC 905   7 return false; 905   7 return false;
906   } 906   }
907   907  
908   std::coroutine_handle<> 908   std::coroutine_handle<>
HITCBC 909   7 await_suspend(std::coroutine_handle<> h, io_env const* env) 909   7 await_suspend(std::coroutine_handle<> h, io_env const* env)
910   { 910   {
HITCBC 911   14 self_->active_write_ops_ = 911   14 self_->active_write_ops_ =
HITCBC 912   14 self_->vt_->construct_write_some_awaitable( 912   14 self_->vt_->construct_write_some_awaitable(
HITCBC 913   7 self_->sink_, 913   7 self_->sink_,
HITCBC 914   7 self_->cached_awaitable_, 914   7 self_->cached_awaitable_,
915   buffers_); 915   buffers_);
916   916  
HITCBC 917   7 if(self_->active_write_ops_->await_ready( 917   7 if(self_->active_write_ops_->await_ready(
HITCBC 918   7 self_->cached_awaitable_)) 918   7 self_->cached_awaitable_))
HITCBC 919   6 return h; 919   6 return h;
920   920  
HITCBC 921   1 return self_->active_write_ops_->await_suspend( 921   1 return self_->active_write_ops_->await_suspend(
HITCBC 922   1 self_->cached_awaitable_, h, env); 922   1 self_->cached_awaitable_, h, env);
923   } 923   }
924   924  
925   io_result<std::size_t> 925   io_result<std::size_t>
HITCBC 926   7 await_resume() 926   7 await_resume()
927   { 927   {
928   struct guard { 928   struct guard {
929   any_buffer_sink* self; 929   any_buffer_sink* self;
HITCBC 930   7 ~guard() { 930   7 ~guard() {
HITCBC 931   7 self->active_write_ops_->destroy( 931   7 self->active_write_ops_->destroy(
HITCBC 932   7 self->cached_awaitable_); 932   7 self->cached_awaitable_);
HITCBC 933   7 self->active_write_ops_ = nullptr; 933   7 self->active_write_ops_ = nullptr;
HITCBC 934   7 } 934   7 }
HITCBC 935   7 } g{self_}; 935   7 } g{self_};
HITCBC 936   7 return self_->active_write_ops_->await_resume( 936   7 return self_->active_write_ops_->await_resume(
HITCBC 937   12 self_->cached_awaitable_); 937   12 self_->cached_awaitable_);
HITCBC 938   7 } 938   7 }
939   }; 939   };
HITCBC 940   7 return awaitable{this, buffers}; 940   7 return awaitable{this, buffers};
941   } 941   }
942   942  
943   inline auto 943   inline auto
HITCBC 944   15 any_buffer_sink::write_( 944   15 any_buffer_sink::write_(
945   std::span<const_buffer const> buffers) 945   std::span<const_buffer const> buffers)
946   { 946   {
947   struct awaitable 947   struct awaitable
948   { 948   {
949   any_buffer_sink* self_; 949   any_buffer_sink* self_;
950   std::span<const_buffer const> buffers_; 950   std::span<const_buffer const> buffers_;
951   951  
952   bool 952   bool
HITCBC 953   15 await_ready() const noexcept 953   15 await_ready() const noexcept
954   { 954   {
HITCBC 955   15 return false; 955   15 return false;
956   } 956   }
957   957  
958   std::coroutine_handle<> 958   std::coroutine_handle<>
HITCBC 959   15 await_suspend(std::coroutine_handle<> h, io_env const* env) 959   15 await_suspend(std::coroutine_handle<> h, io_env const* env)
960   { 960   {
HITCBC 961   30 self_->active_write_ops_ = 961   30 self_->active_write_ops_ =
HITCBC 962   30 self_->vt_->construct_write_awaitable( 962   30 self_->vt_->construct_write_awaitable(
HITCBC 963   15 self_->sink_, 963   15 self_->sink_,
HITCBC 964   15 self_->cached_awaitable_, 964   15 self_->cached_awaitable_,
965   buffers_); 965   buffers_);
966   966  
HITCBC 967   15 if(self_->active_write_ops_->await_ready( 967   15 if(self_->active_write_ops_->await_ready(
HITCBC 968   15 self_->cached_awaitable_)) 968   15 self_->cached_awaitable_))
HITCBC 969   14 return h; 969   14 return h;
970   970  
HITCBC 971   1 return self_->active_write_ops_->await_suspend( 971   1 return self_->active_write_ops_->await_suspend(
HITCBC 972   1 self_->cached_awaitable_, h, env); 972   1 self_->cached_awaitable_, h, env);
973   } 973   }
974   974  
975   io_result<std::size_t> 975   io_result<std::size_t>
HITCBC 976   15 await_resume() 976   15 await_resume()
977   { 977   {
978   struct guard { 978   struct guard {
979   any_buffer_sink* self; 979   any_buffer_sink* self;
HITCBC 980   15 ~guard() { 980   15 ~guard() {
HITCBC 981   15 self->active_write_ops_->destroy( 981   15 self->active_write_ops_->destroy(
HITCBC 982   15 self->cached_awaitable_); 982   15 self->cached_awaitable_);
HITCBC 983   15 self->active_write_ops_ = nullptr; 983   15 self->active_write_ops_ = nullptr;
HITCBC 984   15 } 984   15 }
HITCBC 985   15 } g{self_}; 985   15 } g{self_};
HITCBC 986   15 return self_->active_write_ops_->await_resume( 986   15 return self_->active_write_ops_->await_resume(
HITCBC 987   26 self_->cached_awaitable_); 987   26 self_->cached_awaitable_);
HITCBC 988   15 } 988   15 }
989   }; 989   };
HITCBC 990   15 return awaitable{this, buffers}; 990   15 return awaitable{this, buffers};
991   } 991   }
992   992  
993   inline auto 993   inline auto
HITCBC 994   13 any_buffer_sink::write_eof_buffers_( 994   13 any_buffer_sink::write_eof_buffers_(
995   std::span<const_buffer const> buffers) 995   std::span<const_buffer const> buffers)
996   { 996   {
997   struct awaitable 997   struct awaitable
998   { 998   {
999   any_buffer_sink* self_; 999   any_buffer_sink* self_;
1000   std::span<const_buffer const> buffers_; 1000   std::span<const_buffer const> buffers_;
1001   1001  
1002   bool 1002   bool
HITCBC 1003   13 await_ready() const noexcept 1003   13 await_ready() const noexcept
1004   { 1004   {
HITCBC 1005   13 return false; 1005   13 return false;
1006   } 1006   }
1007   1007  
1008   std::coroutine_handle<> 1008   std::coroutine_handle<>
HITCBC 1009   13 await_suspend(std::coroutine_handle<> h, io_env const* env) 1009   13 await_suspend(std::coroutine_handle<> h, io_env const* env)
1010   { 1010   {
HITCBC 1011   26 self_->active_write_ops_ = 1011   26 self_->active_write_ops_ =
HITCBC 1012   26 self_->vt_->construct_write_eof_buffers_awaitable( 1012   26 self_->vt_->construct_write_eof_buffers_awaitable(
HITCBC 1013   13 self_->sink_, 1013   13 self_->sink_,
HITCBC 1014   13 self_->cached_awaitable_, 1014   13 self_->cached_awaitable_,
1015   buffers_); 1015   buffers_);
1016   1016  
HITCBC 1017   13 if(self_->active_write_ops_->await_ready( 1017   13 if(self_->active_write_ops_->await_ready(
HITCBC 1018   13 self_->cached_awaitable_)) 1018   13 self_->cached_awaitable_))
HITCBC 1019   12 return h; 1019   12 return h;
1020   1020  
HITCBC 1021   1 return self_->active_write_ops_->await_suspend( 1021   1 return self_->active_write_ops_->await_suspend(
HITCBC 1022   1 self_->cached_awaitable_, h, env); 1022   1 self_->cached_awaitable_, h, env);
1023   } 1023   }
1024   1024  
1025   io_result<std::size_t> 1025   io_result<std::size_t>
HITCBC 1026   13 await_resume() 1026   13 await_resume()
1027   { 1027   {
1028   struct guard { 1028   struct guard {
1029   any_buffer_sink* self; 1029   any_buffer_sink* self;
HITCBC 1030   13 ~guard() { 1030   13 ~guard() {
HITCBC 1031   13 self->active_write_ops_->destroy( 1031   13 self->active_write_ops_->destroy(
HITCBC 1032   13 self->cached_awaitable_); 1032   13 self->cached_awaitable_);
HITCBC 1033   13 self->active_write_ops_ = nullptr; 1033   13 self->active_write_ops_ = nullptr;
HITCBC 1034   13 } 1034   13 }
HITCBC 1035   13 } g{self_}; 1035   13 } g{self_};
HITCBC 1036   13 return self_->active_write_ops_->await_resume( 1036   13 return self_->active_write_ops_->await_resume(
HITCBC 1037   22 self_->cached_awaitable_); 1037   22 self_->cached_awaitable_);
HITCBC 1038   13 } 1038   13 }
1039   }; 1039   };
HITCBC 1040   13 return awaitable{this, buffers}; 1040   13 return awaitable{this, buffers};
1041   } 1041   }
1042   1042  
1043   template<ConstBufferSequence CB> 1043   template<ConstBufferSequence CB>
1044   io_task<std::size_t> 1044   io_task<std::size_t>
HITCBC 1045   23 any_buffer_sink::write_some(CB buffers) 1045   23 any_buffer_sink::write_some(CB buffers)
1046   { 1046   {
1047   buffer_param<CB> bp(buffers); 1047   buffer_param<CB> bp(buffers);
1048   auto src = bp.data(); 1048   auto src = bp.data();
1049   if(src.empty()) 1049   if(src.empty())
1050   co_return {{}, 0}; 1050   co_return {{}, 0};
1051   1051  
1052   // Native WriteSink path 1052   // Native WriteSink path
1053   if(vt_->construct_write_some_awaitable) 1053   if(vt_->construct_write_some_awaitable)
1054   co_return co_await write_some_(src); 1054   co_return co_await write_some_(src);
1055   1055  
1056   // Synthesized path: prepare + buffer_copy + commit 1056   // Synthesized path: prepare + buffer_copy + commit
1057   mutable_buffer arr[detail::max_iovec_]; 1057   mutable_buffer arr[detail::max_iovec_];
1058   auto dst_bufs = prepare(arr); 1058   auto dst_bufs = prepare(arr);
1059   if(dst_bufs.empty()) 1059   if(dst_bufs.empty())
1060   { 1060   {
1061   auto [ec] = co_await commit(0); 1061   auto [ec] = co_await commit(0);
1062   if(ec) 1062   if(ec)
1063   co_return {ec, 0}; 1063   co_return {ec, 0};
1064   dst_bufs = prepare(arr); 1064   dst_bufs = prepare(arr);
1065   if(dst_bufs.empty()) 1065   if(dst_bufs.empty())
1066   co_return {{}, 0}; 1066   co_return {{}, 0};
1067   } 1067   }
1068   1068  
1069   auto n = buffer_copy(dst_bufs, src); 1069   auto n = buffer_copy(dst_bufs, src);
1070   auto [ec] = co_await commit(n); 1070   auto [ec] = co_await commit(n);
1071   if(ec) 1071   if(ec)
1072   co_return {ec, 0}; 1072   co_return {ec, 0};
1073   co_return {{}, n}; 1073   co_return {{}, n};
HITCBC 1074   46 } 1074   46 }
1075   1075  
1076   template<ConstBufferSequence CB> 1076   template<ConstBufferSequence CB>
1077   io_task<std::size_t> 1077   io_task<std::size_t>
HITCBC 1078   39 any_buffer_sink::write(CB buffers) 1078   39 any_buffer_sink::write(CB buffers)
1079   { 1079   {
1080   buffer_param<CB> bp(buffers); 1080   buffer_param<CB> bp(buffers);
1081   std::size_t total = 0; 1081   std::size_t total = 0;
1082   1082  
1083   // Native WriteSink path 1083   // Native WriteSink path
1084   if(vt_->construct_write_awaitable) 1084   if(vt_->construct_write_awaitable)
1085   { 1085   {
1086   for(;;) 1086   for(;;)
1087   { 1087   {
1088   auto bufs = bp.data(); 1088   auto bufs = bp.data();
1089   if(bufs.empty()) 1089   if(bufs.empty())
1090   break; 1090   break;
1091   1091  
1092   auto [ec, n] = co_await write_(bufs); 1092   auto [ec, n] = co_await write_(bufs);
1093   total += n; 1093   total += n;
1094   if(ec) 1094   if(ec)
1095   co_return {ec, total}; 1095   co_return {ec, total};
1096   bp.consume(n); 1096   bp.consume(n);
1097   } 1097   }
1098   co_return {{}, total}; 1098   co_return {{}, total};
1099   } 1099   }
1100   1100  
1101   // Synthesized path: prepare + buffer_copy + commit 1101   // Synthesized path: prepare + buffer_copy + commit
1102   for(;;) 1102   for(;;)
1103   { 1103   {
1104   auto src = bp.data(); 1104   auto src = bp.data();
1105   if(src.empty()) 1105   if(src.empty())
1106   break; 1106   break;
1107   1107  
1108   mutable_buffer arr[detail::max_iovec_]; 1108   mutable_buffer arr[detail::max_iovec_];
1109   auto dst_bufs = prepare(arr); 1109   auto dst_bufs = prepare(arr);
1110   if(dst_bufs.empty()) 1110   if(dst_bufs.empty())
1111   { 1111   {
1112   auto [ec] = co_await commit(0); 1112   auto [ec] = co_await commit(0);
1113   if(ec) 1113   if(ec)
1114   co_return {ec, total}; 1114   co_return {ec, total};
1115   continue; 1115   continue;
1116   } 1116   }
1117   1117  
1118   auto n = buffer_copy(dst_bufs, src); 1118   auto n = buffer_copy(dst_bufs, src);
1119   auto [ec] = co_await commit(n); 1119   auto [ec] = co_await commit(n);
1120   if(ec) 1120   if(ec)
1121   co_return {ec, total}; 1121   co_return {ec, total};
1122   bp.consume(n); 1122   bp.consume(n);
1123   total += n; 1123   total += n;
1124   } 1124   }
1125   1125  
1126   co_return {{}, total}; 1126   co_return {{}, total};
HITCBC 1127   78 } 1127   78 }
1128   1128  
1129   inline auto 1129   inline auto
HITCBC 1130   33 any_buffer_sink::write_eof() 1130   33 any_buffer_sink::write_eof()
1131   { 1131   {
1132   struct awaitable 1132   struct awaitable
1133   { 1133   {
1134   any_buffer_sink* self_; 1134   any_buffer_sink* self_;
1135   1135  
1136   bool 1136   bool
HITCBC 1137   33 await_ready() 1137   33 await_ready()
1138   { 1138   {
HITCBC 1139   33 if(self_->vt_->construct_write_eof_awaitable) 1139   33 if(self_->vt_->construct_write_eof_awaitable)
1140   { 1140   {
1141   // Native WriteSink: forward to underlying write_eof() 1141   // Native WriteSink: forward to underlying write_eof()
HITCBC 1142   34 self_->active_ops_ = 1142   34 self_->active_ops_ =
HITCBC 1143   17 self_->vt_->construct_write_eof_awaitable( 1143   17 self_->vt_->construct_write_eof_awaitable(
HITCBC 1144   17 self_->sink_, 1144   17 self_->sink_,
HITCBC 1145   17 self_->cached_awaitable_); 1145   17 self_->cached_awaitable_);
1146   } 1146   }
1147   else 1147   else
1148   { 1148   {
1149   // Synthesized: commit_eof(0) 1149   // Synthesized: commit_eof(0)
HITCBC 1150   32 self_->active_ops_ = 1150   32 self_->active_ops_ =
HITCBC 1151   16 self_->vt_->construct_commit_eof_awaitable( 1151   16 self_->vt_->construct_commit_eof_awaitable(
HITCBC 1152   16 self_->sink_, 1152   16 self_->sink_,
HITCBC 1153   16 self_->cached_awaitable_, 1153   16 self_->cached_awaitable_,
1154   0); 1154   0);
1155   } 1155   }
HITCBC 1156   66 return self_->active_ops_->await_ready( 1156   66 return self_->active_ops_->await_ready(
HITCBC 1157   33 self_->cached_awaitable_); 1157   33 self_->cached_awaitable_);
1158   } 1158   }
1159   1159  
1160   std::coroutine_handle<> 1160   std::coroutine_handle<>
HITCBC 1161   1 await_suspend(std::coroutine_handle<> h, io_env const* env) 1161   1 await_suspend(std::coroutine_handle<> h, io_env const* env)
1162   { 1162   {
HITCBC 1163   1 return self_->active_ops_->await_suspend( 1163   1 return self_->active_ops_->await_suspend(
HITCBC 1164   1 self_->cached_awaitable_, h, env); 1164   1 self_->cached_awaitable_, h, env);
1165   } 1165   }
1166   1166  
1167   io_result<> 1167   io_result<>
HITCBC 1168   33 await_resume() 1168   33 await_resume()
1169   { 1169   {
1170   struct guard { 1170   struct guard {
1171   any_buffer_sink* self; 1171   any_buffer_sink* self;
HITCBC 1172   33 ~guard() { 1172   33 ~guard() {
HITCBC 1173   33 self->active_ops_->destroy(self->cached_awaitable_); 1173   33 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 1174   33 self->active_ops_ = nullptr; 1174   33 self->active_ops_ = nullptr;
HITCBC 1175   33 } 1175   33 }
HITCBC 1176   33 } g{self_}; 1176   33 } g{self_};
HITCBC 1177   33 return self_->active_ops_->await_resume( 1177   33 return self_->active_ops_->await_resume(
HITCBC 1178   56 self_->cached_awaitable_); 1178   56 self_->cached_awaitable_);
HITCBC 1179   33 } 1179   33 }
1180   }; 1180   };
HITCBC 1181   33 return awaitable{this}; 1181   33 return awaitable{this};
1182   } 1182   }
1183   1183  
1184   template<ConstBufferSequence CB> 1184   template<ConstBufferSequence CB>
1185   io_task<std::size_t> 1185   io_task<std::size_t>
HITCBC 1186   41 any_buffer_sink::write_eof(CB buffers) 1186   41 any_buffer_sink::write_eof(CB buffers)
1187   { 1187   {
1188   // Native WriteSink path 1188   // Native WriteSink path
1189   if(vt_->construct_write_eof_buffers_awaitable) 1189   if(vt_->construct_write_eof_buffers_awaitable)
1190   { 1190   {
1191   const_buffer_param<CB> bp(buffers); 1191   const_buffer_param<CB> bp(buffers);
1192   std::size_t total = 0; 1192   std::size_t total = 0;
1193   1193  
1194   for(;;) 1194   for(;;)
1195   { 1195   {
1196   auto bufs = bp.data(); 1196   auto bufs = bp.data();
1197   if(bufs.empty()) 1197   if(bufs.empty())
1198   { 1198   {
1199   auto [ec] = co_await write_eof(); 1199   auto [ec] = co_await write_eof();
1200   co_return {ec, total}; 1200   co_return {ec, total};
1201   } 1201   }
1202   1202  
1203   if(!bp.more()) 1203   if(!bp.more())
1204   { 1204   {
1205   // Last window: send atomically with EOF 1205   // Last window: send atomically with EOF
1206   auto [ec, n] = co_await write_eof_buffers_(bufs); 1206   auto [ec, n] = co_await write_eof_buffers_(bufs);
1207   total += n; 1207   total += n;
1208   co_return {ec, total}; 1208   co_return {ec, total};
1209   } 1209   }
1210   1210  
1211   auto [ec, n] = co_await write_(bufs); 1211   auto [ec, n] = co_await write_(bufs);
1212   total += n; 1212   total += n;
1213   if(ec) 1213   if(ec)
1214   co_return {ec, total}; 1214   co_return {ec, total};
1215   bp.consume(n); 1215   bp.consume(n);
1216   } 1216   }
1217   } 1217   }
1218   1218  
1219   // Synthesized path: prepare + buffer_copy + commit + commit_eof 1219   // Synthesized path: prepare + buffer_copy + commit + commit_eof
1220   buffer_param<CB> bp(buffers); 1220   buffer_param<CB> bp(buffers);
1221   std::size_t total = 0; 1221   std::size_t total = 0;
1222   1222  
1223   for(;;) 1223   for(;;)
1224   { 1224   {
1225   auto src = bp.data(); 1225   auto src = bp.data();
1226   if(src.empty()) 1226   if(src.empty())
1227   break; 1227   break;
1228   1228  
1229   mutable_buffer arr[detail::max_iovec_]; 1229   mutable_buffer arr[detail::max_iovec_];
1230   auto dst_bufs = prepare(arr); 1230   auto dst_bufs = prepare(arr);
1231   if(dst_bufs.empty()) 1231   if(dst_bufs.empty())
1232   { 1232   {
1233   auto [ec] = co_await commit(0); 1233   auto [ec] = co_await commit(0);
1234   if(ec) 1234   if(ec)
1235   co_return {ec, total}; 1235   co_return {ec, total};
1236   continue; 1236   continue;
1237   } 1237   }
1238   1238  
1239   auto n = buffer_copy(dst_bufs, src); 1239   auto n = buffer_copy(dst_bufs, src);
1240   auto [ec] = co_await commit(n); 1240   auto [ec] = co_await commit(n);
1241   if(ec) 1241   if(ec)
1242   co_return {ec, total}; 1242   co_return {ec, total};
1243   bp.consume(n); 1243   bp.consume(n);
1244   total += n; 1244   total += n;
1245   } 1245   }
1246   1246  
1247   auto [ec] = co_await commit_eof(0); 1247   auto [ec] = co_await commit_eof(0);
1248   if(ec) 1248   if(ec)
1249   co_return {ec, total}; 1249   co_return {ec, total};
1250   1250  
1251   co_return {{}, total}; 1251   co_return {{}, total};
HITCBC 1252   82 } 1252   82 }
1253   1253  
1254   static_assert(BufferSink<any_buffer_sink>); 1254   static_assert(BufferSink<any_buffer_sink>);
1255   static_assert(WriteSink<any_buffer_sink>); 1255   static_assert(WriteSink<any_buffer_sink>);
1256   1256  
1257   } // namespace capy 1257   } // namespace capy
1258   } // namespace boost 1258   } // namespace boost
1259   1259  
1260   #endif 1260   #endif