86.63% Lines (149/172) 90.70% Functions (39/43)
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_SOURCE_HPP 10   #ifndef BOOST_CAPY_IO_ANY_BUFFER_SOURCE_HPP
11   #define BOOST_CAPY_IO_ANY_BUFFER_SOURCE_HPP 11   #define BOOST_CAPY_IO_ANY_BUFFER_SOURCE_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/slice.hpp>  
18   #include <boost/capy/buffers/buffer_param.hpp> 17   #include <boost/capy/buffers/buffer_param.hpp>
19   #include <boost/capy/concept/buffer_source.hpp> 18   #include <boost/capy/concept/buffer_source.hpp>
20   #include <boost/capy/concept/io_awaitable.hpp> 19   #include <boost/capy/concept/io_awaitable.hpp>
21   #include <boost/capy/concept/read_source.hpp> 20   #include <boost/capy/concept/read_source.hpp>
22   #include <boost/capy/error.hpp> 21   #include <boost/capy/error.hpp>
23   #include <boost/capy/ex/io_env.hpp> 22   #include <boost/capy/ex/io_env.hpp>
24   #include <boost/capy/io_result.hpp> 23   #include <boost/capy/io_result.hpp>
25   #include <boost/capy/io_task.hpp> 24   #include <boost/capy/io_task.hpp>
26   25  
27   #include <concepts> 26   #include <concepts>
28   #include <coroutine> 27   #include <coroutine>
29   #include <cstddef> 28   #include <cstddef>
30   #include <exception> 29   #include <exception>
31   #include <new> 30   #include <new>
32   #include <span> 31   #include <span>
33   #include <stop_token> 32   #include <stop_token>
34   #include <system_error> 33   #include <system_error>
35   #include <utility> 34   #include <utility>
36   35  
37   namespace boost { 36   namespace boost {
38   namespace capy { 37   namespace capy {
39   38  
40   /** Type-erased wrapper for any BufferSource. 39   /** Type-erased wrapper for any BufferSource.
41   40  
42   This class provides type erasure for any type satisfying the 41   This class provides type erasure for any type satisfying the
43   @ref BufferSource concept, enabling runtime polymorphism for 42   @ref BufferSource concept, enabling runtime polymorphism for
44   buffer pull operations. It uses cached awaitable storage to achieve 43   buffer pull operations. It uses cached awaitable storage to achieve
45   zero steady-state allocation after construction. 44   zero steady-state allocation after construction.
46   45  
47   The wrapper also satisfies @ref ReadSource. When the wrapped type 46   The wrapper also satisfies @ref ReadSource. When the wrapped type
48   satisfies only @ref BufferSource, the read operations are 47   satisfies only @ref BufferSource, the read operations are
49   synthesized using @ref pull and @ref consume with an extra 48   synthesized using @ref pull and @ref consume with an extra
50   buffer copy. When the wrapped type satisfies both @ref BufferSource 49   buffer copy. When the wrapped type satisfies both @ref BufferSource
51   and @ref ReadSource, the native read operations are forwarded 50   and @ref ReadSource, the native read operations are forwarded
52   directly across the virtual boundary, avoiding the copy. 51   directly across the virtual boundary, avoiding the copy.
53   52  
54   The wrapper supports two construction modes: 53   The wrapper supports two construction modes:
55   - **Owning**: Pass by value to transfer ownership. The wrapper 54   - **Owning**: Pass by value to transfer ownership. The wrapper
56   allocates storage and owns the source. 55   allocates storage and owns the source.
57   - **Reference**: Pass a pointer to wrap without ownership. The 56   - **Reference**: Pass a pointer to wrap without ownership. The
58   pointed-to source must outlive this wrapper. 57   pointed-to source must outlive this wrapper.
59   58  
60   Within each mode, the vtable is populated at compile time based 59   Within each mode, the vtable is populated at compile time based
61   on whether the wrapped type also satisfies @ref ReadSource: 60   on whether the wrapped type also satisfies @ref ReadSource:
62   - **BufferSource only**: @ref read_some and @ref read are 61   - **BufferSource only**: @ref read_some and @ref read are
63   synthesized from @ref pull and @ref consume, incurring one 62   synthesized from @ref pull and @ref consume, incurring one
64   buffer copy per operation. 63   buffer copy per operation.
65   - **BufferSource + ReadSource**: All read operations are 64   - **BufferSource + ReadSource**: All read operations are
66   forwarded natively through the type-erased boundary with 65   forwarded natively through the type-erased boundary with
67   no extra copy. 66   no extra copy.
68   67  
69   @par Awaitable Preallocation 68   @par Awaitable Preallocation
70   The constructor preallocates storage for the type-erased awaitable. 69   The constructor preallocates storage for the type-erased awaitable.
71   This reserves all virtual address space at server startup 70   This reserves all virtual address space at server startup
72   so memory usage can be measured up front, rather than 71   so memory usage can be measured up front, rather than
73   allocating piecemeal as traffic arrives. 72   allocating piecemeal as traffic arrives.
74   73  
75   @par Thread Safety 74   @par Thread Safety
76   Not thread-safe. Concurrent operations on the same wrapper 75   Not thread-safe. Concurrent operations on the same wrapper
77   are undefined behavior. 76   are undefined behavior.
78   77  
79   @par Example 78   @par Example
80   @code 79   @code
81   // Owning - takes ownership of the source 80   // Owning - takes ownership of the source
82   any_buffer_source abs(some_buffer_source{args...}); 81   any_buffer_source abs(some_buffer_source{args...});
83   82  
84   // Reference - wraps without ownership 83   // Reference - wraps without ownership
85   some_buffer_source src; 84   some_buffer_source src;
86   any_buffer_source abs(&src); 85   any_buffer_source abs(&src);
87   86  
88   const_buffer arr[16]; 87   const_buffer arr[16];
89   auto [ec, bufs] = co_await abs.pull(arr); 88   auto [ec, bufs] = co_await abs.pull(arr);
90   89  
91   // ReadSource interface also available 90   // ReadSource interface also available
92   char buf[64]; 91   char buf[64];
93   auto [ec2, n] = co_await abs.read_some(mutable_buffer(buf, 64)); 92   auto [ec2, n] = co_await abs.read_some(mutable_buffer(buf, 64));
94   @endcode 93   @endcode
95   94  
96   @see any_buffer_sink, BufferSource, ReadSource 95   @see any_buffer_sink, BufferSource, ReadSource
97   */ 96   */
98   class any_buffer_source 97   class any_buffer_source
99   { 98   {
100   struct vtable; 99   struct vtable;
101   struct awaitable_ops; 100   struct awaitable_ops;
102   struct read_awaitable_ops; 101   struct read_awaitable_ops;
103   102  
104   template<BufferSource S> 103   template<BufferSource S>
105   struct vtable_for_impl; 104   struct vtable_for_impl;
106   105  
107   // hot-path members first for cache locality 106   // hot-path members first for cache locality
108   void* source_ = nullptr; 107   void* source_ = nullptr;
109   vtable const* vt_ = nullptr; 108   vtable const* vt_ = nullptr;
110   void* cached_awaitable_ = nullptr; 109   void* cached_awaitable_ = nullptr;
111   awaitable_ops const* active_ops_ = nullptr; 110   awaitable_ops const* active_ops_ = nullptr;
112   read_awaitable_ops const* active_read_ops_ = nullptr; 111   read_awaitable_ops const* active_read_ops_ = nullptr;
113   void* storage_ = nullptr; 112   void* storage_ = nullptr;
114   113  
115   public: 114   public:
116   /** Destructor. 115   /** Destructor.
117   116  
118   Destroys the owned source (if any) and releases the cached 117   Destroys the owned source (if any) and releases the cached
119   awaitable storage. 118   awaitable storage.
120   */ 119   */
121   ~any_buffer_source(); 120   ~any_buffer_source();
122   121  
123   /** Construct a default instance. 122   /** Construct a default instance.
124   123  
125   Constructs an empty wrapper. Operations on a default-constructed 124   Constructs an empty wrapper. Operations on a default-constructed
126   wrapper result in undefined behavior. 125   wrapper result in undefined behavior.
127   */ 126   */
128   any_buffer_source() = default; 127   any_buffer_source() = default;
129   128  
130   /** Non-copyable. 129   /** Non-copyable.
131   130  
132   The awaitable cache is per-instance and cannot be shared. 131   The awaitable cache is per-instance and cannot be shared.
133   */ 132   */
134   any_buffer_source(any_buffer_source const&) = delete; 133   any_buffer_source(any_buffer_source const&) = delete;
135   any_buffer_source& operator=(any_buffer_source const&) = delete; 134   any_buffer_source& operator=(any_buffer_source const&) = delete;
136   135  
137   /** Construct by moving. 136   /** Construct by moving.
138   137  
139   Transfers ownership of the wrapped source (if owned) and 138   Transfers ownership of the wrapped source (if owned) and
140   cached awaitable storage from `other`. After the move, `other` is 139   cached awaitable storage from `other`. After the move, `other` is
141   in a default-constructed state. 140   in a default-constructed state.
142   141  
143   @param other The wrapper to move from. 142   @param other The wrapper to move from.
144   */ 143   */
HITCBC 145   2 any_buffer_source(any_buffer_source&& other) noexcept 144   2 any_buffer_source(any_buffer_source&& other) noexcept
HITCBC 146   2 : source_(std::exchange(other.source_, nullptr)) 145   2 : source_(std::exchange(other.source_, nullptr))
HITCBC 147   2 , vt_(std::exchange(other.vt_, nullptr)) 146   2 , vt_(std::exchange(other.vt_, nullptr))
HITCBC 148   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr)) 147   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr))
HITCBC 149   2 , active_ops_(std::exchange(other.active_ops_, nullptr)) 148   2 , active_ops_(std::exchange(other.active_ops_, nullptr))
HITCBC 150   2 , active_read_ops_(std::exchange(other.active_read_ops_, nullptr)) 149   2 , active_read_ops_(std::exchange(other.active_read_ops_, nullptr))
HITCBC 151   2 , storage_(std::exchange(other.storage_, nullptr)) 150   2 , storage_(std::exchange(other.storage_, nullptr))
152   { 151   {
HITCBC 153   2 } 152   2 }
154   153  
155   /** Assign by moving. 154   /** Assign by moving.
156   155  
157   Destroys any owned source and releases existing resources, 156   Destroys any owned source and releases existing resources,
158   then transfers ownership from `other`. 157   then transfers ownership from `other`.
159   158  
160   @param other The wrapper to move from. 159   @param other The wrapper to move from.
161   @return Reference to this wrapper. 160   @return Reference to this wrapper.
162   */ 161   */
163   any_buffer_source& 162   any_buffer_source&
164   operator=(any_buffer_source&& other) noexcept; 163   operator=(any_buffer_source&& other) noexcept;
165   164  
166   /** Construct by taking ownership of a BufferSource. 165   /** Construct by taking ownership of a BufferSource.
167   166  
168   Allocates storage and moves the source into this wrapper. 167   Allocates storage and moves the source into this wrapper.
169   The wrapper owns the source and will destroy it. If `S` also 168   The wrapper owns the source and will destroy it. If `S` also
170   satisfies @ref ReadSource, native read operations are 169   satisfies @ref ReadSource, native read operations are
171   forwarded through the virtual boundary. 170   forwarded through the virtual boundary.
172   171  
173   @param s The source to take ownership of. 172   @param s The source to take ownership of.
174   */ 173   */
175   template<BufferSource S> 174   template<BufferSource S>
176   requires (!std::same_as<std::decay_t<S>, any_buffer_source>) 175   requires (!std::same_as<std::decay_t<S>, any_buffer_source>)
177   any_buffer_source(S s); 176   any_buffer_source(S s);
178   177  
179   /** Construct by wrapping a BufferSource without ownership. 178   /** Construct by wrapping a BufferSource without ownership.
180   179  
181   Wraps the given source by pointer. The source must remain 180   Wraps the given source by pointer. The source must remain
182   valid for the lifetime of this wrapper. If `S` also 181   valid for the lifetime of this wrapper. If `S` also
183   satisfies @ref ReadSource, native read operations are 182   satisfies @ref ReadSource, native read operations are
184   forwarded through the virtual boundary. 183   forwarded through the virtual boundary.
185   184  
186   @param s Pointer to the source to wrap. 185   @param s Pointer to the source to wrap.
187   */ 186   */
188   template<BufferSource S> 187   template<BufferSource S>
189   any_buffer_source(S* s); 188   any_buffer_source(S* s);
190   189  
191   /** Check if the wrapper contains a valid source. 190   /** Check if the wrapper contains a valid source.
192   191  
193   @return `true` if wrapping a source, `false` if default-constructed 192   @return `true` if wrapping a source, `false` if default-constructed
194   or moved-from. 193   or moved-from.
195   */ 194   */
196   bool 195   bool
HITCBC 197   16 has_value() const noexcept 196   16 has_value() const noexcept
198   { 197   {
HITCBC 199   16 return source_ != nullptr; 198   16 return source_ != nullptr;
200   } 199   }
201   200  
202   /** Check if the wrapper contains a valid source. 201   /** Check if the wrapper contains a valid source.
203   202  
204   @return `true` if wrapping a source, `false` if default-constructed 203   @return `true` if wrapping a source, `false` if default-constructed
205   or moved-from. 204   or moved-from.
206   */ 205   */
207   explicit 206   explicit
HITCBC 208   2 operator bool() const noexcept 207   2 operator bool() const noexcept
209   { 208   {
HITCBC 210   2 return has_value(); 209   2 return has_value();
211   } 210   }
212   211  
213   /** Consume bytes from the source. 212   /** Consume bytes from the source.
214   213  
215   Advances the internal read position of the underlying source 214   Advances the internal read position of the underlying source
216   by the specified number of bytes. The next call to @ref pull 215   by the specified number of bytes. The next call to @ref pull
217   returns data starting after the consumed bytes. 216   returns data starting after the consumed bytes.
218   217  
219   @param n The number of bytes to consume. Must not exceed the 218   @param n The number of bytes to consume. Must not exceed the
220   total size of buffers returned by the previous @ref pull. 219   total size of buffers returned by the previous @ref pull.
221   220  
222   @par Preconditions 221   @par Preconditions
223   The wrapper must contain a valid source (`has_value() == true`). 222   The wrapper must contain a valid source (`has_value() == true`).
224   */ 223   */
225   void 224   void
226   consume(std::size_t n) noexcept; 225   consume(std::size_t n) noexcept;
227   226  
228   /** Pull buffer data from the source. 227   /** Pull buffer data from the source.
229   228  
230   Fills the provided span with buffer descriptors from the 229   Fills the provided span with buffer descriptors from the
231   underlying source. The operation completes when data is 230   underlying source. The operation completes when data is
232   available, the source is exhausted, or an error occurs. 231   available, the source is exhausted, or an error occurs.
233   232  
234   @param dest Span of const_buffer to fill. 233   @param dest Span of const_buffer to fill.
235   234  
236   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`. 235   @return An awaitable that await-returns `(error_code,std::span<const_buffer>)`.
237   On success with data, a non-empty span of filled buffers. 236   On success with data, a non-empty span of filled buffers.
238   On EOF, `ec == cond::eof` and span is empty. 237   On EOF, `ec == cond::eof` and span is empty.
239   238  
240   @par Preconditions 239   @par Preconditions
241   The wrapper must contain a valid source (`has_value() == true`). 240   The wrapper must contain a valid source (`has_value() == true`).
242   The caller must not call this function again after a prior 241   The caller must not call this function again after a prior
243   call returned an error. 242   call returned an error.
244   */ 243   */
245   auto 244   auto
246   pull(std::span<const_buffer> dest); 245   pull(std::span<const_buffer> dest);
247   246  
248   /** Read some data into a mutable buffer sequence. 247   /** Read some data into a mutable buffer sequence.
249   248  
250   Attempt to read up to `buffer_size( buffers )` bytes into 249   Attempt to read up to `buffer_size( buffers )` bytes into
251   the caller's buffers. May fill less than the full sequence. 250   the caller's buffers. May fill less than the full sequence.
252   251  
253   When the wrapped type provides native @ref ReadSource support, 252   When the wrapped type provides native @ref ReadSource support,
254   the operation forwards directly. Otherwise it is synthesized 253   the operation forwards directly. Otherwise it is synthesized
255   from @ref pull, @ref buffer_copy, and @ref consume. 254   from @ref pull, @ref buffer_copy, and @ref consume.
256   255  
257   @param buffers The buffer sequence to fill. 256   @param buffers The buffer sequence to fill.
258   257  
259   @return An awaitable that await-returns `(error_code,std::size_t)`. 258   @return An awaitable that await-returns `(error_code,std::size_t)`.
260   259  
261   @par Preconditions 260   @par Preconditions
262   The wrapper must contain a valid source (`has_value() == true`). 261   The wrapper must contain a valid source (`has_value() == true`).
263   The caller must not call this function again after a prior 262   The caller must not call this function again after a prior
264   call returned an error (including EOF). 263   call returned an error (including EOF).
265   264  
266   @see pull, consume 265   @see pull, consume
267   */ 266   */
268   template<MutableBufferSequence MB> 267   template<MutableBufferSequence MB>
269   io_task<std::size_t> 268   io_task<std::size_t>
270   read_some(MB buffers); 269   read_some(MB buffers);
271   270  
272   /** Read data into a mutable buffer sequence. 271   /** Read data into a mutable buffer sequence.
273   272  
274   Fills the provided buffer sequence completely. When the 273   Fills the provided buffer sequence completely. When the
275   wrapped type provides native @ref ReadSource support, each 274   wrapped type provides native @ref ReadSource support, each
276   window is forwarded directly. Otherwise the data is 275   window is forwarded directly. Otherwise the data is
277   synthesized from @ref pull, @ref buffer_copy, and @ref consume. 276   synthesized from @ref pull, @ref buffer_copy, and @ref consume.
278   277  
279   @param buffers The buffer sequence to fill. 278   @param buffers The buffer sequence to fill.
280   279  
281   @return An awaitable that await-returns `(error_code,std::size_t)`. 280   @return An awaitable that await-returns `(error_code,std::size_t)`.
282   On success, `n == buffer_size(buffers)`. 281   On success, `n == buffer_size(buffers)`.
283   On EOF, `ec == error::eof` and `n` is bytes transferred. 282   On EOF, `ec == error::eof` and `n` is bytes transferred.
284   283  
285   @par Preconditions 284   @par Preconditions
286   The wrapper must contain a valid source (`has_value() == true`). 285   The wrapper must contain a valid source (`has_value() == true`).
287   The caller must not call this function again after a prior 286   The caller must not call this function again after a prior
288   call returned an error (including EOF). 287   call returned an error (including EOF).
289   288  
290   @see pull, consume 289   @see pull, consume
291   */ 290   */
292   template<MutableBufferSequence MB> 291   template<MutableBufferSequence MB>
293   io_task<std::size_t> 292   io_task<std::size_t>
294   read(MB buffers); 293   read(MB buffers);
295   294  
296   protected: 295   protected:
297   /** Rebind to a new source after move. 296   /** Rebind to a new source after move.
298   297  
299   Updates the internal pointer to reference a new source object. 298   Updates the internal pointer to reference a new source object.
300   Used by owning wrappers after move assignment when the owned 299   Used by owning wrappers after move assignment when the owned
301   object has moved to a new location. 300   object has moved to a new location.
302   301  
303   @param new_source The new source to bind to. Must be the same 302   @param new_source The new source to bind to. Must be the same
304   type as the original source. 303   type as the original source.
305   304  
306   @note Terminates if called with a source of different type 305   @note Terminates if called with a source of different type
307   than the original. 306   than the original.
308   */ 307   */
309   template<BufferSource S> 308   template<BufferSource S>
310   void 309   void
311   rebind(S& new_source) noexcept 310   rebind(S& new_source) noexcept
312   { 311   {
313   if(vt_ != &vtable_for_impl<S>::value) 312   if(vt_ != &vtable_for_impl<S>::value)
314   std::terminate(); 313   std::terminate();
315   source_ = &new_source; 314   source_ = &new_source;
316   } 315   }
317   316  
318   private: 317   private:
319   /** Forward a partial read through the vtable. 318   /** Forward a partial read through the vtable.
320   319  
321   Constructs the underlying `read_some` awaitable in 320   Constructs the underlying `read_some` awaitable in
322   cached storage and returns a type-erased awaitable. 321   cached storage and returns a type-erased awaitable.
323   */ 322   */
324   auto 323   auto
325   read_some_(std::span<mutable_buffer const> buffers); 324   read_some_(std::span<mutable_buffer const> buffers);
326   325  
327   /** Forward a complete read through the vtable. 326   /** Forward a complete read through the vtable.
328   327  
329   Constructs the underlying `read` awaitable in 328   Constructs the underlying `read` awaitable in
330   cached storage and returns a type-erased awaitable. 329   cached storage and returns a type-erased awaitable.
331   */ 330   */
332   auto 331   auto
333   read_(std::span<mutable_buffer const> buffers); 332   read_(std::span<mutable_buffer const> buffers);
334   }; 333   };
335   334  
336   /** Type-erased ops for awaitables that await-return `io_result<std::span<const_buffer>>`. */ 335   /** Type-erased ops for awaitables that await-return `io_result<std::span<const_buffer>>`. */
337   struct any_buffer_source::awaitable_ops 336   struct any_buffer_source::awaitable_ops
338   { 337   {
339   bool (*await_ready)(void*); 338   bool (*await_ready)(void*);
340   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 339   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
341   io_result<std::span<const_buffer>> (*await_resume)(void*); 340   io_result<std::span<const_buffer>> (*await_resume)(void*);
342   void (*destroy)(void*) noexcept; 341   void (*destroy)(void*) noexcept;
343   }; 342   };
344   343  
345   /** Type-erased ops for awaitables that await-return `io_result<std::size_t>`. */ 344   /** Type-erased ops for awaitables that await-return `io_result<std::size_t>`. */
346   struct any_buffer_source::read_awaitable_ops 345   struct any_buffer_source::read_awaitable_ops
347   { 346   {
348   bool (*await_ready)(void*); 347   bool (*await_ready)(void*);
349   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 348   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
350   io_result<std::size_t> (*await_resume)(void*); 349   io_result<std::size_t> (*await_resume)(void*);
351   void (*destroy)(void*) noexcept; 350   void (*destroy)(void*) noexcept;
352   }; 351   };
353   352  
354   struct any_buffer_source::vtable 353   struct any_buffer_source::vtable
355   { 354   {
356   // BufferSource ops (always populated) 355   // BufferSource ops (always populated)
357   void (*destroy)(void*) noexcept; 356   void (*destroy)(void*) noexcept;
358   void (*do_consume)(void* source, std::size_t n) noexcept; 357   void (*do_consume)(void* source, std::size_t n) noexcept;
359   std::size_t awaitable_size; 358   std::size_t awaitable_size;
360   std::size_t awaitable_align; 359   std::size_t awaitable_align;
361   awaitable_ops const* (*construct_awaitable)( 360   awaitable_ops const* (*construct_awaitable)(
362   void* source, 361   void* source,
363   void* storage, 362   void* storage,
364   std::span<const_buffer> dest); 363   std::span<const_buffer> dest);
365   364  
366   // ReadSource forwarding (null when wrapped type is BufferSource-only) 365   // ReadSource forwarding (null when wrapped type is BufferSource-only)
367   read_awaitable_ops const* (*construct_read_some_awaitable)( 366   read_awaitable_ops const* (*construct_read_some_awaitable)(
368   void* source, 367   void* source,
369   void* storage, 368   void* storage,
370   std::span<mutable_buffer const> buffers); 369   std::span<mutable_buffer const> buffers);
371   read_awaitable_ops const* (*construct_read_awaitable)( 370   read_awaitable_ops const* (*construct_read_awaitable)(
372   void* source, 371   void* source,
373   void* storage, 372   void* storage,
374   std::span<mutable_buffer const> buffers); 373   std::span<mutable_buffer const> buffers);
375   }; 374   };
376   375  
377   template<BufferSource S> 376   template<BufferSource S>
378   struct any_buffer_source::vtable_for_impl 377   struct any_buffer_source::vtable_for_impl
379   { 378   {
380   using PullAwaitable = decltype(std::declval<S&>().pull( 379   using PullAwaitable = decltype(std::declval<S&>().pull(
381   std::declval<std::span<const_buffer>>())); 380   std::declval<std::span<const_buffer>>()));
382   381  
383   static void 382   static void
HITCBC 384   7 do_destroy_impl(void* source) noexcept 383   7 do_destroy_impl(void* source) noexcept
385   { 384   {
HITCBC 386   7 static_cast<S*>(source)->~S(); 385   7 static_cast<S*>(source)->~S();
HITCBC 387   7 } 386   7 }
388   387  
389   static void 388   static void
HITCBC 390   45 do_consume_impl(void* source, std::size_t n) noexcept 389   45 do_consume_impl(void* source, std::size_t n) noexcept
391   { 390   {
HITCBC 392   45 static_cast<S*>(source)->consume(n); 391   45 static_cast<S*>(source)->consume(n);
HITCBC 393   45 } 392   45 }
394   393  
395   static awaitable_ops const* 394   static awaitable_ops const*
HITCBC 396   110 construct_awaitable_impl( 395   110 construct_awaitable_impl(
397   void* source, 396   void* source,
398   void* storage, 397   void* storage,
399   std::span<const_buffer> dest) 398   std::span<const_buffer> dest)
400   { 399   {
HITCBC 401   110 auto& s = *static_cast<S*>(source); 400   110 auto& s = *static_cast<S*>(source);
HITCBC 402   110 ::new(storage) PullAwaitable(s.pull(dest)); 401   110 ::new(storage) PullAwaitable(s.pull(dest));
403   402  
404   static constexpr awaitable_ops ops = { 403   static constexpr awaitable_ops ops = {
HITCBC 405   110 +[](void* p) { 404   110 +[](void* p) {
HITCBC 406   110 return static_cast<PullAwaitable*>(p)->await_ready(); 405   110 return static_cast<PullAwaitable*>(p)->await_ready();
407   }, 406   },
MISUBC 408   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 407   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 409   return detail::call_await_suspend( 408   return detail::call_await_suspend(
MISUBC 410   static_cast<PullAwaitable*>(p), h, env); 409   static_cast<PullAwaitable*>(p), h, env);
411   }, 410   },
HITCBC 412   110 +[](void* p) { 411   110 +[](void* p) {
HITCBC 413   110 return static_cast<PullAwaitable*>(p)->await_resume(); 412   110 return static_cast<PullAwaitable*>(p)->await_resume();
414   }, 413   },
HITCBC 415   110 +[](void* p) noexcept { 414   110 +[](void* p) noexcept {
HITCBC 416   110 static_cast<PullAwaitable*>(p)->~PullAwaitable(); 415   110 static_cast<PullAwaitable*>(p)->~PullAwaitable();
417   } 416   }
418   }; 417   };
HITCBC 419   110 return &ops; 418   110 return &ops;
420   } 419   }
421   420  
422   static read_awaitable_ops const* 421   static read_awaitable_ops const*
HITCBC 423   48 construct_read_some_awaitable_impl( 422   48 construct_read_some_awaitable_impl(
424   void* source, 423   void* source,
425   void* storage, 424   void* storage,
426   std::span<mutable_buffer const> buffers) 425   std::span<mutable_buffer const> buffers)
427   requires ReadSource<S> 426   requires ReadSource<S>
428   { 427   {
429   using Aw = decltype(std::declval<S&>().read_some( 428   using Aw = decltype(std::declval<S&>().read_some(
430   std::span<mutable_buffer const>{})); 429   std::span<mutable_buffer const>{}));
HITCBC 431   48 auto& s = *static_cast<S*>(source); 430   48 auto& s = *static_cast<S*>(source);
HITCBC 432   48 ::new(storage) Aw(s.read_some(buffers)); 431   48 ::new(storage) Aw(s.read_some(buffers));
433   432  
434   static constexpr read_awaitable_ops ops = { 433   static constexpr read_awaitable_ops ops = {
HITCBC 435   48 +[](void* p) { 434   48 +[](void* p) {
HITCBC 436   48 return static_cast<Aw*>(p)->await_ready(); 435   48 return static_cast<Aw*>(p)->await_ready();
437   }, 436   },
MISUBC 438   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 437   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 439   return detail::call_await_suspend( 438   return detail::call_await_suspend(
MISUBC 440   static_cast<Aw*>(p), h, env); 439   static_cast<Aw*>(p), h, env);
441   }, 440   },
HITCBC 442   48 +[](void* p) { 441   48 +[](void* p) {
HITCBC 443   48 return static_cast<Aw*>(p)->await_resume(); 442   48 return static_cast<Aw*>(p)->await_resume();
444   }, 443   },
HITCBC 445   48 +[](void* p) noexcept { 444   48 +[](void* p) noexcept {
HITCBC 446   48 static_cast<Aw*>(p)->~Aw(); 445   48 static_cast<Aw*>(p)->~Aw();
447   } 446   }
448   }; 447   };
HITCBC 449   48 return &ops; 448   48 return &ops;
450   } 449   }
451   450  
452   static read_awaitable_ops const* 451   static read_awaitable_ops const*
HITCBC 453   18 construct_read_awaitable_impl( 452   18 construct_read_awaitable_impl(
454   void* source, 453   void* source,
455   void* storage, 454   void* storage,
456   std::span<mutable_buffer const> buffers) 455   std::span<mutable_buffer const> buffers)
457   requires ReadSource<S> 456   requires ReadSource<S>
458   { 457   {
459   using Aw = decltype(std::declval<S&>().read( 458   using Aw = decltype(std::declval<S&>().read(
460   std::span<mutable_buffer const>{})); 459   std::span<mutable_buffer const>{}));
HITCBC 461   18 auto& s = *static_cast<S*>(source); 460   18 auto& s = *static_cast<S*>(source);
HITCBC 462   18 ::new(storage) Aw(s.read(buffers)); 461   18 ::new(storage) Aw(s.read(buffers));
463   462  
464   static constexpr read_awaitable_ops ops = { 463   static constexpr read_awaitable_ops ops = {
HITCBC 465   18 +[](void* p) { 464   18 +[](void* p) {
HITCBC 466   18 return static_cast<Aw*>(p)->await_ready(); 465   18 return static_cast<Aw*>(p)->await_ready();
467   }, 466   },
MISUBC 468   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 467   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 469   return detail::call_await_suspend( 468   return detail::call_await_suspend(
MISUBC 470   static_cast<Aw*>(p), h, env); 469   static_cast<Aw*>(p), h, env);
471   }, 470   },
HITCBC 472   18 +[](void* p) { 471   18 +[](void* p) {
HITCBC 473   18 return static_cast<Aw*>(p)->await_resume(); 472   18 return static_cast<Aw*>(p)->await_resume();
474   }, 473   },
HITCBC 475   18 +[](void* p) noexcept { 474   18 +[](void* p) noexcept {
HITCBC 476   18 static_cast<Aw*>(p)->~Aw(); 475   18 static_cast<Aw*>(p)->~Aw();
477   } 476   }
478   }; 477   };
HITCBC 479   18 return &ops; 478   18 return &ops;
480   } 479   }
481   480  
482   static consteval std::size_t 481   static consteval std::size_t
483   compute_max_size() noexcept 482   compute_max_size() noexcept
484   { 483   {
485   std::size_t s = sizeof(PullAwaitable); 484   std::size_t s = sizeof(PullAwaitable);
486   if constexpr (ReadSource<S>) 485   if constexpr (ReadSource<S>)
487   { 486   {
488   using RS = decltype(std::declval<S&>().read_some( 487   using RS = decltype(std::declval<S&>().read_some(
489   std::span<mutable_buffer const>{})); 488   std::span<mutable_buffer const>{}));
490   using R = decltype(std::declval<S&>().read( 489   using R = decltype(std::declval<S&>().read(
491   std::span<mutable_buffer const>{})); 490   std::span<mutable_buffer const>{}));
492   491  
493   if(sizeof(RS) > s) s = sizeof(RS); 492   if(sizeof(RS) > s) s = sizeof(RS);
494   if(sizeof(R) > s) s = sizeof(R); 493   if(sizeof(R) > s) s = sizeof(R);
495   } 494   }
496   return s; 495   return s;
497   } 496   }
498   497  
499   static consteval std::size_t 498   static consteval std::size_t
500   compute_max_align() noexcept 499   compute_max_align() noexcept
501   { 500   {
502   std::size_t a = alignof(PullAwaitable); 501   std::size_t a = alignof(PullAwaitable);
503   if constexpr (ReadSource<S>) 502   if constexpr (ReadSource<S>)
504   { 503   {
505   using RS = decltype(std::declval<S&>().read_some( 504   using RS = decltype(std::declval<S&>().read_some(
506   std::span<mutable_buffer const>{})); 505   std::span<mutable_buffer const>{}));
507   using R = decltype(std::declval<S&>().read( 506   using R = decltype(std::declval<S&>().read(
508   std::span<mutable_buffer const>{})); 507   std::span<mutable_buffer const>{}));
509   508  
510   if(alignof(RS) > a) a = alignof(RS); 509   if(alignof(RS) > a) a = alignof(RS);
511   if(alignof(R) > a) a = alignof(R); 510   if(alignof(R) > a) a = alignof(R);
512   } 511   }
513   return a; 512   return a;
514   } 513   }
515   514  
516   static consteval vtable 515   static consteval vtable
517   make_vtable() noexcept 516   make_vtable() noexcept
518   { 517   {
519   vtable v{}; 518   vtable v{};
520   v.destroy = &do_destroy_impl; 519   v.destroy = &do_destroy_impl;
521   v.do_consume = &do_consume_impl; 520   v.do_consume = &do_consume_impl;
522   v.awaitable_size = compute_max_size(); 521   v.awaitable_size = compute_max_size();
523   v.awaitable_align = compute_max_align(); 522   v.awaitable_align = compute_max_align();
524   v.construct_awaitable = &construct_awaitable_impl; 523   v.construct_awaitable = &construct_awaitable_impl;
525   v.construct_read_some_awaitable = nullptr; 524   v.construct_read_some_awaitable = nullptr;
526   v.construct_read_awaitable = nullptr; 525   v.construct_read_awaitable = nullptr;
527   526  
528   if constexpr (ReadSource<S>) 527   if constexpr (ReadSource<S>)
529   { 528   {
530   v.construct_read_some_awaitable = 529   v.construct_read_some_awaitable =
531   &construct_read_some_awaitable_impl; 530   &construct_read_some_awaitable_impl;
532   v.construct_read_awaitable = 531   v.construct_read_awaitable =
533   &construct_read_awaitable_impl; 532   &construct_read_awaitable_impl;
534   } 533   }
535   return v; 534   return v;
536   } 535   }
537   536  
538   static constexpr vtable value = make_vtable(); 537   static constexpr vtable value = make_vtable();
539   }; 538   };
540   539  
541   inline 540   inline
HITCBC 542   124 any_buffer_source::~any_buffer_source() 541   124 any_buffer_source::~any_buffer_source()
543   { 542   {
HITCBC 544   124 if(storage_) 543   124 if(storage_)
545   { 544   {
HITCBC 546   7 vt_->destroy(source_); 545   7 vt_->destroy(source_);
HITCBC 547   7 ::operator delete(storage_); 546   7 ::operator delete(storage_);
548   } 547   }
HITCBC 549   124 if(cached_awaitable_) 548   124 if(cached_awaitable_)
HITCBC 550   119 ::operator delete(cached_awaitable_); 549   119 ::operator delete(cached_awaitable_);
HITCBC 551   124 } 550   124 }
552   551  
553   inline any_buffer_source& 552   inline any_buffer_source&
HITCBC 554   2 any_buffer_source::operator=(any_buffer_source&& other) noexcept 553   2 any_buffer_source::operator=(any_buffer_source&& other) noexcept
555   { 554   {
HITCBC 556   2 if(this != &other) 555   2 if(this != &other)
557   { 556   {
HITCBC 558   2 if(storage_) 557   2 if(storage_)
559   { 558   {
MISUBC 560   vt_->destroy(source_); 559   vt_->destroy(source_);
MISUBC 561   ::operator delete(storage_); 560   ::operator delete(storage_);
562   } 561   }
HITCBC 563   2 if(cached_awaitable_) 562   2 if(cached_awaitable_)
MISUBC 564   ::operator delete(cached_awaitable_); 563   ::operator delete(cached_awaitable_);
HITCBC 565   2 source_ = std::exchange(other.source_, nullptr); 564   2 source_ = std::exchange(other.source_, nullptr);
HITCBC 566   2 vt_ = std::exchange(other.vt_, nullptr); 565   2 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 567   2 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 566   2 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 568   2 storage_ = std::exchange(other.storage_, nullptr); 567   2 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 569   2 active_ops_ = std::exchange(other.active_ops_, nullptr); 568   2 active_ops_ = std::exchange(other.active_ops_, nullptr);
HITCBC 570   2 active_read_ops_ = std::exchange(other.active_read_ops_, nullptr); 569   2 active_read_ops_ = std::exchange(other.active_read_ops_, nullptr);
571   } 570   }
HITCBC 572   2 return *this; 571   2 return *this;
573   } 572   }
574   573  
575   template<BufferSource S> 574   template<BufferSource S>
576   requires (!std::same_as<std::decay_t<S>, any_buffer_source>) 575   requires (!std::same_as<std::decay_t<S>, any_buffer_source>)
HITCBC 577   7 any_buffer_source::any_buffer_source(S s) 576   7 any_buffer_source::any_buffer_source(S s)
HITCBC 578   7 : vt_(&vtable_for_impl<S>::value) 577   7 : vt_(&vtable_for_impl<S>::value)
579   { 578   {
580   struct guard { 579   struct guard {
581   any_buffer_source* self; 580   any_buffer_source* self;
582   bool committed = false; 581   bool committed = false;
HITCBC 583   7 ~guard() { 582   7 ~guard() {
HITCBC 584   7 if(!committed && self->storage_) { 583   7 if(!committed && self->storage_) {
MISUBC 585   self->vt_->destroy(self->source_); 584   self->vt_->destroy(self->source_);
MISUBC 586   ::operator delete(self->storage_); 585   ::operator delete(self->storage_);
MISUBC 587   self->storage_ = nullptr; 586   self->storage_ = nullptr;
MISUBC 588   self->source_ = nullptr; 587   self->source_ = nullptr;
589   } 588   }
HITCBC 590   7 } 589   7 }
HITCBC 591   7 } g{this}; 590   7 } g{this};
592   591  
HITCBC 593   7 storage_ = ::operator new(sizeof(S)); 592   7 storage_ = ::operator new(sizeof(S));
HITCBC 594   7 source_ = ::new(storage_) S(std::move(s)); 593   7 source_ = ::new(storage_) S(std::move(s));
595   594  
HITCBC 596   7 cached_awaitable_ = ::operator new(vt_->awaitable_size); 595   7 cached_awaitable_ = ::operator new(vt_->awaitable_size);
597   596  
HITCBC 598   7 g.committed = true; 597   7 g.committed = true;
HITCBC 599   7 } 598   7 }
600   599  
601   template<BufferSource S> 600   template<BufferSource S>
HITCBC 602   112 any_buffer_source::any_buffer_source(S* s) 601   112 any_buffer_source::any_buffer_source(S* s)
HITCBC 603   112 : source_(s) 602   112 : source_(s)
HITCBC 604   112 , vt_(&vtable_for_impl<S>::value) 603   112 , vt_(&vtable_for_impl<S>::value)
605   { 604   {
HITCBC 606   112 cached_awaitable_ = ::operator new(vt_->awaitable_size); 605   112 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 607   112 } 606   112 }
608   607  
609   inline void 608   inline void
HITCBC 610   45 any_buffer_source::consume(std::size_t n) noexcept 609   45 any_buffer_source::consume(std::size_t n) noexcept
611   { 610   {
HITCBC 612   45 vt_->do_consume(source_, n); 611   45 vt_->do_consume(source_, n);
HITCBC 613   45 } 612   45 }
614   613  
615   inline auto 614   inline auto
HITCBC 616   110 any_buffer_source::pull(std::span<const_buffer> dest) 615   110 any_buffer_source::pull(std::span<const_buffer> dest)
617   { 616   {
618   struct awaitable 617   struct awaitable
619   { 618   {
620   any_buffer_source* self_; 619   any_buffer_source* self_;
621   std::span<const_buffer> dest_; 620   std::span<const_buffer> dest_;
622   621  
623   bool 622   bool
HITCBC 624   110 await_ready() 623   110 await_ready()
625   { 624   {
HITCBC 626   220 self_->active_ops_ = self_->vt_->construct_awaitable( 625   220 self_->active_ops_ = self_->vt_->construct_awaitable(
HITCBC 627   110 self_->source_, 626   110 self_->source_,
HITCBC 628   110 self_->cached_awaitable_, 627   110 self_->cached_awaitable_,
629   dest_); 628   dest_);
HITCBC 630   110 return self_->active_ops_->await_ready(self_->cached_awaitable_); 629   110 return self_->active_ops_->await_ready(self_->cached_awaitable_);
631   } 630   }
632   631  
633   std::coroutine_handle<> 632   std::coroutine_handle<>
MISUBC 634   await_suspend(std::coroutine_handle<> h, io_env const* env) 633   await_suspend(std::coroutine_handle<> h, io_env const* env)
635   { 634   {
MISUBC 636   return self_->active_ops_->await_suspend( 635   return self_->active_ops_->await_suspend(
MISUBC 637   self_->cached_awaitable_, h, env); 636   self_->cached_awaitable_, h, env);
638   } 637   }
639   638  
640   io_result<std::span<const_buffer>> 639   io_result<std::span<const_buffer>>
HITCBC 641   110 await_resume() 640   110 await_resume()
642   { 641   {
643   struct guard { 642   struct guard {
644   any_buffer_source* self; 643   any_buffer_source* self;
HITCBC 645   110 ~guard() { 644   110 ~guard() {
HITCBC 646   110 self->active_ops_->destroy(self->cached_awaitable_); 645   110 self->active_ops_->destroy(self->cached_awaitable_);
HITCBC 647   110 self->active_ops_ = nullptr; 646   110 self->active_ops_ = nullptr;
HITCBC 648   110 } 647   110 }
HITCBC 649   110 } g{self_}; 648   110 } g{self_};
HITCBC 650   110 return self_->active_ops_->await_resume( 649   110 return self_->active_ops_->await_resume(
HITCBC 651   195 self_->cached_awaitable_); 650   195 self_->cached_awaitable_);
HITCBC 652   110 } 651   110 }
653   }; 652   };
HITCBC 654   110 return awaitable{this, dest}; 653   110 return awaitable{this, dest};
655   } 654   }
656   655  
657   inline auto 656   inline auto
HITCBC 658   48 any_buffer_source::read_some_( 657   48 any_buffer_source::read_some_(
659   std::span<mutable_buffer const> buffers) 658   std::span<mutable_buffer const> buffers)
660   { 659   {
661   struct awaitable 660   struct awaitable
662   { 661   {
663   any_buffer_source* self_; 662   any_buffer_source* self_;
664   std::span<mutable_buffer const> buffers_; 663   std::span<mutable_buffer const> buffers_;
665   664  
666   bool 665   bool
HITCBC 667   48 await_ready() const noexcept 666   48 await_ready() const noexcept
668   { 667   {
HITCBC 669   48 return false; 668   48 return false;
670   } 669   }
671   670  
672   std::coroutine_handle<> 671   std::coroutine_handle<>
HITCBC 673   48 await_suspend(std::coroutine_handle<> h, io_env const* env) 672   48 await_suspend(std::coroutine_handle<> h, io_env const* env)
674   { 673   {
HITCBC 675   96 self_->active_read_ops_ = 674   96 self_->active_read_ops_ =
HITCBC 676   96 self_->vt_->construct_read_some_awaitable( 675   96 self_->vt_->construct_read_some_awaitable(
HITCBC 677   48 self_->source_, 676   48 self_->source_,
HITCBC 678   48 self_->cached_awaitable_, 677   48 self_->cached_awaitable_,
679   buffers_); 678   buffers_);
680   679  
HITCBC 681   48 if(self_->active_read_ops_->await_ready( 680   48 if(self_->active_read_ops_->await_ready(
HITCBC 682   48 self_->cached_awaitable_)) 681   48 self_->cached_awaitable_))
HITCBC 683   48 return h; 682   48 return h;
684   683  
MISUBC 685   return self_->active_read_ops_->await_suspend( 684   return self_->active_read_ops_->await_suspend(
MISUBC 686   self_->cached_awaitable_, h, env); 685   self_->cached_awaitable_, h, env);
687   } 686   }
688   687  
689   io_result<std::size_t> 688   io_result<std::size_t>
HITCBC 690   48 await_resume() 689   48 await_resume()
691   { 690   {
692   struct guard { 691   struct guard {
693   any_buffer_source* self; 692   any_buffer_source* self;
HITCBC 694   48 ~guard() { 693   48 ~guard() {
HITCBC 695   48 self->active_read_ops_->destroy( 694   48 self->active_read_ops_->destroy(
HITCBC 696   48 self->cached_awaitable_); 695   48 self->cached_awaitable_);
HITCBC 697   48 self->active_read_ops_ = nullptr; 696   48 self->active_read_ops_ = nullptr;
HITCBC 698   48 } 697   48 }
HITCBC 699   48 } g{self_}; 698   48 } g{self_};
HITCBC 700   48 return self_->active_read_ops_->await_resume( 699   48 return self_->active_read_ops_->await_resume(
HITCBC 701   88 self_->cached_awaitable_); 700   88 self_->cached_awaitable_);
HITCBC 702   48 } 701   48 }
703   }; 702   };
HITCBC 704   48 return awaitable{this, buffers}; 703   48 return awaitable{this, buffers};
705   } 704   }
706   705  
707   inline auto 706   inline auto
HITCBC 708   18 any_buffer_source::read_( 707   18 any_buffer_source::read_(
709   std::span<mutable_buffer const> buffers) 708   std::span<mutable_buffer const> buffers)
710   { 709   {
711   struct awaitable 710   struct awaitable
712   { 711   {
713   any_buffer_source* self_; 712   any_buffer_source* self_;
714   std::span<mutable_buffer const> buffers_; 713   std::span<mutable_buffer const> buffers_;
715   714  
716   bool 715   bool
HITCBC 717   18 await_ready() const noexcept 716   18 await_ready() const noexcept
718   { 717   {
HITCBC 719   18 return false; 718   18 return false;
720   } 719   }
721   720  
722   std::coroutine_handle<> 721   std::coroutine_handle<>
HITCBC 723   18 await_suspend(std::coroutine_handle<> h, io_env const* env) 722   18 await_suspend(std::coroutine_handle<> h, io_env const* env)
724   { 723   {
HITCBC 725   36 self_->active_read_ops_ = 724   36 self_->active_read_ops_ =
HITCBC 726   36 self_->vt_->construct_read_awaitable( 725   36 self_->vt_->construct_read_awaitable(
HITCBC 727   18 self_->source_, 726   18 self_->source_,
HITCBC 728   18 self_->cached_awaitable_, 727   18 self_->cached_awaitable_,
729   buffers_); 728   buffers_);
730   729  
HITCBC 731   18 if(self_->active_read_ops_->await_ready( 730   18 if(self_->active_read_ops_->await_ready(
HITCBC 732   18 self_->cached_awaitable_)) 731   18 self_->cached_awaitable_))
HITCBC 733   18 return h; 732   18 return h;
734   733  
MISUBC 735   return self_->active_read_ops_->await_suspend( 734   return self_->active_read_ops_->await_suspend(
MISUBC 736   self_->cached_awaitable_, h, env); 735   self_->cached_awaitable_, h, env);
737   } 736   }
738   737  
739   io_result<std::size_t> 738   io_result<std::size_t>
HITCBC 740   18 await_resume() 739   18 await_resume()
741   { 740   {
742   struct guard { 741   struct guard {
743   any_buffer_source* self; 742   any_buffer_source* self;
HITCBC 744   18 ~guard() { 743   18 ~guard() {
HITCBC 745   18 self->active_read_ops_->destroy( 744   18 self->active_read_ops_->destroy(
HITCBC 746   18 self->cached_awaitable_); 745   18 self->cached_awaitable_);
HITCBC 747   18 self->active_read_ops_ = nullptr; 746   18 self->active_read_ops_ = nullptr;
HITCBC 748   18 } 747   18 }
HITCBC 749   18 } g{self_}; 748   18 } g{self_};
HITCBC 750   18 return self_->active_read_ops_->await_resume( 749   18 return self_->active_read_ops_->await_resume(
HITCBC 751   30 self_->cached_awaitable_); 750   30 self_->cached_awaitable_);
HITCBC 752   18 } 751   18 }
753   }; 752   };
HITCBC 754   18 return awaitable{this, buffers}; 753   18 return awaitable{this, buffers};
755   } 754   }
756   755  
757   template<MutableBufferSequence MB> 756   template<MutableBufferSequence MB>
758   io_task<std::size_t> 757   io_task<std::size_t>
HITCBC 759   58 any_buffer_source::read_some(MB buffers) 758   58 any_buffer_source::read_some(MB buffers)
760   { 759   {
761   buffer_param<MB> bp(buffers); 760   buffer_param<MB> bp(buffers);
762   auto dest = bp.data(); 761   auto dest = bp.data();
763   if(dest.empty()) 762   if(dest.empty())
764   co_return {{}, 0}; 763   co_return {{}, 0};
765   764  
766   // Native ReadSource path 765   // Native ReadSource path
767   if(vt_->construct_read_some_awaitable) 766   if(vt_->construct_read_some_awaitable)
768   co_return co_await read_some_(dest); 767   co_return co_await read_some_(dest);
769   768  
770   // Synthesized path: pull + buffer_copy + consume 769   // Synthesized path: pull + buffer_copy + consume
771   const_buffer arr[detail::max_iovec_]; 770   const_buffer arr[detail::max_iovec_];
772   auto [ec, bufs] = co_await pull(arr); 771   auto [ec, bufs] = co_await pull(arr);
773   if(ec) 772   if(ec)
774   co_return {ec, 0}; 773   co_return {ec, 0};
775   774  
776   auto n = buffer_copy(dest, bufs); 775   auto n = buffer_copy(dest, bufs);
777   consume(n); 776   consume(n);
778   co_return {{}, n}; 777   co_return {{}, n};
HITCBC 779   116 } 778   116 }
780   779  
781   template<MutableBufferSequence MB> 780   template<MutableBufferSequence MB>
782   io_task<std::size_t> 781   io_task<std::size_t>
HITCBC 783   24 any_buffer_source::read(MB buffers) 782   24 any_buffer_source::read(MB buffers)
784   { 783   {
785   buffer_param<MB> bp(buffers); 784   buffer_param<MB> bp(buffers);
786   std::size_t total = 0; 785   std::size_t total = 0;
787   786  
788   // Native ReadSource path 787   // Native ReadSource path
789   if(vt_->construct_read_awaitable) 788   if(vt_->construct_read_awaitable)
790   { 789   {
791   for(;;) 790   for(;;)
792   { 791   {
793   auto dest = bp.data(); 792   auto dest = bp.data();
794   if(dest.empty()) 793   if(dest.empty())
795   break; 794   break;
796   795  
797   auto [ec, n] = co_await read_(dest); 796   auto [ec, n] = co_await read_(dest);
798   total += n; 797   total += n;
799   if(ec) 798   if(ec)
800   co_return {ec, total}; 799   co_return {ec, total};
801   bp.consume(n); 800   bp.consume(n);
802   } 801   }
803   co_return {{}, total}; 802   co_return {{}, total};
804   } 803   }
805   804  
806   // Synthesized path: pull + buffer_copy + consume 805   // Synthesized path: pull + buffer_copy + consume
807   for(;;) 806   for(;;)
808   { 807   {
809   auto dest = bp.data(); 808   auto dest = bp.data();
810   if(dest.empty()) 809   if(dest.empty())
811   break; 810   break;
812   811  
813   const_buffer arr[detail::max_iovec_]; 812   const_buffer arr[detail::max_iovec_];
814   auto [ec, bufs] = co_await pull(arr); 813   auto [ec, bufs] = co_await pull(arr);
815   814  
816   if(ec) 815   if(ec)
817   co_return {ec, total}; 816   co_return {ec, total};
818   817  
819   auto n = buffer_copy(dest, bufs); 818   auto n = buffer_copy(dest, bufs);
820   consume(n); 819   consume(n);
821   total += n; 820   total += n;
822   bp.consume(n); 821   bp.consume(n);
823   } 822   }
824   823  
825   co_return {{}, total}; 824   co_return {{}, total};
HITCBC 826   48 } 825   48 }
827   826  
828   static_assert(BufferSource<any_buffer_source>); 827   static_assert(BufferSource<any_buffer_source>);
829   static_assert(ReadSource<any_buffer_source>); 828   static_assert(ReadSource<any_buffer_source>);
830   829  
831   } // namespace capy 830   } // namespace capy
832   } // namespace boost 831   } // namespace boost
833   832  
834   #endif 833   #endif