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