90.57% Lines (192/212) 96.15% Functions (50/52)
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_WRITE_SINK_HPP 10   #ifndef BOOST_CAPY_IO_ANY_WRITE_SINK_HPP
11   #define BOOST_CAPY_IO_ANY_WRITE_SINK_HPP 11   #define BOOST_CAPY_IO_ANY_WRITE_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_array.hpp> 16 + #include <boost/capy/detail/buffer_array.hpp>
17   #include <boost/capy/buffers/buffer_param.hpp> 17   #include <boost/capy/buffers/buffer_param.hpp>
18   #include <boost/capy/concept/io_awaitable.hpp> 18   #include <boost/capy/concept/io_awaitable.hpp>
19   #include <boost/capy/concept/write_sink.hpp> 19   #include <boost/capy/concept/write_sink.hpp>
20   #include <coroutine> 20   #include <coroutine>
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 WriteSink. 38   /** Type-erased wrapper for any WriteSink.
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 WriteSink concept, enabling runtime polymorphism for 41   @ref WriteSink concept, enabling runtime polymorphism for
42   sink write operations. It uses cached awaitable storage to achieve 42   sink write 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 supports two construction modes: 45   The wrapper supports two construction modes:
46   - **Owning**: Pass by value to transfer ownership. The wrapper 46   - **Owning**: Pass by value to transfer ownership. The wrapper
47   allocates storage and owns the sink. 47   allocates storage and owns the sink.
48   - **Reference**: Pass a pointer to wrap without ownership. The 48   - **Reference**: Pass a pointer to wrap without ownership. The
49   pointed-to sink must outlive this wrapper. 49   pointed-to sink must outlive this wrapper.
50   50  
51   @par Awaitable Preallocation 51   @par Awaitable Preallocation
52   The constructor preallocates storage for the type-erased awaitable. 52   The constructor preallocates storage for the type-erased awaitable.
53   This reserves all virtual address space at server startup 53   This reserves all virtual address space at server startup
54   so memory usage can be measured up front, rather than 54   so memory usage can be measured up front, rather than
55   allocating piecemeal as traffic arrives. 55   allocating piecemeal as traffic arrives.
56   56  
57   @par Immediate Completion 57   @par Immediate Completion
58   Operations complete immediately without suspending when the 58   Operations complete immediately without suspending when the
59   buffer sequence is empty, or when the underlying sink's 59   buffer sequence is empty, or when the underlying sink's
60   awaitable reports readiness via `await_ready`. 60   awaitable reports readiness via `await_ready`.
61   61  
62   @par Thread Safety 62   @par Thread Safety
63   Not thread-safe. Concurrent operations on the same wrapper 63   Not thread-safe. Concurrent operations on the same wrapper
64   are undefined behavior. 64   are undefined behavior.
65   65  
66   @par Example 66   @par Example
67   @code 67   @code
68   // Owning - takes ownership of the sink 68   // Owning - takes ownership of the sink
69   any_write_sink ws(some_sink{args...}); 69   any_write_sink ws(some_sink{args...});
70   70  
71   // Reference - wraps without ownership 71   // Reference - wraps without ownership
72   some_sink sink; 72   some_sink sink;
73   any_write_sink ws(&sink); 73   any_write_sink ws(&sink);
74   74  
75   const_buffer buf(data, size); 75   const_buffer buf(data, size);
76   auto [ec, n] = co_await ws.write(std::span(&buf, 1)); 76   auto [ec, n] = co_await ws.write(std::span(&buf, 1));
77   auto [ec2] = co_await ws.write_eof(); 77   auto [ec2] = co_await ws.write_eof();
78   @endcode 78   @endcode
79   79  
80   @see any_write_stream, WriteSink 80   @see any_write_stream, WriteSink
81   */ 81   */
82   class any_write_sink 82   class any_write_sink
83   { 83   {
84   struct vtable; 84   struct vtable;
85   struct write_awaitable_ops; 85   struct write_awaitable_ops;
86   struct eof_awaitable_ops; 86   struct eof_awaitable_ops;
87   87  
88   template<WriteSink S> 88   template<WriteSink S>
89   struct vtable_for_impl; 89   struct vtable_for_impl;
90   90  
91   void* sink_ = nullptr; 91   void* sink_ = nullptr;
92   vtable const* vt_ = nullptr; 92   vtable const* vt_ = nullptr;
93   void* cached_awaitable_ = nullptr; 93   void* cached_awaitable_ = nullptr;
94   void* storage_ = nullptr; 94   void* storage_ = nullptr;
95   write_awaitable_ops const* active_write_ops_ = nullptr; 95   write_awaitable_ops const* active_write_ops_ = nullptr;
96   eof_awaitable_ops const* active_eof_ops_ = nullptr; 96   eof_awaitable_ops const* active_eof_ops_ = nullptr;
97   97  
98   public: 98   public:
99   /** Destructor. 99   /** Destructor.
100   100  
101   Destroys the owned sink (if any) and releases the cached 101   Destroys the owned sink (if any) and releases the cached
102   awaitable storage. 102   awaitable storage.
103   */ 103   */
104   ~any_write_sink(); 104   ~any_write_sink();
105   105  
106 - /** Default constructor. 106 + /** Construct a default instance.
107   107  
108   Constructs an empty wrapper. Operations on a default-constructed 108   Constructs an empty wrapper. Operations on a default-constructed
109   wrapper result in undefined behavior. 109   wrapper result in undefined behavior.
110   */ 110   */
111   any_write_sink() = default; 111   any_write_sink() = default;
112   112  
113   /** Non-copyable. 113   /** Non-copyable.
114   114  
115   The awaitable cache is per-instance and cannot be shared. 115   The awaitable cache is per-instance and cannot be shared.
116   */ 116   */
117   any_write_sink(any_write_sink const&) = delete; 117   any_write_sink(any_write_sink const&) = delete;
118   any_write_sink& operator=(any_write_sink const&) = delete; 118   any_write_sink& operator=(any_write_sink const&) = delete;
119   119  
120 - /** Move constructor. 120 + /** Construct by moving.
121   121  
122   Transfers ownership of the wrapped sink (if owned) and 122   Transfers ownership of the wrapped sink (if owned) and
123   cached awaitable storage from `other`. After the move, `other` is 123   cached awaitable storage from `other`. After the move, `other` is
124   in a default-constructed state. 124   in a default-constructed state.
125   125  
126   @param other The wrapper to move from. 126   @param other The wrapper to move from.
127   */ 127   */
HITCBC 128   1 any_write_sink(any_write_sink&& other) noexcept 128   1 any_write_sink(any_write_sink&& other) noexcept
HITCBC 129   1 : sink_(std::exchange(other.sink_, nullptr)) 129   1 : sink_(std::exchange(other.sink_, nullptr))
HITCBC 130   1 , vt_(std::exchange(other.vt_, nullptr)) 130   1 , vt_(std::exchange(other.vt_, nullptr))
HITCBC 131   1 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr)) 131   1 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr))
HITCBC 132   1 , storage_(std::exchange(other.storage_, nullptr)) 132   1 , storage_(std::exchange(other.storage_, nullptr))
HITCBC 133   1 , active_write_ops_(std::exchange(other.active_write_ops_, nullptr)) 133   1 , active_write_ops_(std::exchange(other.active_write_ops_, nullptr))
HITCBC 134   1 , active_eof_ops_(std::exchange(other.active_eof_ops_, nullptr)) 134   1 , active_eof_ops_(std::exchange(other.active_eof_ops_, nullptr))
135   { 135   {
HITCBC 136   1 } 136   1 }
137   137  
138 - /** Move assignment operator. 138 + /** Assign by moving.
139   139  
140   Destroys any owned sink and releases existing resources, 140   Destroys any owned sink and releases existing resources,
141   then transfers ownership from `other`. 141   then transfers ownership from `other`.
142   142  
143   @param other The wrapper to move from. 143   @param other The wrapper to move from.
144   @return Reference to this wrapper. 144   @return Reference to this wrapper.
145   */ 145   */
146   any_write_sink& 146   any_write_sink&
147   operator=(any_write_sink&& other) noexcept; 147   operator=(any_write_sink&& other) noexcept;
148   148  
149   /** Construct by taking ownership of a WriteSink. 149   /** Construct by taking ownership of a WriteSink.
150   150  
151   Allocates storage and moves the sink into this wrapper. 151   Allocates storage and moves the sink into this wrapper.
152   The wrapper owns the sink and will destroy it. 152   The wrapper owns the sink and will destroy it.
153   153  
154   @param s The sink to take ownership of. 154   @param s The sink to take ownership of.
155   */ 155   */
156   template<WriteSink S> 156   template<WriteSink S>
157   requires (!std::same_as<std::decay_t<S>, any_write_sink>) 157   requires (!std::same_as<std::decay_t<S>, any_write_sink>)
158   any_write_sink(S s); 158   any_write_sink(S s);
159   159  
160   /** Construct by wrapping a WriteSink without ownership. 160   /** Construct by wrapping a WriteSink without ownership.
161   161  
162   Wraps the given sink by pointer. The sink must remain 162   Wraps the given sink by pointer. The sink must remain
163   valid for the lifetime of this wrapper. 163   valid for the lifetime of this wrapper.
164   164  
165   @param s Pointer to the sink to wrap. 165   @param s Pointer to the sink to wrap.
166   */ 166   */
167   template<WriteSink S> 167   template<WriteSink S>
168   any_write_sink(S* s); 168   any_write_sink(S* s);
169   169  
170   /** Check if the wrapper contains a valid sink. 170   /** Check if the wrapper contains a valid sink.
171   171  
172   @return `true` if wrapping a sink, `false` if default-constructed 172   @return `true` if wrapping a sink, `false` if default-constructed
173   or moved-from. 173   or moved-from.
174   */ 174   */
175   bool 175   bool
HITCBC 176   15 has_value() const noexcept 176   15 has_value() const noexcept
177   { 177   {
HITCBC 178   15 return sink_ != nullptr; 178   15 return sink_ != nullptr;
179   } 179   }
180   180  
181   /** Check if the wrapper contains a valid sink. 181   /** Check if the wrapper contains a valid sink.
182   182  
183   @return `true` if wrapping a sink, `false` if default-constructed 183   @return `true` if wrapping a sink, `false` if default-constructed
184   or moved-from. 184   or moved-from.
185   */ 185   */
186   explicit 186   explicit
HITCBC 187   2 operator bool() const noexcept 187   2 operator bool() const noexcept
188   { 188   {
HITCBC 189   2 return has_value(); 189   2 return has_value();
190   } 190   }
191   191  
192   /** Initiate a partial write operation. 192   /** Initiate a partial write operation.
193   193  
194 - Writes one or more bytes from the provided buffer sequence. 194 + Attempt to write up to `buffer_size( buffers )` bytes from
195 - May consume less than the full sequence. 195 + the provided buffer sequence. May consume less than the
  196 + full sequence.
196   197  
197   @param buffers The buffer sequence containing data to write. 198   @param buffers The buffer sequence containing data to write.
198   199  
199 - @return An awaitable yielding `(error_code,std::size_t)`. 200 + @return An awaitable that await-returns `(error_code,std::size_t)`.
200   201  
201   @par Immediate Completion 202   @par Immediate Completion
202   The operation completes immediately without suspending 203   The operation completes immediately without suspending
203   the calling coroutine when: 204   the calling coroutine when:
204   @li The buffer sequence is empty, returning `{error_code{}, 0}`. 205   @li The buffer sequence is empty, returning `{error_code{}, 0}`.
205   @li The underlying sink's awaitable reports immediate 206   @li The underlying sink's awaitable reports immediate
206   readiness via `await_ready`. 207   readiness via `await_ready`.
207   208  
208   @note This is a partial operation and may not process the 209   @note This is a partial operation and may not process the
209   entire buffer sequence. Use @ref write for guaranteed 210   entire buffer sequence. Use @ref write for guaranteed
210   complete transfer. 211   complete transfer.
211   212  
212   @par Preconditions 213   @par Preconditions
213   The wrapper must contain a valid sink (`has_value() == true`). 214   The wrapper must contain a valid sink (`has_value() == true`).
214   */ 215   */
215   template<ConstBufferSequence CB> 216   template<ConstBufferSequence CB>
216   auto 217   auto
217   write_some(CB buffers); 218   write_some(CB buffers);
218   219  
219   /** Initiate a complete write operation. 220   /** Initiate a complete write operation.
220   221  
221   Writes data from the provided buffer sequence. The operation 222   Writes data from the provided buffer sequence. The operation
222   completes when all bytes have been consumed, or an error 223   completes when all bytes have been consumed, or an error
223   occurs. Forwards to the underlying sink's `write` operation, 224   occurs. Forwards to the underlying sink's `write` operation,
224   windowed through @ref buffer_param when the sequence exceeds 225   windowed through @ref buffer_param when the sequence exceeds
225   the per-call buffer limit. 226   the per-call buffer limit.
226   227  
227   @param buffers The buffer sequence containing data to write. 228   @param buffers The buffer sequence containing data to write.
228   229  
229 - @return An awaitable yielding `(error_code,std::size_t)`. 230 + @return An awaitable that await-returns `(error_code,std::size_t)`.
230   231  
231   @par Immediate Completion 232   @par Immediate Completion
232   The operation completes immediately without suspending 233   The operation completes immediately without suspending
233   the calling coroutine when: 234   the calling coroutine when:
234   @li The buffer sequence is empty, returning `{error_code{}, 0}`. 235   @li The buffer sequence is empty, returning `{error_code{}, 0}`.
235   @li Every underlying `write` call completes 236   @li Every underlying `write` call completes
236   immediately (the wrapped sink reports readiness 237   immediately (the wrapped sink reports readiness
237   via `await_ready` on each iteration). 238   via `await_ready` on each iteration).
238   239  
239   @par Preconditions 240   @par Preconditions
240   The wrapper must contain a valid sink (`has_value() == true`). 241   The wrapper must contain a valid sink (`has_value() == true`).
241   */ 242   */
242   template<ConstBufferSequence CB> 243   template<ConstBufferSequence CB>
243   io_task<std::size_t> 244   io_task<std::size_t>
244   write(CB buffers); 245   write(CB buffers);
245   246  
246   /** Atomically write data and signal end-of-stream. 247   /** Atomically write data and signal end-of-stream.
247   248  
248   Writes all data from the buffer sequence and then signals 249   Writes all data from the buffer sequence and then signals
249   end-of-stream. The implementation decides how to partition 250   end-of-stream. The implementation decides how to partition
250   the data across calls to the underlying sink's @ref write 251   the data across calls to the underlying sink's @ref write
251   and `write_eof`. When the caller's buffer sequence is 252   and `write_eof`. When the caller's buffer sequence is
252   non-empty, the final call to the underlying sink is always 253   non-empty, the final call to the underlying sink is always
253   `write_eof` with a non-empty buffer sequence. When the 254   `write_eof` with a non-empty buffer sequence. When the
254   caller's buffer sequence is empty, only `write_eof()` with 255   caller's buffer sequence is empty, only `write_eof()` with
255   no data is called. 256   no data is called.
256   257  
257   @param buffers The buffer sequence containing data to write. 258   @param buffers The buffer sequence containing data to write.
258   259  
259 - @return An awaitable yielding `(error_code,std::size_t)`. 260 + @return An awaitable that await-returns `(error_code,std::size_t)`.
260   261  
261   @par Immediate Completion 262   @par Immediate Completion
262   The operation completes immediately without suspending 263   The operation completes immediately without suspending
263   the calling coroutine when: 264   the calling coroutine when:
264   @li The buffer sequence is empty. Only the @ref write_eof() 265   @li The buffer sequence is empty. Only the @ref write_eof()
265   call is performed. 266   call is performed.
266   @li All underlying operations complete immediately (the 267   @li All underlying operations complete immediately (the
267   wrapped sink reports readiness via `await_ready`). 268   wrapped sink reports readiness via `await_ready`).
268   269  
269   @par Preconditions 270   @par Preconditions
270   The wrapper must contain a valid sink (`has_value() == true`). 271   The wrapper must contain a valid sink (`has_value() == true`).
271   */ 272   */
272   template<ConstBufferSequence CB> 273   template<ConstBufferSequence CB>
273   io_task<std::size_t> 274   io_task<std::size_t>
274   write_eof(CB buffers); 275   write_eof(CB buffers);
275   276  
276   /** Signal end of data. 277   /** Signal end of data.
277   278  
278   Indicates that no more data will be written to the sink. 279   Indicates that no more data will be written to the sink.
279   The operation completes when the sink is finalized, or 280   The operation completes when the sink is finalized, or
280   an error occurs. 281   an error occurs.
281   282  
282 - @return An awaitable yielding `(error_code)`. 283 + @return An awaitable that await-returns `(error_code)`.
283   284  
284   @par Immediate Completion 285   @par Immediate Completion
285   The operation completes immediately without suspending 286   The operation completes immediately without suspending
286   the calling coroutine when the underlying sink's awaitable 287   the calling coroutine when the underlying sink's awaitable
287   reports immediate readiness via `await_ready`. 288   reports immediate readiness via `await_ready`.
288   289  
289   @par Preconditions 290   @par Preconditions
290   The wrapper must contain a valid sink (`has_value() == true`). 291   The wrapper must contain a valid sink (`has_value() == true`).
291   */ 292   */
292   auto 293   auto
293   write_eof(); 294   write_eof();
294   295  
295   protected: 296   protected:
296   /** Rebind to a new sink after move. 297   /** Rebind to a new sink after move.
297   298  
298   Updates the internal pointer to reference a new sink object. 299   Updates the internal pointer to reference a new sink object.
299   Used by owning wrappers after move assignment when the owned 300   Used by owning wrappers after move assignment when the owned
300   object has moved to a new location. 301   object has moved to a new location.
301   302  
302   @param new_sink The new sink to bind to. Must be the same 303   @param new_sink The new sink to bind to. Must be the same
303   type as the original sink. 304   type as the original sink.
304   305  
305   @note Terminates if called with a sink of different type 306   @note Terminates if called with a sink of different type
306   than the original. 307   than the original.
307   */ 308   */
308   template<WriteSink S> 309   template<WriteSink S>
309   void 310   void
310   rebind(S& new_sink) noexcept 311   rebind(S& new_sink) noexcept
311   { 312   {
312   if(vt_ != &vtable_for_impl<S>::value) 313   if(vt_ != &vtable_for_impl<S>::value)
313   std::terminate(); 314   std::terminate();
314   sink_ = &new_sink; 315   sink_ = &new_sink;
315   } 316   }
316   317  
317   private: 318   private:
318   auto 319   auto
319   write_some_(std::span<const_buffer const> buffers); 320   write_some_(std::span<const_buffer const> buffers);
320   321  
321   auto 322   auto
322   write_(std::span<const_buffer const> buffers); 323   write_(std::span<const_buffer const> buffers);
323   324  
324   auto 325   auto
325   write_eof_buffers_(std::span<const_buffer const> buffers); 326   write_eof_buffers_(std::span<const_buffer const> buffers);
326   }; 327   };
327 - //----------------------------------------------------------  
328 -  
329   328  
330   struct any_write_sink::write_awaitable_ops 329   struct any_write_sink::write_awaitable_ops
331   { 330   {
332   bool (*await_ready)(void*); 331   bool (*await_ready)(void*);
333   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 332   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
334   io_result<std::size_t> (*await_resume)(void*); 333   io_result<std::size_t> (*await_resume)(void*);
335   void (*destroy)(void*) noexcept; 334   void (*destroy)(void*) noexcept;
336   }; 335   };
337   336  
338   struct any_write_sink::eof_awaitable_ops 337   struct any_write_sink::eof_awaitable_ops
339   { 338   {
340   bool (*await_ready)(void*); 339   bool (*await_ready)(void*);
341   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 340   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
342   io_result<> (*await_resume)(void*); 341   io_result<> (*await_resume)(void*);
343   void (*destroy)(void*) noexcept; 342   void (*destroy)(void*) noexcept;
344   }; 343   };
345   344  
346   struct any_write_sink::vtable 345   struct any_write_sink::vtable
347   { 346   {
348   write_awaitable_ops const* (*construct_write_some_awaitable)( 347   write_awaitable_ops const* (*construct_write_some_awaitable)(
349   void* sink, 348   void* sink,
350   void* storage, 349   void* storage,
351   std::span<const_buffer const> buffers); 350   std::span<const_buffer const> buffers);
352   write_awaitable_ops const* (*construct_write_awaitable)( 351   write_awaitable_ops const* (*construct_write_awaitable)(
353   void* sink, 352   void* sink,
354   void* storage, 353   void* storage,
355   std::span<const_buffer const> buffers); 354   std::span<const_buffer const> buffers);
356   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)( 355   write_awaitable_ops const* (*construct_write_eof_buffers_awaitable)(
357   void* sink, 356   void* sink,
358   void* storage, 357   void* storage,
359   std::span<const_buffer const> buffers); 358   std::span<const_buffer const> buffers);
360   eof_awaitable_ops const* (*construct_eof_awaitable)( 359   eof_awaitable_ops const* (*construct_eof_awaitable)(
361   void* sink, 360   void* sink,
362   void* storage); 361   void* storage);
363   std::size_t awaitable_size; 362   std::size_t awaitable_size;
364   std::size_t awaitable_align; 363   std::size_t awaitable_align;
365   void (*destroy)(void*) noexcept; 364   void (*destroy)(void*) noexcept;
366   }; 365   };
367   366  
368   template<WriteSink S> 367   template<WriteSink S>
369   struct any_write_sink::vtable_for_impl 368   struct any_write_sink::vtable_for_impl
370   { 369   {
371   using WriteSomeAwaitable = decltype(std::declval<S&>().write_some( 370   using WriteSomeAwaitable = decltype(std::declval<S&>().write_some(
372   std::span<const_buffer const>{})); 371   std::span<const_buffer const>{}));
373   using WriteAwaitable = decltype(std::declval<S&>().write( 372   using WriteAwaitable = decltype(std::declval<S&>().write(
374   std::span<const_buffer const>{})); 373   std::span<const_buffer const>{}));
375   using WriteEofBuffersAwaitable = decltype(std::declval<S&>().write_eof( 374   using WriteEofBuffersAwaitable = decltype(std::declval<S&>().write_eof(
376   std::span<const_buffer const>{})); 375   std::span<const_buffer const>{}));
377   using EofAwaitable = decltype(std::declval<S&>().write_eof()); 376   using EofAwaitable = decltype(std::declval<S&>().write_eof());
378   377  
379   static void 378   static void
HITCBC 380   6 do_destroy_impl(void* sink) noexcept 379   6 do_destroy_impl(void* sink) noexcept
381   { 380   {
HITCBC 382   6 static_cast<S*>(sink)->~S(); 381   6 static_cast<S*>(sink)->~S();
HITCBC 383   6 } 382   6 }
384   383  
385   static write_awaitable_ops const* 384   static write_awaitable_ops const*
HITCBC 386   40 construct_write_some_awaitable_impl( 385   40 construct_write_some_awaitable_impl(
387   void* sink, 386   void* sink,
388   void* storage, 387   void* storage,
389   std::span<const_buffer const> buffers) 388   std::span<const_buffer const> buffers)
390   { 389   {
HITCBC 391   40 auto& s = *static_cast<S*>(sink); 390   40 auto& s = *static_cast<S*>(sink);
HITCBC 392   40 ::new(storage) WriteSomeAwaitable(s.write_some(buffers)); 391   40 ::new(storage) WriteSomeAwaitable(s.write_some(buffers));
393   392  
394   static constexpr write_awaitable_ops ops = { 393   static constexpr write_awaitable_ops ops = {
HITCBC 395   40 +[](void* p) { 394   40 +[](void* p) {
HITCBC 396   40 return static_cast<WriteSomeAwaitable*>(p)->await_ready(); 395   40 return static_cast<WriteSomeAwaitable*>(p)->await_ready();
397   }, 396   },
HITCBC 398   2 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 397   2 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 399   2 return detail::call_await_suspend( 398   2 return detail::call_await_suspend(
HITCBC 400   2 static_cast<WriteSomeAwaitable*>(p), h, env); 399   2 static_cast<WriteSomeAwaitable*>(p), h, env);
401   }, 400   },
HITCBC 402   38 +[](void* p) { 401   38 +[](void* p) {
HITCBC 403   38 return static_cast<WriteSomeAwaitable*>(p)->await_resume(); 402   38 return static_cast<WriteSomeAwaitable*>(p)->await_resume();
404   }, 403   },
HITCBC 405   42 +[](void* p) noexcept { 404   42 +[](void* p) noexcept {
HITCBC 406   2 static_cast<WriteSomeAwaitable*>(p)->~WriteSomeAwaitable(); 405   2 static_cast<WriteSomeAwaitable*>(p)->~WriteSomeAwaitable();
407   } 406   }
408   }; 407   };
HITCBC 409   40 return &ops; 408   40 return &ops;
410   } 409   }
411   410  
412   static write_awaitable_ops const* 411   static write_awaitable_ops const*
HITCBC 413   78 construct_write_awaitable_impl( 412   78 construct_write_awaitable_impl(
414   void* sink, 413   void* sink,
415   void* storage, 414   void* storage,
416   std::span<const_buffer const> buffers) 415   std::span<const_buffer const> buffers)
417   { 416   {
HITCBC 418   78 auto& s = *static_cast<S*>(sink); 417   78 auto& s = *static_cast<S*>(sink);
HITCBC 419   78 ::new(storage) WriteAwaitable(s.write(buffers)); 418   78 ::new(storage) WriteAwaitable(s.write(buffers));
420   419  
421   static constexpr write_awaitable_ops ops = { 420   static constexpr write_awaitable_ops ops = {
HITCBC 422   78 +[](void* p) { 421   78 +[](void* p) {
HITCBC 423   78 return static_cast<WriteAwaitable*>(p)->await_ready(); 422   78 return static_cast<WriteAwaitable*>(p)->await_ready();
424   }, 423   },
MISUBC 425   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 424   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 426   return detail::call_await_suspend( 425   return detail::call_await_suspend(
MISUBC 427   static_cast<WriteAwaitable*>(p), h, env); 426   static_cast<WriteAwaitable*>(p), h, env);
428   }, 427   },
HITCBC 429   78 +[](void* p) { 428   78 +[](void* p) {
HITCBC 430   78 return static_cast<WriteAwaitable*>(p)->await_resume(); 429   78 return static_cast<WriteAwaitable*>(p)->await_resume();
431   }, 430   },
HITCBC 432   78 +[](void* p) noexcept { 431   78 +[](void* p) noexcept {
MISUBC 433   static_cast<WriteAwaitable*>(p)->~WriteAwaitable(); 432   static_cast<WriteAwaitable*>(p)->~WriteAwaitable();
434   } 433   }
435   }; 434   };
HITCBC 436   78 return &ops; 435   78 return &ops;
437   } 436   }
438   437  
439   static write_awaitable_ops const* 438   static write_awaitable_ops const*
HITCBC 440   16 construct_write_eof_buffers_awaitable_impl( 439   16 construct_write_eof_buffers_awaitable_impl(
441   void* sink, 440   void* sink,
442   void* storage, 441   void* storage,
443   std::span<const_buffer const> buffers) 442   std::span<const_buffer const> buffers)
444   { 443   {
HITCBC 445   16 auto& s = *static_cast<S*>(sink); 444   16 auto& s = *static_cast<S*>(sink);
HITCBC 446   16 ::new(storage) WriteEofBuffersAwaitable(s.write_eof(buffers)); 445   16 ::new(storage) WriteEofBuffersAwaitable(s.write_eof(buffers));
447   446  
448   static constexpr write_awaitable_ops ops = { 447   static constexpr write_awaitable_ops ops = {
HITCBC 449   16 +[](void* p) { 448   16 +[](void* p) {
HITCBC 450   16 return static_cast<WriteEofBuffersAwaitable*>(p)->await_ready(); 449   16 return static_cast<WriteEofBuffersAwaitable*>(p)->await_ready();
451   }, 450   },
MISUBC 452   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 451   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 453   return detail::call_await_suspend( 452   return detail::call_await_suspend(
MISUBC 454   static_cast<WriteEofBuffersAwaitable*>(p), h, env); 453   static_cast<WriteEofBuffersAwaitable*>(p), h, env);
455   }, 454   },
HITCBC 456   16 +[](void* p) { 455   16 +[](void* p) {
HITCBC 457   16 return static_cast<WriteEofBuffersAwaitable*>(p)->await_resume(); 456   16 return static_cast<WriteEofBuffersAwaitable*>(p)->await_resume();
458   }, 457   },
HITCBC 459   16 +[](void* p) noexcept { 458   16 +[](void* p) noexcept {
MISUBC 460   static_cast<WriteEofBuffersAwaitable*>(p)->~WriteEofBuffersAwaitable(); 459   static_cast<WriteEofBuffersAwaitable*>(p)->~WriteEofBuffersAwaitable();
461   } 460   }
462   }; 461   };
HITCBC 463   16 return &ops; 462   16 return &ops;
464   } 463   }
465   464  
466   static eof_awaitable_ops const* 465   static eof_awaitable_ops const*
HITCBC 467   17 construct_eof_awaitable_impl( 466   17 construct_eof_awaitable_impl(
468   void* sink, 467   void* sink,
469   void* storage) 468   void* storage)
470   { 469   {
HITCBC 471   17 auto& s = *static_cast<S*>(sink); 470   17 auto& s = *static_cast<S*>(sink);
HITCBC 472   17 ::new(storage) EofAwaitable(s.write_eof()); 471   17 ::new(storage) EofAwaitable(s.write_eof());
473   472  
474   static constexpr eof_awaitable_ops ops = { 473   static constexpr eof_awaitable_ops ops = {
HITCBC 475   17 +[](void* p) { 474   17 +[](void* p) {
HITCBC 476   17 return static_cast<EofAwaitable*>(p)->await_ready(); 475   17 return static_cast<EofAwaitable*>(p)->await_ready();
477   }, 476   },
HITCBC 478   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) { 477   1 +[](void* p, std::coroutine_handle<> h, io_env const* env) {
HITCBC 479   1 return detail::call_await_suspend( 478   1 return detail::call_await_suspend(
HITCBC 480   1 static_cast<EofAwaitable*>(p), h, env); 479   1 static_cast<EofAwaitable*>(p), h, env);
481   }, 480   },
HITCBC 482   16 +[](void* p) { 481   16 +[](void* p) {
HITCBC 483   16 return static_cast<EofAwaitable*>(p)->await_resume(); 482   16 return static_cast<EofAwaitable*>(p)->await_resume();
484   }, 483   },
HITCBC 485   18 +[](void* p) noexcept { 484   18 +[](void* p) noexcept {
HITCBC 486   1 static_cast<EofAwaitable*>(p)->~EofAwaitable(); 485   1 static_cast<EofAwaitable*>(p)->~EofAwaitable();
487   } 486   }
488   }; 487   };
HITCBC 489   17 return &ops; 488   17 return &ops;
490   } 489   }
491   490  
492   static constexpr std::size_t max4( 491   static constexpr std::size_t max4(
493   std::size_t a, std::size_t b, 492   std::size_t a, std::size_t b,
494   std::size_t c, std::size_t d) noexcept 493   std::size_t c, std::size_t d) noexcept
495   { 494   {
496   std::size_t ab = a > b ? a : b; 495   std::size_t ab = a > b ? a : b;
497   std::size_t cd = c > d ? c : d; 496   std::size_t cd = c > d ? c : d;
498   return ab > cd ? ab : cd; 497   return ab > cd ? ab : cd;
499   } 498   }
500   499  
501   static constexpr std::size_t max_awaitable_size = 500   static constexpr std::size_t max_awaitable_size =
502   max4(sizeof(WriteSomeAwaitable), 501   max4(sizeof(WriteSomeAwaitable),
503   sizeof(WriteAwaitable), 502   sizeof(WriteAwaitable),
504   sizeof(WriteEofBuffersAwaitable), 503   sizeof(WriteEofBuffersAwaitable),
505   sizeof(EofAwaitable)); 504   sizeof(EofAwaitable));
506   505  
507   static constexpr std::size_t max_awaitable_align = 506   static constexpr std::size_t max_awaitable_align =
508   max4(alignof(WriteSomeAwaitable), 507   max4(alignof(WriteSomeAwaitable),
509   alignof(WriteAwaitable), 508   alignof(WriteAwaitable),
510   alignof(WriteEofBuffersAwaitable), 509   alignof(WriteEofBuffersAwaitable),
511   alignof(EofAwaitable)); 510   alignof(EofAwaitable));
512   511  
513   static constexpr vtable value = { 512   static constexpr vtable value = {
514   &construct_write_some_awaitable_impl, 513   &construct_write_some_awaitable_impl,
515   &construct_write_awaitable_impl, 514   &construct_write_awaitable_impl,
516   &construct_write_eof_buffers_awaitable_impl, 515   &construct_write_eof_buffers_awaitable_impl,
517   &construct_eof_awaitable_impl, 516   &construct_eof_awaitable_impl,
518   max_awaitable_size, 517   max_awaitable_size,
519   max_awaitable_align, 518   max_awaitable_align,
520   &do_destroy_impl 519   &do_destroy_impl
521   }; 520   };
522   }; 521   };
523 - //----------------------------------------------------------  
524 -  
525   522  
526   inline 523   inline
HITCBC 527   129 any_write_sink::~any_write_sink() 524   129 any_write_sink::~any_write_sink()
528   { 525   {
HITCBC 529   129 if(storage_) 526   129 if(storage_)
530   { 527   {
HITCBC 531   6 vt_->destroy(sink_); 528   6 vt_->destroy(sink_);
HITCBC 532   6 ::operator delete(storage_); 529   6 ::operator delete(storage_);
533   } 530   }
HITCBC 534   129 if(cached_awaitable_) 531   129 if(cached_awaitable_)
535   { 532   {
HITCBC 536   124 if(active_write_ops_) 533   124 if(active_write_ops_)
HITCBC 537   1 active_write_ops_->destroy(cached_awaitable_); 534   1 active_write_ops_->destroy(cached_awaitable_);
HITCBC 538   123 else if(active_eof_ops_) 535   123 else if(active_eof_ops_)
HITCBC 539   1 active_eof_ops_->destroy(cached_awaitable_); 536   1 active_eof_ops_->destroy(cached_awaitable_);
HITCBC 540   124 ::operator delete(cached_awaitable_); 537   124 ::operator delete(cached_awaitable_);
541   } 538   }
HITCBC 542   129 } 539   129 }
543   540  
544   inline any_write_sink& 541   inline any_write_sink&
HITCBC 545   2 any_write_sink::operator=(any_write_sink&& other) noexcept 542   2 any_write_sink::operator=(any_write_sink&& other) noexcept
546   { 543   {
HITCBC 547   2 if(this != &other) 544   2 if(this != &other)
548   { 545   {
HITCBC 549   2 if(storage_) 546   2 if(storage_)
550   { 547   {
MISUBC 551   vt_->destroy(sink_); 548   vt_->destroy(sink_);
MISUBC 552   ::operator delete(storage_); 549   ::operator delete(storage_);
553   } 550   }
HITCBC 554   2 if(cached_awaitable_) 551   2 if(cached_awaitable_)
555   { 552   {
HITCBC 556   1 if(active_write_ops_) 553   1 if(active_write_ops_)
HITCBC 557   1 active_write_ops_->destroy(cached_awaitable_); 554   1 active_write_ops_->destroy(cached_awaitable_);
MISUBC 558   else if(active_eof_ops_) 555   else if(active_eof_ops_)
MISUBC 559   active_eof_ops_->destroy(cached_awaitable_); 556   active_eof_ops_->destroy(cached_awaitable_);
HITCBC 560   1 ::operator delete(cached_awaitable_); 557   1 ::operator delete(cached_awaitable_);
561   } 558   }
HITCBC 562   2 sink_ = std::exchange(other.sink_, nullptr); 559   2 sink_ = std::exchange(other.sink_, nullptr);
HITCBC 563   2 vt_ = std::exchange(other.vt_, nullptr); 560   2 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 564   2 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 561   2 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 565   2 storage_ = std::exchange(other.storage_, nullptr); 562   2 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 566   2 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr); 563   2 active_write_ops_ = std::exchange(other.active_write_ops_, nullptr);
HITCBC 567   2 active_eof_ops_ = std::exchange(other.active_eof_ops_, nullptr); 564   2 active_eof_ops_ = std::exchange(other.active_eof_ops_, nullptr);
568   } 565   }
HITCBC 569   2 return *this; 566   2 return *this;
570   } 567   }
571   568  
572   template<WriteSink S> 569   template<WriteSink S>
573   requires (!std::same_as<std::decay_t<S>, any_write_sink>) 570   requires (!std::same_as<std::decay_t<S>, any_write_sink>)
HITCBC 574   6 any_write_sink::any_write_sink(S s) 571   6 any_write_sink::any_write_sink(S s)
HITCBC 575   6 : vt_(&vtable_for_impl<S>::value) 572   6 : vt_(&vtable_for_impl<S>::value)
576   { 573   {
577   struct guard { 574   struct guard {
578   any_write_sink* self; 575   any_write_sink* self;
579   bool committed = false; 576   bool committed = false;
HITCBC 580   6 ~guard() { 577   6 ~guard() {
HITCBC 581   6 if(!committed && self->storage_) { 578   6 if(!committed && self->storage_) {
MISUBC 582   self->vt_->destroy(self->sink_); 579   self->vt_->destroy(self->sink_);
MISUBC 583   ::operator delete(self->storage_); 580   ::operator delete(self->storage_);
MISUBC 584   self->storage_ = nullptr; 581   self->storage_ = nullptr;
MISUBC 585   self->sink_ = nullptr; 582   self->sink_ = nullptr;
586   } 583   }
HITCBC 587   6 } 584   6 }
HITCBC 588   6 } g{this}; 585   6 } g{this};
589   586  
HITCBC 590   6 storage_ = ::operator new(sizeof(S)); 587   6 storage_ = ::operator new(sizeof(S));
HITCBC 591   6 sink_ = ::new(storage_) S(std::move(s)); 588   6 sink_ = ::new(storage_) S(std::move(s));
592   589  
593   // Preallocate the awaitable storage (sized for max of write/eof) 590   // Preallocate the awaitable storage (sized for max of write/eof)
HITCBC 594   6 cached_awaitable_ = ::operator new(vt_->awaitable_size); 591   6 cached_awaitable_ = ::operator new(vt_->awaitable_size);
595   592  
HITCBC 596   6 g.committed = true; 593   6 g.committed = true;
HITCBC 597   6 } 594   6 }
598   595  
599   template<WriteSink S> 596   template<WriteSink S>
HITCBC 600   119 any_write_sink::any_write_sink(S* s) 597   119 any_write_sink::any_write_sink(S* s)
HITCBC 601   119 : sink_(s) 598   119 : sink_(s)
HITCBC 602   119 , vt_(&vtable_for_impl<S>::value) 599   119 , vt_(&vtable_for_impl<S>::value)
603   { 600   {
604   // Preallocate the awaitable storage (sized for max of write/eof) 601   // Preallocate the awaitable storage (sized for max of write/eof)
HITCBC 605   119 cached_awaitable_ = ::operator new(vt_->awaitable_size); 602   119 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 606   119 } 603   119 }
607 - //----------------------------------------------------------  
608 -  
609   604  
610   inline auto 605   inline auto
611   any_write_sink::write_some_( 606   any_write_sink::write_some_(
612   std::span<const_buffer const> buffers) 607   std::span<const_buffer const> buffers)
613   { 608   {
614   struct awaitable 609   struct awaitable
615   { 610   {
616   any_write_sink* self_; 611   any_write_sink* self_;
617   std::span<const_buffer const> buffers_; 612   std::span<const_buffer const> buffers_;
618   613  
619   bool 614   bool
620   await_ready() const noexcept 615   await_ready() const noexcept
621   { 616   {
622   return false; 617   return false;
623   } 618   }
624   619  
625   std::coroutine_handle<> 620   std::coroutine_handle<>
626   await_suspend(std::coroutine_handle<> h, io_env const* env) 621   await_suspend(std::coroutine_handle<> h, io_env const* env)
627   { 622   {
628   self_->active_write_ops_ = self_->vt_->construct_write_some_awaitable( 623   self_->active_write_ops_ = self_->vt_->construct_write_some_awaitable(
629   self_->sink_, 624   self_->sink_,
630   self_->cached_awaitable_, 625   self_->cached_awaitable_,
631   buffers_); 626   buffers_);
632   627  
633   if(self_->active_write_ops_->await_ready(self_->cached_awaitable_)) 628   if(self_->active_write_ops_->await_ready(self_->cached_awaitable_))
634   return h; 629   return h;
635   630  
636   return self_->active_write_ops_->await_suspend( 631   return self_->active_write_ops_->await_suspend(
637   self_->cached_awaitable_, h, env); 632   self_->cached_awaitable_, h, env);
638   } 633   }
639   634  
640   io_result<std::size_t> 635   io_result<std::size_t>
641   await_resume() 636   await_resume()
642   { 637   {
643   struct guard { 638   struct guard {
644   any_write_sink* self; 639   any_write_sink* self;
645   ~guard() { 640   ~guard() {
646   self->active_write_ops_->destroy(self->cached_awaitable_); 641   self->active_write_ops_->destroy(self->cached_awaitable_);
647   self->active_write_ops_ = nullptr; 642   self->active_write_ops_ = nullptr;
648   } 643   }
649   } g{self_}; 644   } g{self_};
650   return self_->active_write_ops_->await_resume( 645   return self_->active_write_ops_->await_resume(
651   self_->cached_awaitable_); 646   self_->cached_awaitable_);
652   } 647   }
653   }; 648   };
654   return awaitable{this, buffers}; 649   return awaitable{this, buffers};
655   } 650   }
656   651  
657   inline auto 652   inline auto
HITCBC 658   78 any_write_sink::write_( 653   78 any_write_sink::write_(
659   std::span<const_buffer const> buffers) 654   std::span<const_buffer const> buffers)
660   { 655   {
661   struct awaitable 656   struct awaitable
662   { 657   {
663   any_write_sink* self_; 658   any_write_sink* self_;
664   std::span<const_buffer const> buffers_; 659   std::span<const_buffer const> buffers_;
665   660  
666   bool 661   bool
HITCBC 667   78 await_ready() const noexcept 662   78 await_ready() const noexcept
668   { 663   {
HITCBC 669   78 return false; 664   78 return false;
670   } 665   }
671   666  
672   std::coroutine_handle<> 667   std::coroutine_handle<>
HITCBC 673   78 await_suspend(std::coroutine_handle<> h, io_env const* env) 668   78 await_suspend(std::coroutine_handle<> h, io_env const* env)
674   { 669   {
HITCBC 675   156 self_->active_write_ops_ = self_->vt_->construct_write_awaitable( 670   156 self_->active_write_ops_ = self_->vt_->construct_write_awaitable(
HITCBC 676   78 self_->sink_, 671   78 self_->sink_,
HITCBC 677   78 self_->cached_awaitable_, 672   78 self_->cached_awaitable_,
678   buffers_); 673   buffers_);
679   674  
HITCBC 680   78 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_)) 675   78 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_))
HITCBC 681   78 return h; 676   78 return h;
682   677  
MISUBC 683   return self_->active_write_ops_->await_suspend( 678   return self_->active_write_ops_->await_suspend(
MISUBC 684   self_->cached_awaitable_, h, env); 679   self_->cached_awaitable_, h, env);
685   } 680   }
686   681  
687   io_result<std::size_t> 682   io_result<std::size_t>
HITCBC 688   78 await_resume() 683   78 await_resume()
689   { 684   {
690   struct guard { 685   struct guard {
691   any_write_sink* self; 686   any_write_sink* self;
HITCBC 692   78 ~guard() { 687   78 ~guard() {
HITCBC 693   78 self->active_write_ops_->destroy(self->cached_awaitable_); 688   78 self->active_write_ops_->destroy(self->cached_awaitable_);
HITCBC 694   78 self->active_write_ops_ = nullptr; 689   78 self->active_write_ops_ = nullptr;
HITCBC 695   78 } 690   78 }
HITCBC 696   78 } g{self_}; 691   78 } g{self_};
HITCBC 697   78 return self_->active_write_ops_->await_resume( 692   78 return self_->active_write_ops_->await_resume(
HITCBC 698   135 self_->cached_awaitable_); 693   135 self_->cached_awaitable_);
HITCBC 699   78 } 694   78 }
700   }; 695   };
HITCBC 701   78 return awaitable{this, buffers}; 696   78 return awaitable{this, buffers};
702   } 697   }
703   698  
704   inline auto 699   inline auto
HITCBC 705   17 any_write_sink::write_eof() 700   17 any_write_sink::write_eof()
706   { 701   {
707   struct awaitable 702   struct awaitable
708   { 703   {
709   any_write_sink* self_; 704   any_write_sink* self_;
710   705  
711   bool 706   bool
HITCBC 712   17 await_ready() const noexcept 707   17 await_ready() const noexcept
713   { 708   {
HITCBC 714   17 return false; 709   17 return false;
715   } 710   }
716   711  
717   std::coroutine_handle<> 712   std::coroutine_handle<>
HITCBC 718   17 await_suspend(std::coroutine_handle<> h, io_env const* env) 713   17 await_suspend(std::coroutine_handle<> h, io_env const* env)
719   { 714   {
720   // Construct the underlying awaitable into cached storage 715   // Construct the underlying awaitable into cached storage
HITCBC 721   34 self_->active_eof_ops_ = self_->vt_->construct_eof_awaitable( 716   34 self_->active_eof_ops_ = self_->vt_->construct_eof_awaitable(
HITCBC 722   17 self_->sink_, 717   17 self_->sink_,
HITCBC 723   17 self_->cached_awaitable_); 718   17 self_->cached_awaitable_);
724   719  
725   // Check if underlying is immediately ready 720   // Check if underlying is immediately ready
HITCBC 726   17 if(self_->active_eof_ops_->await_ready(self_->cached_awaitable_)) 721   17 if(self_->active_eof_ops_->await_ready(self_->cached_awaitable_))
HITCBC 727   16 return h; 722   16 return h;
728   723  
729   // Forward to underlying awaitable 724   // Forward to underlying awaitable
HITCBC 730   1 return self_->active_eof_ops_->await_suspend( 725   1 return self_->active_eof_ops_->await_suspend(
HITCBC 731   1 self_->cached_awaitable_, h, env); 726   1 self_->cached_awaitable_, h, env);
732   } 727   }
733   728  
734   io_result<> 729   io_result<>
HITCBC 735   16 await_resume() 730   16 await_resume()
736   { 731   {
737   struct guard { 732   struct guard {
738   any_write_sink* self; 733   any_write_sink* self;
HITCBC 739   16 ~guard() { 734   16 ~guard() {
HITCBC 740   16 self->active_eof_ops_->destroy(self->cached_awaitable_); 735   16 self->active_eof_ops_->destroy(self->cached_awaitable_);
HITCBC 741   16 self->active_eof_ops_ = nullptr; 736   16 self->active_eof_ops_ = nullptr;
HITCBC 742   16 } 737   16 }
HITCBC 743   16 } g{self_}; 738   16 } g{self_};
HITCBC 744   16 return self_->active_eof_ops_->await_resume( 739   16 return self_->active_eof_ops_->await_resume(
HITCBC 745   27 self_->cached_awaitable_); 740   27 self_->cached_awaitable_);
HITCBC 746   16 } 741   16 }
747   }; 742   };
HITCBC 748   17 return awaitable{this}; 743   17 return awaitable{this};
749   } 744   }
750   745  
751   inline auto 746   inline auto
HITCBC 752   16 any_write_sink::write_eof_buffers_( 747   16 any_write_sink::write_eof_buffers_(
753   std::span<const_buffer const> buffers) 748   std::span<const_buffer const> buffers)
754   { 749   {
755   struct awaitable 750   struct awaitable
756   { 751   {
757   any_write_sink* self_; 752   any_write_sink* self_;
758   std::span<const_buffer const> buffers_; 753   std::span<const_buffer const> buffers_;
759   754  
760   bool 755   bool
HITCBC 761   16 await_ready() const noexcept 756   16 await_ready() const noexcept
762   { 757   {
HITCBC 763   16 return false; 758   16 return false;
764   } 759   }
765   760  
766   std::coroutine_handle<> 761   std::coroutine_handle<>
HITCBC 767   16 await_suspend(std::coroutine_handle<> h, io_env const* env) 762   16 await_suspend(std::coroutine_handle<> h, io_env const* env)
768   { 763   {
HITCBC 769   32 self_->active_write_ops_ = 764   32 self_->active_write_ops_ =
HITCBC 770   32 self_->vt_->construct_write_eof_buffers_awaitable( 765   32 self_->vt_->construct_write_eof_buffers_awaitable(
HITCBC 771   16 self_->sink_, 766   16 self_->sink_,
HITCBC 772   16 self_->cached_awaitable_, 767   16 self_->cached_awaitable_,
773   buffers_); 768   buffers_);
774   769  
HITCBC 775   16 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_)) 770   16 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_))
HITCBC 776   16 return h; 771   16 return h;
777   772  
MISUBC 778   return self_->active_write_ops_->await_suspend( 773   return self_->active_write_ops_->await_suspend(
MISUBC 779   self_->cached_awaitable_, h, env); 774   self_->cached_awaitable_, h, env);
780   } 775   }
781   776  
782   io_result<std::size_t> 777   io_result<std::size_t>
HITCBC 783   16 await_resume() 778   16 await_resume()
784   { 779   {
785   struct guard { 780   struct guard {
786   any_write_sink* self; 781   any_write_sink* self;
HITCBC 787   16 ~guard() { 782   16 ~guard() {
HITCBC 788   16 self->active_write_ops_->destroy(self->cached_awaitable_); 783   16 self->active_write_ops_->destroy(self->cached_awaitable_);
HITCBC 789   16 self->active_write_ops_ = nullptr; 784   16 self->active_write_ops_ = nullptr;
HITCBC 790   16 } 785   16 }
HITCBC 791   16 } g{self_}; 786   16 } g{self_};
HITCBC 792   16 return self_->active_write_ops_->await_resume( 787   16 return self_->active_write_ops_->await_resume(
HITCBC 793   27 self_->cached_awaitable_); 788   27 self_->cached_awaitable_);
HITCBC 794   16 } 789   16 }
795   }; 790   };
HITCBC 796   16 return awaitable{this, buffers}; 791   16 return awaitable{this, buffers};
797   } 792   }
798   793  
799   template<ConstBufferSequence CB> 794   template<ConstBufferSequence CB>
800   auto 795   auto
HITCBC 801   42 any_write_sink::write_some(CB buffers) 796   42 any_write_sink::write_some(CB buffers)
802   { 797   {
803   struct awaitable 798   struct awaitable
804   { 799   {
805   any_write_sink* self_; 800   any_write_sink* self_;
806 - const_buffer_array<detail::max_iovec_> ba_; 801 + detail::const_buffer_array<detail::max_iovec_> ba_;
807   802  
HITCBC 808   42 awaitable( 803   42 awaitable(
809   any_write_sink* self, 804   any_write_sink* self,
810   CB const& buffers) 805   CB const& buffers)
HITCBC 811   42 : self_(self) 806   42 : self_(self)
HITCBC 812   42 , ba_(buffers) 807   42 , ba_(buffers)
813   { 808   {
HITCBC 814   42 } 809   42 }
815   810  
816   bool 811   bool
HITCBC 817   42 await_ready() const noexcept 812   42 await_ready() const noexcept
818   { 813   {
HITCBC 819   42 return ba_.to_span().empty(); 814   42 return ba_.to_span().empty();
820   } 815   }
821   816  
822   std::coroutine_handle<> 817   std::coroutine_handle<>
HITCBC 823   40 await_suspend(std::coroutine_handle<> h, io_env const* env) 818   40 await_suspend(std::coroutine_handle<> h, io_env const* env)
824   { 819   {
HITCBC 825   40 self_->active_write_ops_ = self_->vt_->construct_write_some_awaitable( 820   40 self_->active_write_ops_ = self_->vt_->construct_write_some_awaitable(
HITCBC 826   40 self_->sink_, 821   40 self_->sink_,
HITCBC 827   40 self_->cached_awaitable_, 822   40 self_->cached_awaitable_,
HITCBC 828   40 ba_.to_span()); 823   40 ba_.to_span());
829   824  
HITCBC 830   40 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_)) 825   40 if(self_->active_write_ops_->await_ready(self_->cached_awaitable_))
HITCBC 831   38 return h; 826   38 return h;
832   827  
HITCBC 833   2 return self_->active_write_ops_->await_suspend( 828   2 return self_->active_write_ops_->await_suspend(
HITCBC 834   2 self_->cached_awaitable_, h, env); 829   2 self_->cached_awaitable_, h, env);
835   } 830   }
836   831  
837   io_result<std::size_t> 832   io_result<std::size_t>
HITCBC 838   40 await_resume() 833   40 await_resume()
839   { 834   {
HITCBC 840   40 if(ba_.to_span().empty()) 835   40 if(ba_.to_span().empty())
HITCBC 841   2 return {{}, 0}; 836   2 return {{}, 0};
842   837  
843   struct guard { 838   struct guard {
844   any_write_sink* self; 839   any_write_sink* self;
HITCBC 845   38 ~guard() { 840   38 ~guard() {
HITCBC 846   38 self->active_write_ops_->destroy(self->cached_awaitable_); 841   38 self->active_write_ops_->destroy(self->cached_awaitable_);
HITCBC 847   38 self->active_write_ops_ = nullptr; 842   38 self->active_write_ops_ = nullptr;
HITCBC 848   38 } 843   38 }
HITCBC 849   38 } g{self_}; 844   38 } g{self_};
HITCBC 850   38 return self_->active_write_ops_->await_resume( 845   38 return self_->active_write_ops_->await_resume(
HITCBC 851   38 self_->cached_awaitable_); 846   38 self_->cached_awaitable_);
HITCBC 852   38 } 847   38 }
853   }; 848   };
HITCBC 854   42 return awaitable{this, buffers}; 849   42 return awaitable{this, buffers};
855   } 850   }
856   851  
857   template<ConstBufferSequence CB> 852   template<ConstBufferSequence CB>
858   io_task<std::size_t> 853   io_task<std::size_t>
HITCBC 859   68 any_write_sink::write(CB buffers) 854   68 any_write_sink::write(CB buffers)
860   { 855   {
861   buffer_param<CB> bp(buffers); 856   buffer_param<CB> bp(buffers);
862   std::size_t total = 0; 857   std::size_t total = 0;
863   858  
864   for(;;) 859   for(;;)
865   { 860   {
866   auto bufs = bp.data(); 861   auto bufs = bp.data();
867   if(bufs.empty()) 862   if(bufs.empty())
868   break; 863   break;
869   864  
870   auto [ec, n] = co_await write_(bufs); 865   auto [ec, n] = co_await write_(bufs);
871   total += n; 866   total += n;
872   if(ec) 867   if(ec)
873   co_return {ec, total}; 868   co_return {ec, total};
874   bp.consume(n); 869   bp.consume(n);
875   } 870   }
876   871  
877   co_return {{}, total}; 872   co_return {{}, total};
HITCBC 878   136 } 873   136 }
879   874  
880   template<ConstBufferSequence CB> 875   template<ConstBufferSequence CB>
881   io_task<std::size_t> 876   io_task<std::size_t>
HITCBC 882   26 any_write_sink::write_eof(CB buffers) 877   26 any_write_sink::write_eof(CB buffers)
883   { 878   {
884   const_buffer_param<CB> bp(buffers); 879   const_buffer_param<CB> bp(buffers);
885   std::size_t total = 0; 880   std::size_t total = 0;
886   881  
887   for(;;) 882   for(;;)
888   { 883   {
889   auto bufs = bp.data(); 884   auto bufs = bp.data();
890   if(bufs.empty()) 885   if(bufs.empty())
891   { 886   {
892   auto [ec] = co_await write_eof(); 887   auto [ec] = co_await write_eof();
893   co_return {ec, total}; 888   co_return {ec, total};
894   } 889   }
895   890  
896   if(! bp.more()) 891   if(! bp.more())
897   { 892   {
898   // Last window — send atomically with EOF 893   // Last window — send atomically with EOF
899   auto [ec, n] = co_await write_eof_buffers_(bufs); 894   auto [ec, n] = co_await write_eof_buffers_(bufs);
900   total += n; 895   total += n;
901   co_return {ec, total}; 896   co_return {ec, total};
902   } 897   }
903   898  
904   auto [ec, n] = co_await write_(bufs); 899   auto [ec, n] = co_await write_(bufs);
905   total += n; 900   total += n;
906   if(ec) 901   if(ec)
907   co_return {ec, total}; 902   co_return {ec, total};
908   bp.consume(n); 903   bp.consume(n);
909   } 904   }
HITCBC 910   52 } 905   52 }
911   906  
912   } // namespace capy 907   } // namespace capy
913   } // namespace boost 908   } // namespace boost
914   909  
915   #endif 910   #endif