86.83% Lines (244/281) 87.84% Functions (65/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 - /** Default constructor. 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 - /** Move constructor. 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 - /** Move assignment operator. 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 yielding `(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 yielding `(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 - Writes one or more bytes from the buffer sequence to the 279 + Attempt to write up to `buffer_size( buffers )` bytes from
280 - underlying sink. May consume less than the full sequence. 280 + the buffer sequence to the underlying sink. May consume less
  281 + than the full sequence.
281   282  
282   When the wrapped type provides native @ref WriteSink support, 283   When the wrapped type provides native @ref WriteSink support,
283   the operation forwards directly. Otherwise it is synthesized 284   the operation forwards directly. Otherwise it is synthesized
284   from @ref prepare and @ref commit with a buffer copy. 285   from @ref prepare and @ref commit with a buffer copy.
285   286  
286   @param buffers The buffer sequence to write. 287   @param buffers The buffer sequence to write.
287   288  
288 - @return An awaitable yielding `(error_code,std::size_t)`. 289 + @return An awaitable that await-returns `(error_code,std::size_t)`.
289   290  
290   @par Preconditions 291   @par Preconditions
291   The wrapper must contain a valid sink (`has_value() == true`). 292   The wrapper must contain a valid sink (`has_value() == true`).
292   */ 293   */
293   template<ConstBufferSequence CB> 294   template<ConstBufferSequence CB>
294   io_task<std::size_t> 295   io_task<std::size_t>
295   write_some(CB buffers); 296   write_some(CB buffers);
296   297  
297   /** Write all data from a buffer sequence. 298   /** Write all data from a buffer sequence.
298   299  
299   Writes all data from the buffer sequence to the underlying 300   Writes all data from the buffer sequence to the underlying
300   sink. This method satisfies the @ref WriteSink concept. 301   sink. This method satisfies the @ref WriteSink concept.
301   302  
302   When the wrapped type provides native @ref WriteSink support, 303   When the wrapped type provides native @ref WriteSink support,
303   each window is forwarded directly. Otherwise the data is 304   each window is forwarded directly. Otherwise the data is
304   copied into the sink via @ref prepare and @ref commit. 305   copied into the sink via @ref prepare and @ref commit.
305   306  
306   @param buffers The buffer sequence to write. 307   @param buffers The buffer sequence to write.
307   308  
308 - @return An awaitable yielding `(error_code,std::size_t)`. 309 + @return An awaitable that await-returns `(error_code,std::size_t)`.
309   310  
310   @par Preconditions 311   @par Preconditions
311   The wrapper must contain a valid sink (`has_value() == true`). 312   The wrapper must contain a valid sink (`has_value() == true`).
312   */ 313   */
313   template<ConstBufferSequence CB> 314   template<ConstBufferSequence CB>
314   io_task<std::size_t> 315   io_task<std::size_t>
315   write(CB buffers); 316   write(CB buffers);
316   317  
317   /** Atomically write data and signal end-of-stream. 318   /** Atomically write data and signal end-of-stream.
318   319  
319   Writes all data from the buffer sequence to the underlying 320   Writes all data from the buffer sequence to the underlying
320   sink and then signals end-of-stream. 321   sink and then signals end-of-stream.
321   322  
322   When the wrapped type provides native @ref WriteSink support, 323   When the wrapped type provides native @ref WriteSink support,
323   the final window is sent atomically via the underlying 324   the final window is sent atomically via the underlying
324   `write_eof(buffers)`. Otherwise the data is synthesized 325   `write_eof(buffers)`. Otherwise the data is synthesized
325   through @ref prepare, @ref commit, and @ref commit_eof. 326   through @ref prepare, @ref commit, and @ref commit_eof.
326   327  
327   @param buffers The buffer sequence to write. 328   @param buffers The buffer sequence to write.
328   329  
329 - @return An awaitable yielding `(error_code,std::size_t)`. 330 + @return An awaitable that await-returns `(error_code,std::size_t)`.
330   331  
331   @par Preconditions 332   @par Preconditions
332   The wrapper must contain a valid sink (`has_value() == true`). 333   The wrapper must contain a valid sink (`has_value() == true`).
333   */ 334   */
334   template<ConstBufferSequence CB> 335   template<ConstBufferSequence CB>
335   io_task<std::size_t> 336   io_task<std::size_t>
336   write_eof(CB buffers); 337   write_eof(CB buffers);
337   338  
338   /** Signal end-of-stream. 339   /** Signal end-of-stream.
339   340  
340   Indicates that no more data will be written to the sink. 341   Indicates that no more data will be written to the sink.
341   This method satisfies the @ref WriteSink concept. 342   This method satisfies the @ref WriteSink concept.
342   343  
343   When the wrapped type provides native @ref WriteSink support, 344   When the wrapped type provides native @ref WriteSink support,
344   the underlying `write_eof()` is called. Otherwise the 345   the underlying `write_eof()` is called. Otherwise the
345   operation is implemented as `commit_eof(0)`. 346   operation is implemented as `commit_eof(0)`.
346   347  
347 - @return An awaitable yielding `(error_code)`. 348 + @return An awaitable that await-returns `(error_code)`.
348   349  
349   @par Preconditions 350   @par Preconditions
350   The wrapper must contain a valid sink (`has_value() == true`). 351   The wrapper must contain a valid sink (`has_value() == true`).
351   */ 352   */
352   auto 353   auto
353   write_eof(); 354   write_eof();
354   355  
355   protected: 356   protected:
356   /** Rebind to a new sink after move. 357   /** Rebind to a new sink after move.
357   358  
358   Updates the internal pointer to reference a new sink object. 359   Updates the internal pointer to reference a new sink object.
359   Used by owning wrappers after move assignment when the owned 360   Used by owning wrappers after move assignment when the owned
360   object has moved to a new location. 361   object has moved to a new location.
361   362  
362   @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
363   type as the original sink. 364   type as the original sink.
364   365  
365   @note Terminates if called with a sink of different type 366   @note Terminates if called with a sink of different type
366   than the original. 367   than the original.
367   */ 368   */
368   template<BufferSink S> 369   template<BufferSink S>
369   void 370   void
370   rebind(S& new_sink) noexcept 371   rebind(S& new_sink) noexcept
371   { 372   {
372   if(vt_ != &vtable_for_impl<S>::value) 373   if(vt_ != &vtable_for_impl<S>::value)
373   std::terminate(); 374   std::terminate();
374   sink_ = &new_sink; 375   sink_ = &new_sink;
375   } 376   }
376   377  
377   private: 378   private:
378   /** Forward a partial write through the vtable. 379   /** Forward a partial write through the vtable.
379   380  
380   Constructs the underlying `write_some` awaitable in 381   Constructs the underlying `write_some` awaitable in
381   cached storage and returns a type-erased awaitable. 382   cached storage and returns a type-erased awaitable.
382   */ 383   */
383   auto 384   auto
384   write_some_(std::span<const_buffer const> buffers); 385   write_some_(std::span<const_buffer const> buffers);
385   386  
386   /** Forward a complete write through the vtable. 387   /** Forward a complete write through the vtable.
387   388  
388   Constructs the underlying `write` awaitable in 389   Constructs the underlying `write` awaitable in
389   cached storage and returns a type-erased awaitable. 390   cached storage and returns a type-erased awaitable.
390   */ 391   */
391   auto 392   auto
392   write_(std::span<const_buffer const> buffers); 393   write_(std::span<const_buffer const> buffers);
393   394  
394   /** Forward an atomic write-with-EOF through the vtable. 395   /** Forward an atomic write-with-EOF through the vtable.
395   396  
396   Constructs the underlying `write_eof(buffers)` awaitable 397   Constructs the underlying `write_eof(buffers)` awaitable
397   in cached storage and returns a type-erased awaitable. 398   in cached storage and returns a type-erased awaitable.
398   */ 399   */
399   auto 400   auto
400   write_eof_buffers_(std::span<const_buffer const> buffers); 401   write_eof_buffers_(std::span<const_buffer const> buffers);
401   }; 402   };
402   403  
403 - //---------------------------------------------------------- 404 + /** Type-erased ops for awaitables that await-return `io_result<>`. */
404 -  
405 - /** Type-erased ops for awaitables yielding `io_result<>`. */  
406   struct any_buffer_sink::awaitable_ops 405   struct any_buffer_sink::awaitable_ops
407   { 406   {
408   bool (*await_ready)(void*); 407   bool (*await_ready)(void*);
409   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*);
410   io_result<> (*await_resume)(void*); 409   io_result<> (*await_resume)(void*);
411   void (*destroy)(void*) noexcept; 410   void (*destroy)(void*) noexcept;
412   }; 411   };
413   412  
414 - /** Type-erased ops for awaitables yielding `io_result<std::size_t>`. */ 413 + /** Type-erased ops for awaitables that await-return `io_result<std::size_t>`. */
415   struct any_buffer_sink::write_awaitable_ops 414   struct any_buffer_sink::write_awaitable_ops
416   { 415   {
417   bool (*await_ready)(void*); 416   bool (*await_ready)(void*);
418   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*);
419   io_result<std::size_t> (*await_resume)(void*); 418   io_result<std::size_t> (*await_resume)(void*);
420   void (*destroy)(void*) noexcept; 419   void (*destroy)(void*) noexcept;
421   }; 420   };
422   421  
423   struct any_buffer_sink::vtable 422   struct any_buffer_sink::vtable
424   { 423   {
425   void (*destroy)(void*) noexcept; 424   void (*destroy)(void*) noexcept;
426   std::span<mutable_buffer> (*do_prepare)( 425   std::span<mutable_buffer> (*do_prepare)(
427   void* sink, 426   void* sink,
428   std::span<mutable_buffer> dest); 427   std::span<mutable_buffer> dest);
429   std::size_t awaitable_size; 428   std::size_t awaitable_size;
430   std::size_t awaitable_align; 429   std::size_t awaitable_align;
431   awaitable_ops const* (*construct_commit_awaitable)( 430   awaitable_ops const* (*construct_commit_awaitable)(
432   void* sink, 431   void* sink,
433   void* storage, 432   void* storage,
434   std::size_t n); 433   std::size_t n);
435   awaitable_ops const* (*construct_commit_eof_awaitable)( 434   awaitable_ops const* (*construct_commit_eof_awaitable)(
436   void* sink, 435   void* sink,
437   void* storage, 436   void* storage,
438   std::size_t n); 437   std::size_t n);
439   438  
440   // WriteSink forwarding (null when wrapped type is BufferSink-only) 439   // WriteSink forwarding (null when wrapped type is BufferSink-only)
441   write_awaitable_ops const* (*construct_write_some_awaitable)( 440   write_awaitable_ops const* (*construct_write_some_awaitable)(
442   void* sink, 441   void* sink,
443   void* storage, 442   void* storage,
444   std::span<const_buffer const> buffers); 443   std::span<const_buffer const> buffers);
445   write_awaitable_ops const* (*construct_write_awaitable)( 444   write_awaitable_ops const* (*construct_write_awaitable)(
446   void* sink, 445   void* sink,
447   void* storage, 446   void* storage,
448   std::span<const_buffer const> buffers); 447   std::span<const_buffer const> buffers);
449   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)( 448   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)(
450   void* sink, 449   void* sink,
451   void* storage, 450   void* storage,
452   std::span<const_buffer const> buffers); 451   std::span<const_buffer const> buffers);
453   awaitable_ops const* (*construct_write_eof_awaitable)( 452   awaitable_ops const* (*construct_write_eof_awaitable)(
454   void* sink, 453   void* sink,
455   void* storage); 454   void* storage);
456   }; 455   };
457   456  
458   template<BufferSink S> 457   template<BufferSink S>
459   struct any_buffer_sink::vtable_for_impl 458   struct any_buffer_sink::vtable_for_impl
460   { 459   {
461   using CommitAwaitable = decltype(std::declval<S&>().commit( 460   using CommitAwaitable = decltype(std::declval<S&>().commit(
462   std::size_t{})); 461   std::size_t{}));
463   using CommitEofAwaitable = decltype(std::declval<S&>().commit_eof( 462   using CommitEofAwaitable = decltype(std::declval<S&>().commit_eof(
464   std::size_t{})); 463   std::size_t{}));
465   464  
466   static void 465   static void
HITCBC 467   18 do_destroy_impl(void* sink) noexcept 466   18 do_destroy_impl(void* sink) noexcept
468   { 467   {
HITCBC 469   18 static_cast<S*>(sink)->~S(); 468   18 static_cast<S*>(sink)->~S();
HITCBC 470   18 } 469   18 }
471   470  
472   static std::span<mutable_buffer> 471   static std::span<mutable_buffer>
HITCBC 473   126 do_prepare_impl( 472   130 do_prepare_impl(
474   void* sink, 473   void* sink,
475   std::span<mutable_buffer> dest) 474   std::span<mutable_buffer> dest)
476   { 475   {
HITCBC 477   126 auto& s = *static_cast<S*>(sink); 476   130 auto& s = *static_cast<S*>(sink);
HITCBC 478   126 return s.prepare(dest); 477   130 return s.prepare(dest);
479   } 478   }
480   479  
481   static awaitable_ops const* 480   static awaitable_ops const*
HITCBC 482   96 construct_commit_awaitable_impl( 481   109 construct_commit_awaitable_impl(
483   void* sink, 482   void* sink,
484   void* storage, 483   void* storage,
485   std::size_t n) 484   std::size_t n)
486   { 485   {
HITCBC 487   96 auto& s = *static_cast<S*>(sink); 486   109 auto& s = *static_cast<S*>(sink);
HITCBC 488   96 ::new(storage) CommitAwaitable(s.commit(n)); 487   109 ::new(storage) CommitAwaitable(s.commit(n));
489   488  
490   static constexpr awaitable_ops ops = { 489   static constexpr awaitable_ops ops = {
HITCBC 491   96 +[](void* p) { 490   109 +[](void* p) {
HITCBC 492   96 return static_cast<CommitAwaitable*>(p)->await_ready(); 491   109 return static_cast<CommitAwaitable*>(p)->await_ready();
493   }, 492   },
MISUBC 494   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 493   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 495   return detail::call_await_suspend( 494   return detail::call_await_suspend(
MISUBC 496   static_cast<CommitAwaitable*>(p), h, env); 495   static_cast<CommitAwaitable*>(p), h, env);
497   }, 496   },
HITCBC 498   96 +[](void* p) { 497   109 +[](void* p) {
HITCBC 499   96 return static_cast<CommitAwaitable*>(p)->await_resume(); 498   109 return static_cast<CommitAwaitable*>(p)->await_resume();
500   }, 499   },
HITCBC 501   96 +[](void* p) noexcept { 500   109 +[](void* p) noexcept {
HITCBC 502   96 static_cast<CommitAwaitable*>(p)->~CommitAwaitable(); 501   109 static_cast<CommitAwaitable*>(p)->~CommitAwaitable();
503   } 502   }
504   }; 503   };
HITCBC 505   96 return &ops; 504   109 return &ops;
506   } 505   }
507   506  
508   static awaitable_ops const* 507   static awaitable_ops const*
HITCBC 509   70 construct_commit_eof_awaitable_impl( 508   70 construct_commit_eof_awaitable_impl(
510   void* sink, 509   void* sink,
511   void* storage, 510   void* storage,
512   std::size_t n) 511   std::size_t n)
513   { 512   {
HITCBC 514   70 auto& s = *static_cast<S*>(sink); 513   70 auto& s = *static_cast<S*>(sink);
HITCBC 515   70 ::new(storage) CommitEofAwaitable(s.commit_eof(n)); 514   70 ::new(storage) CommitEofAwaitable(s.commit_eof(n));
516   515  
517   static constexpr awaitable_ops ops = { 516   static constexpr awaitable_ops ops = {
HITCBC 518   70 +[](void* p) { 517   70 +[](void* p) {
HITCBC 519   70 return static_cast<CommitEofAwaitable*>(p)->await_ready(); 518   70 return static_cast<CommitEofAwaitable*>(p)->await_ready();
520   }, 519   },
MISUBC 521   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 520   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 522   return detail::call_await_suspend( 521   return detail::call_await_suspend(
MISUBC 523   static_cast<CommitEofAwaitable*>(p), h, env); 522   static_cast<CommitEofAwaitable*>(p), h, env);
524   }, 523   },
HITCBC 525   70 +[](void* p) { 524   70 +[](void* p) {
HITCBC 526   70 return static_cast<CommitEofAwaitable*>(p)->await_resume(); 525   70 return static_cast<CommitEofAwaitable*>(p)->await_resume();
527   }, 526   },
HITCBC 528   70 +[](void* p) noexcept { 527   70 +[](void* p) noexcept {
HITCBC 529   70 static_cast<CommitEofAwaitable*>(p)->~CommitEofAwaitable(); 528   70 static_cast<CommitEofAwaitable*>(p)->~CommitEofAwaitable();
530   } 529   }
531   }; 530   };
HITCBC 532   70 return &ops; 531   70 return &ops;
533   } 532   }
534 - //------------------------------------------------------  
535 - // WriteSink forwarding (only instantiated when WriteSink<S>)  
536 -  
537   533  
538   static write_awaitable_ops const* 534   static write_awaitable_ops const*
HITCBC 539   6 construct_write_some_awaitable_impl( 535   6 construct_write_some_awaitable_impl(
540   void* sink, 536   void* sink,
541   void* storage, 537   void* storage,
542   std::span<const_buffer const> buffers) 538   std::span<const_buffer const> buffers)
543   requires WriteSink<S> 539   requires WriteSink<S>
544   { 540   {
545   using Aw = decltype(std::declval<S&>().write_some( 541   using Aw = decltype(std::declval<S&>().write_some(
546   std::span<const_buffer const>{})); 542   std::span<const_buffer const>{}));
HITCBC 547   6 auto& s = *static_cast<S*>(sink); 543   6 auto& s = *static_cast<S*>(sink);
HITCBC 548   6 ::new(storage) Aw(s.write_some(buffers)); 544   6 ::new(storage) Aw(s.write_some(buffers));
549   545  
550   static constexpr write_awaitable_ops ops = { 546   static constexpr write_awaitable_ops ops = {
HITCBC 551   6 +[](void* p) { 547   6 +[](void* p) {
HITCBC 552   6 return static_cast<Aw*>(p)->await_ready(); 548   6 return static_cast<Aw*>(p)->await_ready();
553   }, 549   },
MISUBC 554   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 550   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 555   return detail::call_await_suspend( 551   return detail::call_await_suspend(
MISUBC 556   static_cast<Aw*>(p), h, env); 552   static_cast<Aw*>(p), h, env);
557   }, 553   },
HITCBC 558   6 +[](void* p) { 554   6 +[](void* p) {
HITCBC 559   6 return static_cast<Aw*>(p)->await_resume(); 555   6 return static_cast<Aw*>(p)->await_resume();
560   }, 556   },
HITCBC 561   6 +[](void* p) noexcept { 557   6 +[](void* p) noexcept {
HITCBC 562   6 static_cast<Aw*>(p)->~Aw(); 558   6 static_cast<Aw*>(p)->~Aw();
563   } 559   }
564   }; 560   };
HITCBC 565   6 return &ops; 561   6 return &ops;
566   } 562   }
567   563  
568   static write_awaitable_ops const* 564   static write_awaitable_ops const*
HITCBC 569   14 construct_write_awaitable_impl( 565   14 construct_write_awaitable_impl(
570   void* sink, 566   void* sink,
571   void* storage, 567   void* storage,
572   std::span<const_buffer const> buffers) 568   std::span<const_buffer const> buffers)
573   requires WriteSink<S> 569   requires WriteSink<S>
574   { 570   {
575   using Aw = decltype(std::declval<S&>().write( 571   using Aw = decltype(std::declval<S&>().write(
576   std::span<const_buffer const>{})); 572   std::span<const_buffer const>{}));
HITCBC 577   14 auto& s = *static_cast<S*>(sink); 573   14 auto& s = *static_cast<S*>(sink);
HITCBC 578   14 ::new(storage) Aw(s.write(buffers)); 574   14 ::new(storage) Aw(s.write(buffers));
579   575  
580   static constexpr write_awaitable_ops ops = { 576   static constexpr write_awaitable_ops ops = {
HITCBC 581   14 +[](void* p) { 577   14 +[](void* p) {
HITCBC 582   14 return static_cast<Aw*>(p)->await_ready(); 578   14 return static_cast<Aw*>(p)->await_ready();
583   }, 579   },
MISUBC 584   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 580   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 585   return detail::call_await_suspend( 581   return detail::call_await_suspend(
MISUBC 586   static_cast<Aw*>(p), h, env); 582   static_cast<Aw*>(p), h, env);
587   }, 583   },
HITCBC 588   14 +[](void* p) { 584   14 +[](void* p) {
HITCBC 589   14 return static_cast<Aw*>(p)->await_resume(); 585   14 return static_cast<Aw*>(p)->await_resume();
590   }, 586   },
HITCBC 591   14 +[](void* p) noexcept { 587   14 +[](void* p) noexcept {
HITCBC 592   14 static_cast<Aw*>(p)->~Aw(); 588   14 static_cast<Aw*>(p)->~Aw();
593   } 589   }
594   }; 590   };
HITCBC 595   14 return &ops; 591   14 return &ops;
596   } 592   }
597   593  
598   static write_awaitable_ops const* 594   static write_awaitable_ops const*
HITCBC 599   12 construct_write_eof_buffers_awaitable_impl( 595   12 construct_write_eof_buffers_awaitable_impl(
600   void* sink, 596   void* sink,
601   void* storage, 597   void* storage,
602   std::span<const_buffer const> buffers) 598   std::span<const_buffer const> buffers)
603   requires WriteSink<S> 599   requires WriteSink<S>
604   { 600   {
605   using Aw = decltype(std::declval<S&>().write_eof( 601   using Aw = decltype(std::declval<S&>().write_eof(
606   std::span<const_buffer const>{})); 602   std::span<const_buffer const>{}));
HITCBC 607   12 auto& s = *static_cast<S*>(sink); 603   12 auto& s = *static_cast<S*>(sink);
HITCBC 608   12 ::new(storage) Aw(s.write_eof(buffers)); 604   12 ::new(storage) Aw(s.write_eof(buffers));
609   605  
610   static constexpr write_awaitable_ops ops = { 606   static constexpr write_awaitable_ops ops = {
HITCBC 611   12 +[](void* p) { 607   12 +[](void* p) {
HITCBC 612   12 return static_cast<Aw*>(p)->await_ready(); 608   12 return static_cast<Aw*>(p)->await_ready();
613   }, 609   },
MISUBC 614   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 610   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 615   return detail::call_await_suspend( 611   return detail::call_await_suspend(
MISUBC 616   static_cast<Aw*>(p), h, env); 612   static_cast<Aw*>(p), h, env);
617   }, 613   },
HITCBC 618   12 +[](void* p) { 614   12 +[](void* p) {
HITCBC 619   12 return static_cast<Aw*>(p)->await_resume(); 615   12 return static_cast<Aw*>(p)->await_resume();
620   }, 616   },
HITCBC 621   12 +[](void* p) noexcept { 617   12 +[](void* p) noexcept {
HITCBC 622   12 static_cast<Aw*>(p)->~Aw(); 618   12 static_cast<Aw*>(p)->~Aw();
623   } 619   }
624   }; 620   };
HITCBC 625   12 return &ops; 621   12 return &ops;
626   } 622   }
627   623  
628   static awaitable_ops const* 624   static awaitable_ops const*
HITCBC 629   16 construct_write_eof_awaitable_impl( 625   16 construct_write_eof_awaitable_impl(
630   void* sink, 626   void* sink,
631   void* storage) 627   void* storage)
632   requires WriteSink<S> 628   requires WriteSink<S>
633   { 629   {
634   using Aw = decltype(std::declval<S&>().write_eof()); 630   using Aw = decltype(std::declval<S&>().write_eof());
HITCBC 635   16 auto& s = *static_cast<S*>(sink); 631   16 auto& s = *static_cast<S*>(sink);
HITCBC 636   16 ::new(storage) Aw(s.write_eof()); 632   16 ::new(storage) Aw(s.write_eof());
637   633  
638   static constexpr awaitable_ops ops = { 634   static constexpr awaitable_ops ops = {
HITCBC 639   16 +[](void* p) { 635   16 +[](void* p) {
HITCBC 640   16 return static_cast<Aw*>(p)->await_ready(); 636   16 return static_cast<Aw*>(p)->await_ready();
641   }, 637   },
MISUBC 642   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 638   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 643   return detail::call_await_suspend( 639   return detail::call_await_suspend(
MISUBC 644   static_cast<Aw*>(p), h, env); 640   static_cast<Aw*>(p), h, env);
645   }, 641   },
HITCBC 646   16 +[](void* p) { 642   16 +[](void* p) {
HITCBC 647   16 return static_cast<Aw*>(p)->await_resume(); 643   16 return static_cast<Aw*>(p)->await_resume();
648   }, 644   },
HITCBC 649   16 +[](void* p) noexcept { 645   16 +[](void* p) noexcept {
HITCBC 650   16 static_cast<Aw*>(p)->~Aw(); 646   16 static_cast<Aw*>(p)->~Aw();
651   } 647   }
652   }; 648   };
HITCBC 653   16 return &ops; 649   16 return &ops;
654   } 650   }
655 - //------------------------------------------------------  
656 -  
657   651  
658   static consteval std::size_t 652   static consteval std::size_t
659   compute_max_size() noexcept 653   compute_max_size() noexcept
660   { 654   {
661   std::size_t s = sizeof(CommitAwaitable) > sizeof(CommitEofAwaitable) 655   std::size_t s = sizeof(CommitAwaitable) > sizeof(CommitEofAwaitable)
662   ? sizeof(CommitAwaitable) 656   ? sizeof(CommitAwaitable)
663   : sizeof(CommitEofAwaitable); 657   : sizeof(CommitEofAwaitable);
664   if constexpr (WriteSink<S>) 658   if constexpr (WriteSink<S>)
665   { 659   {
666   using WS = decltype(std::declval<S&>().write_some( 660   using WS = decltype(std::declval<S&>().write_some(
667   std::span<const_buffer const>{})); 661   std::span<const_buffer const>{}));
668   using W = decltype(std::declval<S&>().write( 662   using W = decltype(std::declval<S&>().write(
669   std::span<const_buffer const>{})); 663   std::span<const_buffer const>{}));
670   using WEB = decltype(std::declval<S&>().write_eof( 664   using WEB = decltype(std::declval<S&>().write_eof(
671   std::span<const_buffer const>{})); 665   std::span<const_buffer const>{}));
672   using WE = decltype(std::declval<S&>().write_eof()); 666   using WE = decltype(std::declval<S&>().write_eof());
673   667  
674   if(sizeof(WS) > s) s = sizeof(WS); 668   if(sizeof(WS) > s) s = sizeof(WS);
675   if(sizeof(W) > s) s = sizeof(W); 669   if(sizeof(W) > s) s = sizeof(W);
676   if(sizeof(WEB) > s) s = sizeof(WEB); 670   if(sizeof(WEB) > s) s = sizeof(WEB);
677   if(sizeof(WE) > s) s = sizeof(WE); 671   if(sizeof(WE) > s) s = sizeof(WE);
678   } 672   }
679   return s; 673   return s;
680   } 674   }
681   675  
682   static consteval std::size_t 676   static consteval std::size_t
683   compute_max_align() noexcept 677   compute_max_align() noexcept
684   { 678   {
685   std::size_t a = alignof(CommitAwaitable) > alignof(CommitEofAwaitable) 679   std::size_t a = alignof(CommitAwaitable) > alignof(CommitEofAwaitable)
686   ? alignof(CommitAwaitable) 680   ? alignof(CommitAwaitable)
687   : alignof(CommitEofAwaitable); 681   : alignof(CommitEofAwaitable);
688   if constexpr (WriteSink<S>) 682   if constexpr (WriteSink<S>)
689   { 683   {
690   using WS = decltype(std::declval<S&>().write_some( 684   using WS = decltype(std::declval<S&>().write_some(
691   std::span<const_buffer const>{})); 685   std::span<const_buffer const>{}));
692   using W = decltype(std::declval<S&>().write( 686   using W = decltype(std::declval<S&>().write(
693   std::span<const_buffer const>{})); 687   std::span<const_buffer const>{}));
694   using WEB = decltype(std::declval<S&>().write_eof( 688   using WEB = decltype(std::declval<S&>().write_eof(
695   std::span<const_buffer const>{})); 689   std::span<const_buffer const>{}));
696   using WE = decltype(std::declval<S&>().write_eof()); 690   using WE = decltype(std::declval<S&>().write_eof());
697   691  
698   if(alignof(WS) > a) a = alignof(WS); 692   if(alignof(WS) > a) a = alignof(WS);
699   if(alignof(W) > a) a = alignof(W); 693   if(alignof(W) > a) a = alignof(W);
700   if(alignof(WEB) > a) a = alignof(WEB); 694   if(alignof(WEB) > a) a = alignof(WEB);
701   if(alignof(WE) > a) a = alignof(WE); 695   if(alignof(WE) > a) a = alignof(WE);
702   } 696   }
703   return a; 697   return a;
704   } 698   }
705   699  
706   static consteval vtable 700   static consteval vtable
707   make_vtable() noexcept 701   make_vtable() noexcept
708   { 702   {
709   vtable v{}; 703   vtable v{};
710   v.destroy = &do_destroy_impl; 704   v.destroy = &do_destroy_impl;
711   v.do_prepare = &do_prepare_impl; 705   v.do_prepare = &do_prepare_impl;
712   v.awaitable_size = compute_max_size(); 706   v.awaitable_size = compute_max_size();
713   v.awaitable_align = compute_max_align(); 707   v.awaitable_align = compute_max_align();
714   v.construct_commit_awaitable = &construct_commit_awaitable_impl; 708   v.construct_commit_awaitable = &construct_commit_awaitable_impl;
715   v.construct_commit_eof_awaitable = &construct_commit_eof_awaitable_impl; 709   v.construct_commit_eof_awaitable = &construct_commit_eof_awaitable_impl;
716   v.construct_write_some_awaitable = nullptr; 710   v.construct_write_some_awaitable = nullptr;
717   v.construct_write_awaitable = nullptr; 711   v.construct_write_awaitable = nullptr;
718   v.construct_write_eof_buffers_awaitable = nullptr; 712   v.construct_write_eof_buffers_awaitable = nullptr;
719   v.construct_write_eof_awaitable = nullptr; 713   v.construct_write_eof_awaitable = nullptr;
720   714  
721   if constexpr (WriteSink<S>) 715   if constexpr (WriteSink<S>)
722   { 716   {
723   v.construct_write_some_awaitable = 717   v.construct_write_some_awaitable =
724   &construct_write_some_awaitable_impl; 718   &construct_write_some_awaitable_impl;
725   v.construct_write_awaitable = 719   v.construct_write_awaitable =
726   &construct_write_awaitable_impl; 720   &construct_write_awaitable_impl;
727   v.construct_write_eof_buffers_awaitable = 721   v.construct_write_eof_buffers_awaitable =
728   &construct_write_eof_buffers_awaitable_impl; 722   &construct_write_eof_buffers_awaitable_impl;
729   v.construct_write_eof_awaitable = 723   v.construct_write_eof_awaitable =
730   &construct_write_eof_awaitable_impl; 724   &construct_write_eof_awaitable_impl;
731   } 725   }
732   return v; 726   return v;
733   } 727   }
734   728  
735   static constexpr vtable value = make_vtable(); 729   static constexpr vtable value = make_vtable();
736   }; 730   };
737 - //----------------------------------------------------------  
738 -  
739   731  
740   inline 732   inline
HITCBC 741   215 any_buffer_sink::~any_buffer_sink() 733   217 any_buffer_sink::~any_buffer_sink()
742   { 734   {
HITCBC 743   215 if(storage_) 735   217 if(storage_)
744   { 736   {
HITCBC 745   17 vt_->destroy(sink_); 737   17 vt_->destroy(sink_);
HITCBC 746   17 ::operator delete(storage_); 738   17 ::operator delete(storage_);
747   } 739   }
HITCBC 748   215 if(cached_awaitable_) 740   217 if(cached_awaitable_)
HITCBC 749   208 ::operator delete(cached_awaitable_); 741   210 ::operator delete(cached_awaitable_);
HITCBC 750   215 } 742   217 }
751   743  
752   inline any_buffer_sink& 744   inline any_buffer_sink&
HITCBC 753   5 any_buffer_sink::operator=(any_buffer_sink&& other) noexcept 745   5 any_buffer_sink::operator=(any_buffer_sink&& other) noexcept
754   { 746   {
HITCBC 755   5 if(this != &other) 747   5 if(this != &other)
756   { 748   {
HITCBC 757   4 if(storage_) 749   4 if(storage_)
758   { 750   {
HITCBC 759   1 vt_->destroy(sink_); 751   1 vt_->destroy(sink_);
HITCBC 760   1 ::operator delete(storage_); 752   1 ::operator delete(storage_);
761   } 753   }
HITCBC 762   4 if(cached_awaitable_) 754   4 if(cached_awaitable_)
HITCBC 763   2 ::operator delete(cached_awaitable_); 755   2 ::operator delete(cached_awaitable_);
HITCBC 764   4 sink_ = std::exchange(other.sink_, nullptr); 756   4 sink_ = std::exchange(other.sink_, nullptr);
HITCBC 765   4 vt_ = std::exchange(other.vt_, nullptr); 757   4 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 766   4 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 758   4 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 767   4 storage_ = std::exchange(other.storage_, nullptr); 759   4 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 768   4 active_ops_ = std::exchange(other.active_ops_, nullptr); 760   4 active_ops_ = std::exchange(other.active_ops_, nullptr);
HITCBC 769   4 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr); 761   4 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr);
770   } 762   }
HITCBC 771   5 return *this; 763   5 return *this;
772   } 764   }
773   765  
774   template<BufferSink S> 766   template<BufferSink S>
775   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>) 767   requires (!std::same_as<std::decay_t<S>, any_buffer_sink>)
HITCBC 776   18 any_buffer_sink::any_buffer_sink(S s) 768   18 any_buffer_sink::any_buffer_sink(S s)
HITCBC 777   18 : vt_(&vtable_for_impl<S>::value) 769   18 : vt_(&vtable_for_impl<S>::value)
778   { 770   {
779   struct guard { 771   struct guard {
780   any_buffer_sink* self; 772   any_buffer_sink* self;
781   bool committed = false; 773   bool committed = false;
HITCBC 782   18 ~guard() { 774   18 ~guard() {
HITCBC 783   18 if(!committed && self->storage_) { 775   18 if(!committed && self->storage_) {
MISUBC 784   self->vt_->destroy(self->sink_); 776   self->vt_->destroy(self->sink_);
MISUBC 785   ::operator delete(self->storage_); 777   ::operator delete(self->storage_);
MISUBC 786   self->storage_ = nullptr; 778   self->storage_ = nullptr;
MISUBC 787   self->sink_ = nullptr; 779   self->sink_ = nullptr;
788   } 780   }
HITCBC 789   18 } 781   18 }
HITCBC 790   18 } g{this}; 782   18 } g{this};
791   783  
HITCBC 792   18 storage_ = ::operator new(sizeof(S)); 784   18 storage_ = ::operator new(sizeof(S));
HITCBC 793   18 sink_ = ::new(storage_) S(std::move(s)); 785   18 sink_ = ::new(storage_) S(std::move(s));
794   786  
HITCBC 795   18 cached_awaitable_ = ::operator new(vt_->awaitable_size); 787   18 cached_awaitable_ = ::operator new(vt_->awaitable_size);
796   788  
HITCBC 797   18 g.committed = true; 789   18 g.committed = true;
HITCBC 798   18 } 790   18 }
799   791  
800   template<BufferSink S> 792   template<BufferSink S>
HITCBC 801   192 any_buffer_sink::any_buffer_sink(S* s) 793   194 any_buffer_sink::any_buffer_sink(S* s)
HITCBC 802   192 : sink_(s) 794   194 : sink_(s)
HITCBC 803   192 , vt_(&vtable_for_impl<S>::value) 795   194 , vt_(&vtable_for_impl<S>::value)
804   { 796   {
HITCBC 805   192 cached_awaitable_ = ::operator new(vt_->awaitable_size); 797   194 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 806   192 } 798   194 }
807 - //----------------------------------------------------------  
808 -  
809   799  
810   inline std::span<mutable_buffer> 800   inline std::span<mutable_buffer>
HITCBC 811   126 any_buffer_sink::prepare(std::span<mutable_buffer> dest) 801   130 any_buffer_sink::prepare(std::span<mutable_buffer> dest)
812   { 802   {
HITCBC 813   126 return vt_->do_prepare(sink_, dest); 803   130 return vt_->do_prepare(sink_, dest);
814   } 804   }
815   805  
816   inline auto 806   inline auto
HITCBC 817   96 any_buffer_sink::commit(std::size_t n) 807   109 any_buffer_sink::commit(std::size_t n)
818   { 808   {
819   struct awaitable 809   struct awaitable
820   { 810   {
821   any_buffer_sink* self_; 811   any_buffer_sink* self_;
822   std::size_t n_; 812   std::size_t n_;
823   813  
824   bool 814   bool
HITCBC 825   96 await_ready() 815   109 await_ready()
826   { 816   {
HITCBC 827   192 self_->active_ops_ = self_->vt_->construct_commit_awaitable( 817   218 self_->active_ops_ = self_->vt_->construct_commit_awaitable(
HITCBC 828   96 self_->sink_, 818   109 self_->sink_,
HITCBC 829   96 self_->cached_awaitable_, 819   109 self_->cached_awaitable_,
830   n_); 820   n_);
HITCBC 831   96 return self_->active_ops_->await_ready(self_->cached_awaitable_); 821   109 return self_->active_ops_->await_ready(self_->cached_awaitable_);
832   } 822   }
833   823  
834   std::coroutine_handle<> 824   std::coroutine_handle<>
MISUBC 835   await_suspend(std::coroutine_handle<> h, io_env const* env) 825   await_suspend(std::coroutine_handle<> h, io_env const* env)
836   { 826   {
MISUBC 837   return self_->active_ops_->await_suspend( 827   return self_->active_ops_->await_suspend(
MISUBC 838   self_->cached_awaitable_, h, env); 828   self_->cached_awaitable_, h, env);
839   } 829   }
840   830  
841   io_result<> 831   io_result<>
HITCBC 842   96 await_resume() 832   109 await_resume()
843   { 833   {
844   struct guard { 834   struct guard {
845   any_buffer_sink* self; 835   any_buffer_sink* self;
HITCBC 846   96 ~guard() { 836   109 ~guard() {
HITCBC 847   96 self->active_ops_->destroy(self->cached_awaitable_); 837   109 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 848   96 self->active_ops_ = nullptr; 838   109 self->active_ops_ = nullptr;
HITCBC 849   96 } 839   109 }
HITCBC 850   96 } g{self_}; 840   109 } g{self_};
HITCBC 851   96 return self_->active_ops_->await_resume( 841   109 return self_->active_ops_->await_resume(
HITCBC 852   166 self_->cached_awaitable_); 842   191 self_->cached_awaitable_);
HITCBC 853   96 } 843   109 }
854   }; 844   };
HITCBC 855   96 return awaitable{this, n}; 845   109 return awaitable{this, n};
856   } 846   }
857   847  
858   inline auto 848   inline auto
HITCBC 859   54 any_buffer_sink::commit_eof(std::size_t n) 849   54 any_buffer_sink::commit_eof(std::size_t n)
860   { 850   {
861   struct awaitable 851   struct awaitable
862   { 852   {
863   any_buffer_sink* self_; 853   any_buffer_sink* self_;
864   std::size_t n_; 854   std::size_t n_;
865   855  
866   bool 856   bool
HITCBC 867   54 await_ready() 857   54 await_ready()
868   { 858   {
HITCBC 869   108 self_->active_ops_ = self_->vt_->construct_commit_eof_awaitable( 859   108 self_->active_ops_ = self_->vt_->construct_commit_eof_awaitable(
HITCBC 870   54 self_->sink_, 860   54 self_->sink_,
HITCBC 871   54 self_->cached_awaitable_, 861   54 self_->cached_awaitable_,
872   n_); 862   n_);
HITCBC 873   54 return self_->active_ops_->await_ready(self_->cached_awaitable_); 863   54 return self_->active_ops_->await_ready(self_->cached_awaitable_);
874   } 864   }
875   865  
876   std::coroutine_handle<> 866   std::coroutine_handle<>
MISUBC 877   await_suspend(std::coroutine_handle<> h, io_env const* env) 867   await_suspend(std::coroutine_handle<> h, io_env const* env)
878   { 868   {
MISUBC 879   return self_->active_ops_->await_suspend( 869   return self_->active_ops_->await_suspend(
MISUBC 880   self_->cached_awaitable_, h, env); 870   self_->cached_awaitable_, h, env);
881   } 871   }
882   872  
883   io_result<> 873   io_result<>
HITCBC 884   54 await_resume() 874   54 await_resume()
885   { 875   {
886   struct guard { 876   struct guard {
887   any_buffer_sink* self; 877   any_buffer_sink* self;
HITCBC 888   54 ~guard() { 878   54 ~guard() {
HITCBC 889   54 self->active_ops_->destroy(self->cached_awaitable_); 879   54 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 890   54 self->active_ops_ = nullptr; 880   54 self->active_ops_ = nullptr;
HITCBC 891   54 } 881   54 }
HITCBC 892   54 } g{self_}; 882   54 } g{self_};
HITCBC 893   54 return self_->active_ops_->await_resume( 883   54 return self_->active_ops_->await_resume(
HITCBC 894   92 self_->cached_awaitable_); 884   92 self_->cached_awaitable_);
HITCBC 895   54 } 885   54 }
896   }; 886   };
HITCBC 897   54 return awaitable{this, n}; 887   54 return awaitable{this, n};
898   } 888   }
899 - //----------------------------------------------------------  
900 - // Private helpers for native WriteSink forwarding  
901 -  
902   889  
903   inline auto 890   inline auto
HITCBC 904   6 any_buffer_sink::write_some_( 891   6 any_buffer_sink::write_some_(
905   std::span<const_buffer const> buffers) 892   std::span<const_buffer const> buffers)
906   { 893   {
907   struct awaitable 894   struct awaitable
908   { 895   {
909   any_buffer_sink* self_; 896   any_buffer_sink* self_;
910   std::span<const_buffer const> buffers_; 897   std::span<const_buffer const> buffers_;
911   898  
912   bool 899   bool
HITCBC 913   6 await_ready() const noexcept 900   6 await_ready() const noexcept
914   { 901   {
HITCBC 915   6 return false; 902   6 return false;
916   } 903   }
917   904  
918   std::coroutine_handle<> 905   std::coroutine_handle<>
HITCBC 919   6 await_suspend(std::coroutine_handle<> h, io_env const* env) 906   6 await_suspend(std::coroutine_handle<> h, io_env const* env)
920   { 907   {
HITCBC 921   12 self_->active_write_ops_ = 908   12 self_->active_write_ops_ =
HITCBC 922   12 self_->vt_->construct_write_some_awaitable( 909   12 self_->vt_->construct_write_some_awaitable(
HITCBC 923   6 self_->sink_, 910   6 self_->sink_,
HITCBC 924   6 self_->cached_awaitable_, 911   6 self_->cached_awaitable_,
925   buffers_); 912   buffers_);
926   913  
HITCBC 927   6 if(self_->active_write_ops_->await_ready( 914   6 if(self_->active_write_ops_->await_ready(
HITCBC 928   6 self_->cached_awaitable_)) 915   6 self_->cached_awaitable_))
HITCBC 929   6 return h; 916   6 return h;
930   917  
MISUBC 931   return self_->active_write_ops_->await_suspend( 918   return self_->active_write_ops_->await_suspend(
MISUBC 932   self_->cached_awaitable_, h, env); 919   self_->cached_awaitable_, h, env);
933   } 920   }
934   921  
935   io_result<std::size_t> 922   io_result<std::size_t>
HITCBC 936   6 await_resume() 923   6 await_resume()
937   { 924   {
938   struct guard { 925   struct guard {
939   any_buffer_sink* self; 926   any_buffer_sink* self;
HITCBC 940   6 ~guard() { 927   6 ~guard() {
HITCBC 941   6 self->active_write_ops_->destroy( 928   6 self->active_write_ops_->destroy(
HITCBC 942   6 self->cached_awaitable_); 929   6 self->cached_awaitable_);
HITCBC 943   6 self->active_write_ops_ = nullptr; 930   6 self->active_write_ops_ = nullptr;
HITCBC 944   6 } 931   6 }
HITCBC 945   6 } g{self_}; 932   6 } g{self_};
HITCBC 946   6 return self_->active_write_ops_->await_resume( 933   6 return self_->active_write_ops_->await_resume(
HITCBC 947   10 self_->cached_awaitable_); 934   10 self_->cached_awaitable_);
HITCBC 948   6 } 935   6 }
949   }; 936   };
HITCBC 950   6 return awaitable{this, buffers}; 937   6 return awaitable{this, buffers};
951   } 938   }
952   939  
953   inline auto 940   inline auto
HITCBC 954   14 any_buffer_sink::write_( 941   14 any_buffer_sink::write_(
955   std::span<const_buffer const> buffers) 942   std::span<const_buffer const> buffers)
956   { 943   {
957   struct awaitable 944   struct awaitable
958   { 945   {
959   any_buffer_sink* self_; 946   any_buffer_sink* self_;
960   std::span<const_buffer const> buffers_; 947   std::span<const_buffer const> buffers_;
961   948  
962   bool 949   bool
HITCBC 963   14 await_ready() const noexcept 950   14 await_ready() const noexcept
964   { 951   {
HITCBC 965   14 return false; 952   14 return false;
966   } 953   }
967   954  
968   std::coroutine_handle<> 955   std::coroutine_handle<>
HITCBC 969   14 await_suspend(std::coroutine_handle<> h, io_env const* env) 956   14 await_suspend(std::coroutine_handle<> h, io_env const* env)
970   { 957   {
HITCBC 971   28 self_->active_write_ops_ = 958   28 self_->active_write_ops_ =
HITCBC 972   28 self_->vt_->construct_write_awaitable( 959   28 self_->vt_->construct_write_awaitable(
HITCBC 973   14 self_->sink_, 960   14 self_->sink_,
HITCBC 974   14 self_->cached_awaitable_, 961   14 self_->cached_awaitable_,
975   buffers_); 962   buffers_);
976   963  
HITCBC 977   14 if(self_->active_write_ops_->await_ready( 964   14 if(self_->active_write_ops_->await_ready(
HITCBC 978   14 self_->cached_awaitable_)) 965   14 self_->cached_awaitable_))
HITCBC 979   14 return h; 966   14 return h;
980   967  
MISUBC 981   return self_->active_write_ops_->await_suspend( 968   return self_->active_write_ops_->await_suspend(
MISUBC 982   self_->cached_awaitable_, h, env); 969   self_->cached_awaitable_, h, env);
983   } 970   }
984   971  
985   io_result<std::size_t> 972   io_result<std::size_t>
HITCBC 986   14 await_resume() 973   14 await_resume()
987   { 974   {
988   struct guard { 975   struct guard {
989   any_buffer_sink* self; 976   any_buffer_sink* self;
HITCBC 990   14 ~guard() { 977   14 ~guard() {
HITCBC 991   14 self->active_write_ops_->destroy( 978   14 self->active_write_ops_->destroy(
HITCBC 992   14 self->cached_awaitable_); 979   14 self->cached_awaitable_);
HITCBC 993   14 self->active_write_ops_ = nullptr; 980   14 self->active_write_ops_ = nullptr;
HITCBC 994   14 } 981   14 }
HITCBC 995   14 } g{self_}; 982   14 } g{self_};
HITCBC 996   14 return self_->active_write_ops_->await_resume( 983   14 return self_->active_write_ops_->await_resume(
HITCBC 997   24 self_->cached_awaitable_); 984   24 self_->cached_awaitable_);
HITCBC 998   14 } 985   14 }
999   }; 986   };
HITCBC 1000   14 return awaitable{this, buffers}; 987   14 return awaitable{this, buffers};
1001   } 988   }
1002   989  
1003   inline auto 990   inline auto
HITCBC 1004   12 any_buffer_sink::write_eof_buffers_( 991   12 any_buffer_sink::write_eof_buffers_(
1005   std::span<const_buffer const> buffers) 992   std::span<const_buffer const> buffers)
1006   { 993   {
1007   struct awaitable 994   struct awaitable
1008   { 995   {
1009   any_buffer_sink* self_; 996   any_buffer_sink* self_;
1010   std::span<const_buffer const> buffers_; 997   std::span<const_buffer const> buffers_;
1011   998  
1012   bool 999   bool
HITCBC 1013   12 await_ready() const noexcept 1000   12 await_ready() const noexcept
1014   { 1001   {
HITCBC 1015   12 return false; 1002   12 return false;
1016   } 1003   }
1017   1004  
1018   std::coroutine_handle<> 1005   std::coroutine_handle<>
HITCBC 1019   12 await_suspend(std::coroutine_handle<> h, io_env const* env) 1006   12 await_suspend(std::coroutine_handle<> h, io_env const* env)
1020   { 1007   {
HITCBC 1021   24 self_->active_write_ops_ = 1008   24 self_->active_write_ops_ =
HITCBC 1022   24 self_->vt_->construct_write_eof_buffers_awaitable( 1009   24 self_->vt_->construct_write_eof_buffers_awaitable(
HITCBC 1023   12 self_->sink_, 1010   12 self_->sink_,
HITCBC 1024   12 self_->cached_awaitable_, 1011   12 self_->cached_awaitable_,
1025   buffers_); 1012   buffers_);
1026   1013  
HITCBC 1027   12 if(self_->active_write_ops_->await_ready( 1014   12 if(self_->active_write_ops_->await_ready(
HITCBC 1028   12 self_->cached_awaitable_)) 1015   12 self_->cached_awaitable_))
HITCBC 1029   12 return h; 1016   12 return h;
1030   1017  
MISUBC 1031   return self_->active_write_ops_->await_suspend( 1018   return self_->active_write_ops_->await_suspend(
MISUBC 1032   self_->cached_awaitable_, h, env); 1019   self_->cached_awaitable_, h, env);
1033   } 1020   }
1034   1021  
1035   io_result<std::size_t> 1022   io_result<std::size_t>
HITCBC 1036   12 await_resume() 1023   12 await_resume()
1037   { 1024   {
1038   struct guard { 1025   struct guard {
1039   any_buffer_sink* self; 1026   any_buffer_sink* self;
HITCBC 1040   12 ~guard() { 1027   12 ~guard() {
HITCBC 1041   12 self->active_write_ops_->destroy( 1028   12 self->active_write_ops_->destroy(
HITCBC 1042   12 self->cached_awaitable_); 1029   12 self->cached_awaitable_);
HITCBC 1043   12 self->active_write_ops_ = nullptr; 1030   12 self->active_write_ops_ = nullptr;
HITCBC 1044   12 } 1031   12 }
HITCBC 1045   12 } g{self_}; 1032   12 } g{self_};
HITCBC 1046   12 return self_->active_write_ops_->await_resume( 1033   12 return self_->active_write_ops_->await_resume(
HITCBC 1047   20 self_->cached_awaitable_); 1034   20 self_->cached_awaitable_);
HITCBC 1048   12 } 1035   12 }
1049   }; 1036   };
HITCBC 1050   12 return awaitable{this, buffers}; 1037   12 return awaitable{this, buffers};
1051   } 1038   }
1052 - //----------------------------------------------------------  
1053 - // Public WriteSink methods  
1054 -  
1055   1039  
1056   template<ConstBufferSequence CB> 1040   template<ConstBufferSequence CB>
1057   io_task<std::size_t> 1041   io_task<std::size_t>
HITCBC 1058   22 any_buffer_sink::write_some(CB buffers) 1042   22 any_buffer_sink::write_some(CB buffers)
1059   { 1043   {
1060   buffer_param<CB> bp(buffers); 1044   buffer_param<CB> bp(buffers);
1061   auto src = bp.data(); 1045   auto src = bp.data();
1062   if(src.empty()) 1046   if(src.empty())
1063   co_return {{}, 0}; 1047   co_return {{}, 0};
1064   1048  
1065   // Native WriteSink path 1049   // Native WriteSink path
1066   if(vt_->construct_write_some_awaitable) 1050   if(vt_->construct_write_some_awaitable)
1067   co_return co_await write_some_(src); 1051   co_return co_await write_some_(src);
1068   1052  
1069   // Synthesized path: prepare + buffer_copy + commit 1053   // Synthesized path: prepare + buffer_copy + commit
1070   mutable_buffer arr[detail::max_iovec_]; 1054   mutable_buffer arr[detail::max_iovec_];
1071   auto dst_bufs = prepare(arr); 1055   auto dst_bufs = prepare(arr);
1072   if(dst_bufs.empty()) 1056   if(dst_bufs.empty())
1073   { 1057   {
1074   auto [ec] = co_await commit(0); 1058   auto [ec] = co_await commit(0);
1075   if(ec) 1059   if(ec)
1076   co_return {ec, 0}; 1060   co_return {ec, 0};
1077   dst_bufs = prepare(arr); 1061   dst_bufs = prepare(arr);
1078   if(dst_bufs.empty()) 1062   if(dst_bufs.empty())
1079   co_return {{}, 0}; 1063   co_return {{}, 0};
1080   } 1064   }
1081   1065  
1082   auto n = buffer_copy(dst_bufs, src); 1066   auto n = buffer_copy(dst_bufs, src);
1083   auto [ec] = co_await commit(n); 1067   auto [ec] = co_await commit(n);
1084   if(ec) 1068   if(ec)
1085   co_return {ec, 0}; 1069   co_return {ec, 0};
1086   co_return {{}, n}; 1070   co_return {{}, n};
HITCBC 1087   44 } 1071   44 }
1088   1072  
1089   template<ConstBufferSequence CB> 1073   template<ConstBufferSequence CB>
1090   io_task<std::size_t> 1074   io_task<std::size_t>
HITCBC 1091   38 any_buffer_sink::write(CB buffers) 1075   38 any_buffer_sink::write(CB buffers)
1092   { 1076   {
1093   buffer_param<CB> bp(buffers); 1077   buffer_param<CB> bp(buffers);
1094   std::size_t total = 0; 1078   std::size_t total = 0;
1095   1079  
1096   // Native WriteSink path 1080   // Native WriteSink path
1097   if(vt_->construct_write_awaitable) 1081   if(vt_->construct_write_awaitable)
1098   { 1082   {
1099   for(;;) 1083   for(;;)
1100   { 1084   {
1101   auto bufs = bp.data(); 1085   auto bufs = bp.data();
1102   if(bufs.empty()) 1086   if(bufs.empty())
1103   break; 1087   break;
1104   1088  
1105   auto [ec, n] = co_await write_(bufs); 1089   auto [ec, n] = co_await write_(bufs);
1106   total += n; 1090   total += n;
1107   if(ec) 1091   if(ec)
1108   co_return {ec, total}; 1092   co_return {ec, total};
1109   bp.consume(n); 1093   bp.consume(n);
1110   } 1094   }
1111   co_return {{}, total}; 1095   co_return {{}, total};
1112   } 1096   }
1113   1097  
1114   // Synthesized path: prepare + buffer_copy + commit 1098   // Synthesized path: prepare + buffer_copy + commit
1115   for(;;) 1099   for(;;)
1116   { 1100   {
1117   auto src = bp.data(); 1101   auto src = bp.data();
1118   if(src.empty()) 1102   if(src.empty())
1119   break; 1103   break;
1120   1104  
1121   mutable_buffer arr[detail::max_iovec_]; 1105   mutable_buffer arr[detail::max_iovec_];
1122   auto dst_bufs = prepare(arr); 1106   auto dst_bufs = prepare(arr);
1123   if(dst_bufs.empty()) 1107   if(dst_bufs.empty())
1124   { 1108   {
1125   auto [ec] = co_await commit(0); 1109   auto [ec] = co_await commit(0);
1126   if(ec) 1110   if(ec)
1127   co_return {ec, total}; 1111   co_return {ec, total};
1128   continue; 1112   continue;
1129   } 1113   }
1130   1114  
1131   auto n = buffer_copy(dst_bufs, src); 1115   auto n = buffer_copy(dst_bufs, src);
1132   auto [ec] = co_await commit(n); 1116   auto [ec] = co_await commit(n);
1133   if(ec) 1117   if(ec)
1134   co_return {ec, total}; 1118   co_return {ec, total};
1135   bp.consume(n); 1119   bp.consume(n);
1136   total += n; 1120   total += n;
1137   } 1121   }
1138   1122  
1139   co_return {{}, total}; 1123   co_return {{}, total};
HITCBC 1140   76 } 1124   76 }
1141   1125  
1142   inline auto 1126   inline auto
HITCBC 1143   32 any_buffer_sink::write_eof() 1127   32 any_buffer_sink::write_eof()
1144   { 1128   {
1145   struct awaitable 1129   struct awaitable
1146   { 1130   {
1147   any_buffer_sink* self_; 1131   any_buffer_sink* self_;
1148   1132  
1149   bool 1133   bool
HITCBC 1150   32 await_ready() 1134   32 await_ready()
1151   { 1135   {
HITCBC 1152   32 if(self_->vt_->construct_write_eof_awaitable) 1136   32 if(self_->vt_->construct_write_eof_awaitable)
1153   { 1137   {
1154   // Native WriteSink: forward to underlying write_eof() 1138   // Native WriteSink: forward to underlying write_eof()
HITCBC 1155   32 self_->active_ops_ = 1139   32 self_->active_ops_ =
HITCBC 1156   16 self_->vt_->construct_write_eof_awaitable( 1140   16 self_->vt_->construct_write_eof_awaitable(
HITCBC 1157   16 self_->sink_, 1141   16 self_->sink_,
HITCBC 1158   16 self_->cached_awaitable_); 1142   16 self_->cached_awaitable_);
1159   } 1143   }
1160   else 1144   else
1161   { 1145   {
1162   // Synthesized: commit_eof(0) 1146   // Synthesized: commit_eof(0)
HITCBC 1163   32 self_->active_ops_ = 1147   32 self_->active_ops_ =
HITCBC 1164   16 self_->vt_->construct_commit_eof_awaitable( 1148   16 self_->vt_->construct_commit_eof_awaitable(
HITCBC 1165   16 self_->sink_, 1149   16 self_->sink_,
HITCBC 1166   16 self_->cached_awaitable_, 1150   16 self_->cached_awaitable_,
1167   0); 1151   0);
1168   } 1152   }
HITCBC 1169   64 return self_->active_ops_->await_ready( 1153   64 return self_->active_ops_->await_ready(
HITCBC 1170   32 self_->cached_awaitable_); 1154   32 self_->cached_awaitable_);
1171   } 1155   }
1172   1156  
1173   std::coroutine_handle<> 1157   std::coroutine_handle<>
MISUBC 1174   await_suspend(std::coroutine_handle<> h, io_env const* env) 1158   await_suspend(std::coroutine_handle<> h, io_env const* env)
1175   { 1159   {
MISUBC 1176   return self_->active_ops_->await_suspend( 1160   return self_->active_ops_->await_suspend(
MISUBC 1177   self_->cached_awaitable_, h, env); 1161   self_->cached_awaitable_, h, env);
1178   } 1162   }
1179   1163  
1180   io_result<> 1164   io_result<>
HITCBC 1181   32 await_resume() 1165   32 await_resume()
1182   { 1166   {
1183   struct guard { 1167   struct guard {
1184   any_buffer_sink* self; 1168   any_buffer_sink* self;
HITCBC 1185   32 ~guard() { 1169   32 ~guard() {
HITCBC 1186   32 self->active_ops_->destroy(self->cached_awaitable_); 1170   32 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 1187   32 self->active_ops_ = nullptr; 1171   32 self->active_ops_ = nullptr;
HITCBC 1188   32 } 1172   32 }
HITCBC 1189   32 } g{self_}; 1173   32 } g{self_};
HITCBC 1190   32 return self_->active_ops_->await_resume( 1174   32 return self_->active_ops_->await_resume(
HITCBC 1191   54 self_->cached_awaitable_); 1175   54 self_->cached_awaitable_);
HITCBC 1192   32 } 1176   32 }
1193   }; 1177   };
HITCBC 1194   32 return awaitable{this}; 1178   32 return awaitable{this};
1195   } 1179   }
1196   1180  
1197   template<ConstBufferSequence CB> 1181   template<ConstBufferSequence CB>
1198   io_task<std::size_t> 1182   io_task<std::size_t>
HITCBC 1199   40 any_buffer_sink::write_eof(CB buffers) 1183   40 any_buffer_sink::write_eof(CB buffers)
1200   { 1184   {
1201   // Native WriteSink path 1185   // Native WriteSink path
1202   if(vt_->construct_write_eof_buffers_awaitable) 1186   if(vt_->construct_write_eof_buffers_awaitable)
1203   { 1187   {
1204   const_buffer_param<CB> bp(buffers); 1188   const_buffer_param<CB> bp(buffers);
1205   std::size_t total = 0; 1189   std::size_t total = 0;
1206   1190  
1207   for(;;) 1191   for(;;)
1208   { 1192   {
1209   auto bufs = bp.data(); 1193   auto bufs = bp.data();
1210   if(bufs.empty()) 1194   if(bufs.empty())
1211   { 1195   {
1212   auto [ec] = co_await write_eof(); 1196   auto [ec] = co_await write_eof();
1213   co_return {ec, total}; 1197   co_return {ec, total};
1214   } 1198   }
1215   1199  
1216   if(!bp.more()) 1200   if(!bp.more())
1217   { 1201   {
1218   // Last window: send atomically with EOF 1202   // Last window: send atomically with EOF
1219   auto [ec, n] = co_await write_eof_buffers_(bufs); 1203   auto [ec, n] = co_await write_eof_buffers_(bufs);
1220   total += n; 1204   total += n;
1221   co_return {ec, total}; 1205   co_return {ec, total};
1222   } 1206   }
1223   1207  
1224   auto [ec, n] = co_await write_(bufs); 1208   auto [ec, n] = co_await write_(bufs);
1225   total += n; 1209   total += n;
1226   if(ec) 1210   if(ec)
1227   co_return {ec, total}; 1211   co_return {ec, total};
1228   bp.consume(n); 1212   bp.consume(n);
1229   } 1213   }
1230   } 1214   }
1231   1215  
1232   // Synthesized path: prepare + buffer_copy + commit + commit_eof 1216   // Synthesized path: prepare + buffer_copy + commit + commit_eof
1233   buffer_param<CB> bp(buffers); 1217   buffer_param<CB> bp(buffers);
1234   std::size_t total = 0; 1218   std::size_t total = 0;
1235   1219  
1236   for(;;) 1220   for(;;)
1237   { 1221   {
1238   auto src = bp.data(); 1222   auto src = bp.data();
1239   if(src.empty()) 1223   if(src.empty())
1240   break; 1224   break;
1241   1225  
1242   mutable_buffer arr[detail::max_iovec_]; 1226   mutable_buffer arr[detail::max_iovec_];
1243   auto dst_bufs = prepare(arr); 1227   auto dst_bufs = prepare(arr);
1244   if(dst_bufs.empty()) 1228   if(dst_bufs.empty())
1245   { 1229   {
1246   auto [ec] = co_await commit(0); 1230   auto [ec] = co_await commit(0);
1247   if(ec) 1231   if(ec)
1248   co_return {ec, total}; 1232   co_return {ec, total};
1249   continue; 1233   continue;
1250   } 1234   }
1251   1235  
1252   auto n = buffer_copy(dst_bufs, src); 1236   auto n = buffer_copy(dst_bufs, src);
1253   auto [ec] = co_await commit(n); 1237   auto [ec] = co_await commit(n);
1254   if(ec) 1238   if(ec)
1255   co_return {ec, total}; 1239   co_return {ec, total};
1256   bp.consume(n); 1240   bp.consume(n);
1257   total += n; 1241   total += n;
1258   } 1242   }
1259   1243  
1260   auto [ec] = co_await commit_eof(0); 1244   auto [ec] = co_await commit_eof(0);
1261   if(ec) 1245   if(ec)
1262   co_return {ec, total}; 1246   co_return {ec, total};
1263   1247  
1264   co_return {{}, total}; 1248   co_return {{}, total};
DCB 1265 - 80  
1266 - //----------------------------------------------------------  
HITGIC 1267   } 1249   80 }
1268   1250  
1269   static_assert(BufferSink<any_buffer_sink>); 1251   static_assert(BufferSink<any_buffer_sink>);
1270   static_assert(WriteSink<any_buffer_sink>); 1252   static_assert(WriteSink<any_buffer_sink>);
1271   1253  
1272   } // namespace capy 1254   } // namespace capy
1273   } // namespace boost 1255   } // namespace boost
1274   1256  
1275   #endif 1257   #endif