87.37% Lines (83/95) 90.00% Functions (18/20)
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_READ_STREAM_HPP 10   #ifndef BOOST_CAPY_IO_ANY_READ_STREAM_HPP
11   #define BOOST_CAPY_IO_ANY_READ_STREAM_HPP 11   #define BOOST_CAPY_IO_ANY_READ_STREAM_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/detail/await_suspend_helper.hpp> 14   #include <boost/capy/detail/await_suspend_helper.hpp>
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16 - #include <boost/capy/buffers/buffer_array.hpp> 16 + #include <boost/capy/detail/buffer_array.hpp>
17   #include <boost/capy/concept/io_awaitable.hpp> 17   #include <boost/capy/concept/io_awaitable.hpp>
18   #include <boost/capy/concept/read_stream.hpp> 18   #include <boost/capy/concept/read_stream.hpp>
19   #include <boost/capy/ex/io_env.hpp> 19   #include <boost/capy/ex/io_env.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   21  
22   #include <concepts> 22   #include <concepts>
23   #include <coroutine> 23   #include <coroutine>
24   #include <cstddef> 24   #include <cstddef>
  25 + #include <exception>
25   #include <new> 26   #include <new>
26   #include <span> 27   #include <span>
27   #include <stop_token> 28   #include <stop_token>
28   #include <system_error> 29   #include <system_error>
29   #include <utility> 30   #include <utility>
30   31  
31   namespace boost { 32   namespace boost {
32   namespace capy { 33   namespace capy {
33   34  
34   /** Type-erased wrapper for any ReadStream. 35   /** Type-erased wrapper for any ReadStream.
35   36  
36   This class provides type erasure for any type satisfying the 37   This class provides type erasure for any type satisfying the
37   @ref ReadStream concept, enabling runtime polymorphism for 38   @ref ReadStream concept, enabling runtime polymorphism for
38   read operations. It uses cached awaitable storage to achieve 39   read operations. It uses cached awaitable storage to achieve
39   zero steady-state allocation after construction. 40   zero steady-state allocation after construction.
40   41  
41   The wrapper supports two construction modes: 42   The wrapper supports two construction modes:
42   - **Owning**: Pass by value to transfer ownership. The wrapper 43   - **Owning**: Pass by value to transfer ownership. The wrapper
43   allocates storage and owns the stream. 44   allocates storage and owns the stream.
44   - **Reference**: Pass a pointer to wrap without ownership. The 45   - **Reference**: Pass a pointer to wrap without ownership. The
45   pointed-to stream must outlive this wrapper. 46   pointed-to stream must outlive this wrapper.
46   47  
47   @par Awaitable Preallocation 48   @par Awaitable Preallocation
48   The constructor preallocates storage for the type-erased awaitable. 49   The constructor preallocates storage for the type-erased awaitable.
49   This reserves all virtual address space at server startup 50   This reserves all virtual address space at server startup
50   so memory usage can be measured up front, rather than 51   so memory usage can be measured up front, rather than
51   allocating piecemeal as traffic arrives. 52   allocating piecemeal as traffic arrives.
52   53  
53   @par Immediate Completion 54   @par Immediate Completion
54   When the underlying stream's awaitable reports ready immediately 55   When the underlying stream's awaitable reports ready immediately
55   (e.g. buffered data already available), the wrapper skips 56   (e.g. buffered data already available), the wrapper skips
56   coroutine suspension entirely and returns the result inline. 57   coroutine suspension entirely and returns the result inline.
57   58  
58   @par Thread Safety 59   @par Thread Safety
59   Not thread-safe. Concurrent operations on the same wrapper 60   Not thread-safe. Concurrent operations on the same wrapper
60   are undefined behavior. 61   are undefined behavior.
61   62  
62   @par Example 63   @par Example
63   @code 64   @code
64   // Owning - takes ownership of the stream 65   // Owning - takes ownership of the stream
65   any_read_stream stream(socket{ioc}); 66   any_read_stream stream(socket{ioc});
66   67  
67   // Reference - wraps without ownership 68   // Reference - wraps without ownership
68   socket sock(ioc); 69   socket sock(ioc);
69   any_read_stream stream(&sock); 70   any_read_stream stream(&sock);
70   71  
71   mutable_buffer buf(data, size); 72   mutable_buffer buf(data, size);
72   auto [ec, n] = co_await stream.read_some(buf); 73   auto [ec, n] = co_await stream.read_some(buf);
73   @endcode 74   @endcode
74   75  
75   @see any_write_stream, any_stream, ReadStream 76   @see any_write_stream, any_stream, ReadStream
76   */ 77   */
77   class any_read_stream 78   class any_read_stream
78   { 79   {
79   struct vtable; 80   struct vtable;
80   81  
81   template<ReadStream S> 82   template<ReadStream S>
82   struct vtable_for_impl; 83   struct vtable_for_impl;
83   84  
84   // ordered for cache line coherence 85   // ordered for cache line coherence
85   void* stream_ = nullptr; 86   void* stream_ = nullptr;
86   vtable const* vt_ = nullptr; 87   vtable const* vt_ = nullptr;
87   void* cached_awaitable_ = nullptr; 88   void* cached_awaitable_ = nullptr;
88   void* storage_ = nullptr; 89   void* storage_ = nullptr;
89   bool awaitable_active_ = false; 90   bool awaitable_active_ = false;
90   91  
91   public: 92   public:
92   /** Destructor. 93   /** Destructor.
93   94  
94   Destroys the owned stream (if any) and releases the cached 95   Destroys the owned stream (if any) and releases the cached
95   awaitable storage. 96   awaitable storage.
96   */ 97   */
97   ~any_read_stream(); 98   ~any_read_stream();
98   99  
99 - /** Default constructor. 100 + /** Construct a default instance.
100   101  
101   Constructs an empty wrapper. Operations on a default-constructed 102   Constructs an empty wrapper. Operations on a default-constructed
102   wrapper result in undefined behavior. 103   wrapper result in undefined behavior.
103   */ 104   */
HITCBC 104   1 any_read_stream() = default; 105   1 any_read_stream() = default;
105   106  
106   /** Non-copyable. 107   /** Non-copyable.
107   108  
108   The awaitable cache is per-instance and cannot be shared. 109   The awaitable cache is per-instance and cannot be shared.
109   */ 110   */
110   any_read_stream(any_read_stream const&) = delete; 111   any_read_stream(any_read_stream const&) = delete;
111   any_read_stream& operator=(any_read_stream const&) = delete; 112   any_read_stream& operator=(any_read_stream const&) = delete;
112   113  
113 - /** Move constructor. 114 + /** Construct by moving.
114   115  
115   Transfers ownership of the wrapped stream (if owned) and 116   Transfers ownership of the wrapped stream (if owned) and
116   cached awaitable storage from `other`. After the move, `other` is 117   cached awaitable storage from `other`. After the move, `other` is
117   in a default-constructed state. 118   in a default-constructed state.
118   119  
119   @param other The wrapper to move from. 120   @param other The wrapper to move from.
120   */ 121   */
HITCBC 121   2 any_read_stream(any_read_stream&& other) noexcept 122   2 any_read_stream(any_read_stream&& other) noexcept
HITCBC 122   2 : stream_(std::exchange(other.stream_, nullptr)) 123   2 : stream_(std::exchange(other.stream_, nullptr))
HITCBC 123   2 , vt_(std::exchange(other.vt_, nullptr)) 124   2 , vt_(std::exchange(other.vt_, nullptr))
HITCBC 124   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr)) 125   2 , cached_awaitable_(std::exchange(other.cached_awaitable_, nullptr))
HITCBC 125   2 , storage_(std::exchange(other.storage_, nullptr)) 126   2 , storage_(std::exchange(other.storage_, nullptr))
HITCBC 126   2 , awaitable_active_(std::exchange(other.awaitable_active_, false)) 127   2 , awaitable_active_(std::exchange(other.awaitable_active_, false))
127   { 128   {
HITCBC 128   2 } 129   2 }
129   130  
130 - /** Move assignment operator. 131 + /** Assign by moving.
131   132  
132   Destroys any owned stream and releases existing resources, 133   Destroys any owned stream and releases existing resources,
133   then transfers ownership from `other`. 134   then transfers ownership from `other`.
134   135  
135   @param other The wrapper to move from. 136   @param other The wrapper to move from.
136   @return Reference to this wrapper. 137   @return Reference to this wrapper.
137   */ 138   */
138   any_read_stream& 139   any_read_stream&
139   operator=(any_read_stream&& other) noexcept; 140   operator=(any_read_stream&& other) noexcept;
140   141  
141   /** Construct by taking ownership of a ReadStream. 142   /** Construct by taking ownership of a ReadStream.
142   143  
143   Allocates storage and moves the stream into this wrapper. 144   Allocates storage and moves the stream into this wrapper.
144   The wrapper owns the stream and will destroy it. 145   The wrapper owns the stream and will destroy it.
145   146  
146   @param s The stream to take ownership of. 147   @param s The stream to take ownership of.
147   */ 148   */
148   template<ReadStream S> 149   template<ReadStream S>
149   requires (!std::same_as<std::decay_t<S>, any_read_stream>) 150   requires (!std::same_as<std::decay_t<S>, any_read_stream>)
150   any_read_stream(S s); 151   any_read_stream(S s);
151   152  
152   /** Construct by wrapping a ReadStream without ownership. 153   /** Construct by wrapping a ReadStream without ownership.
153   154  
154   Wraps the given stream by pointer. The stream must remain 155   Wraps the given stream by pointer. The stream must remain
155   valid for the lifetime of this wrapper. 156   valid for the lifetime of this wrapper.
156   157  
157   @param s Pointer to the stream to wrap. 158   @param s Pointer to the stream to wrap.
158   */ 159   */
159   template<ReadStream S> 160   template<ReadStream S>
160   any_read_stream(S* s); 161   any_read_stream(S* s);
161   162  
162   /** Check if the wrapper contains a valid stream. 163   /** Check if the wrapper contains a valid stream.
163   164  
164   @return `true` if wrapping a stream, `false` if default-constructed 165   @return `true` if wrapping a stream, `false` if default-constructed
165   or moved-from. 166   or moved-from.
166   */ 167   */
167   bool 168   bool
HITCBC 168   25 has_value() const noexcept 169   25 has_value() const noexcept
169   { 170   {
HITCBC 170   25 return stream_ != nullptr; 171   25 return stream_ != nullptr;
171   } 172   }
172   173  
173   /** Check if the wrapper contains a valid stream. 174   /** Check if the wrapper contains a valid stream.
174   175  
175   @return `true` if wrapping a stream, `false` if default-constructed 176   @return `true` if wrapping a stream, `false` if default-constructed
176   or moved-from. 177   or moved-from.
177   */ 178   */
178   explicit 179   explicit
HITCBC 179   3 operator bool() const noexcept 180   3 operator bool() const noexcept
180   { 181   {
HITCBC 181   3 return has_value(); 182   3 return has_value();
182   } 183   }
183   184  
184   /** Initiate an asynchronous read operation. 185   /** Initiate an asynchronous read operation.
185   186  
186   Reads data into the provided buffer sequence. The operation 187   Reads data into the provided buffer sequence. The operation
187   completes when at least one byte has been read, or an error 188   completes when at least one byte has been read, or an error
188   occurs. 189   occurs.
189   190  
190   @param buffers The buffer sequence to read into. Passed by 191   @param buffers The buffer sequence to read into. Passed by
191   value to ensure the sequence lives in the coroutine frame 192   value to ensure the sequence lives in the coroutine frame
192   across suspension points. 193   across suspension points.
193   194  
194 - @return An awaitable yielding `(error_code,std::size_t)`. 195 + @return An awaitable that await-returns `(error_code,std::size_t)`.
195   196  
196   @par Immediate Completion 197   @par Immediate Completion
197   The operation completes immediately without suspending 198   The operation completes immediately without suspending
198   the calling coroutine when the underlying stream's 199   the calling coroutine when the underlying stream's
199   awaitable reports immediate readiness via `await_ready`. 200   awaitable reports immediate readiness via `await_ready`.
200   201  
201   @note This is a partial operation and may not process the 202   @note This is a partial operation and may not process the
202   entire buffer sequence. Use the composed @ref read algorithm 203   entire buffer sequence. Use the composed @ref read algorithm
203   for guaranteed complete transfer. 204   for guaranteed complete transfer.
204   205  
205   @par Preconditions 206   @par Preconditions
206   The wrapper must contain a valid stream (`has_value() == true`). 207   The wrapper must contain a valid stream (`has_value() == true`).
207   The caller must not call this function again after a prior 208   The caller must not call this function again after a prior
208   call returned an error (including EOF). 209   call returned an error (including EOF).
209   */ 210   */
210   template<MutableBufferSequence MB> 211   template<MutableBufferSequence MB>
211   auto 212   auto
212   read_some(MB buffers); 213   read_some(MB buffers);
213   214  
214   protected: 215   protected:
215   /** Rebind to a new stream after move. 216   /** Rebind to a new stream after move.
216   217  
217   Updates the internal pointer to reference a new stream object. 218   Updates the internal pointer to reference a new stream object.
218   Used by owning wrappers after move assignment when the owned 219   Used by owning wrappers after move assignment when the owned
219   object has moved to a new location. 220   object has moved to a new location.
220   221  
221   @param new_stream The new stream to bind to. Must be the same 222   @param new_stream The new stream to bind to. Must be the same
222   type as the original stream. 223   type as the original stream.
223   224  
224   @note Terminates if called with a stream of different type 225   @note Terminates if called with a stream of different type
225   than the original. 226   than the original.
226   */ 227   */
227   template<ReadStream S> 228   template<ReadStream S>
228   void 229   void
229   rebind(S& new_stream) noexcept 230   rebind(S& new_stream) noexcept
230   { 231   {
231   if(vt_ != &vtable_for_impl<S>::value) 232   if(vt_ != &vtable_for_impl<S>::value)
232   std::terminate(); 233   std::terminate();
233   stream_ = &new_stream; 234   stream_ = &new_stream;
234   } 235   }
235   }; 236   };
236 - //----------------------------------------------------------  
237 -  
238   237  
239   struct any_read_stream::vtable 238   struct any_read_stream::vtable
240   { 239   {
241   // ordered by call frequency for cache line coherence 240   // ordered by call frequency for cache line coherence
242   void (*construct_awaitable)( 241   void (*construct_awaitable)(
243   void* stream, 242   void* stream,
244   void* storage, 243   void* storage,
245   std::span<mutable_buffer const> buffers); 244   std::span<mutable_buffer const> buffers);
246   bool (*await_ready)(void*); 245   bool (*await_ready)(void*);
247   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); 246   std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*);
248   io_result<std::size_t> (*await_resume)(void*); 247   io_result<std::size_t> (*await_resume)(void*);
249   void (*destroy_awaitable)(void*) noexcept; 248   void (*destroy_awaitable)(void*) noexcept;
250   std::size_t awaitable_size; 249   std::size_t awaitable_size;
251   std::size_t awaitable_align; 250   std::size_t awaitable_align;
252   void (*destroy)(void*) noexcept; 251   void (*destroy)(void*) noexcept;
253   }; 252   };
254   253  
255   template<ReadStream S> 254   template<ReadStream S>
256   struct any_read_stream::vtable_for_impl 255   struct any_read_stream::vtable_for_impl
257   { 256   {
258   using Awaitable = decltype(std::declval<S&>().read_some( 257   using Awaitable = decltype(std::declval<S&>().read_some(
259   std::span<mutable_buffer const>{})); 258   std::span<mutable_buffer const>{}));
260   259  
261   static void 260   static void
HITCBC 262   1 do_destroy_impl(void* stream) noexcept 261   1 do_destroy_impl(void* stream) noexcept
263   { 262   {
HITCBC 264   1 static_cast<S*>(stream)->~S(); 263   1 static_cast<S*>(stream)->~S();
HITCBC 265   1 } 264   1 }
266   265  
267   static void 266   static void
HITCBC 268   91 construct_awaitable_impl( 267   91 construct_awaitable_impl(
269   void* stream, 268   void* stream,
270   void* storage, 269   void* storage,
271   std::span<mutable_buffer const> buffers) 270   std::span<mutable_buffer const> buffers)
272   { 271   {
HITCBC 273   91 auto& s = *static_cast<S*>(stream); 272   91 auto& s = *static_cast<S*>(stream);
HITCBC 274   91 ::new(storage) Awaitable(s.read_some(buffers)); 273   91 ::new(storage) Awaitable(s.read_some(buffers));
HITCBC 275   91 } 274   91 }
276   275  
277   static constexpr vtable value = { 276   static constexpr vtable value = {
278   &construct_awaitable_impl, 277   &construct_awaitable_impl,
HITCBC 279   91 +[](void* p) { 278   91 +[](void* p) {
HITCBC 280   91 return static_cast<Awaitable*>(p)->await_ready(); 279   91 return static_cast<Awaitable*>(p)->await_ready();
281   }, 280   },
MISUBC 282   +[](void* p, std::coroutine_handle<> h, io_env const* env) { 281   +[](void* p, std::coroutine_handle<> h, io_env const* env) {
MISUBC 283   return detail::call_await_suspend( 282   return detail::call_await_suspend(
MISUBC 284   static_cast<Awaitable*>(p), h, env); 283   static_cast<Awaitable*>(p), h, env);
285   }, 284   },
HITCBC 286   89 +[](void* p) { 285   89 +[](void* p) {
HITCBC 287   89 return static_cast<Awaitable*>(p)->await_resume(); 286   89 return static_cast<Awaitable*>(p)->await_resume();
288   }, 287   },
HITCBC 289   93 +[](void* p) noexcept { 288   93 +[](void* p) noexcept {
HITCBC 290   16 static_cast<Awaitable*>(p)->~Awaitable(); 289   16 static_cast<Awaitable*>(p)->~Awaitable();
291   }, 290   },
292   sizeof(Awaitable), 291   sizeof(Awaitable),
293   alignof(Awaitable), 292   alignof(Awaitable),
294   &do_destroy_impl 293   &do_destroy_impl
295   }; 294   };
296   }; 295   };
297 - //----------------------------------------------------------  
298 -  
299   296  
300   inline 297   inline
HITCBC 301   101 any_read_stream::~any_read_stream() 298   101 any_read_stream::~any_read_stream()
302   { 299   {
HITCBC 303   101 if(storage_) 300   101 if(storage_)
304   { 301   {
HITCBC 305   1 vt_->destroy(stream_); 302   1 vt_->destroy(stream_);
HITCBC 306   1 ::operator delete(storage_); 303   1 ::operator delete(storage_);
307   } 304   }
HITCBC 308   101 if(cached_awaitable_) 305   101 if(cached_awaitable_)
309   { 306   {
HITCBC 310   91 if(awaitable_active_) 307   91 if(awaitable_active_)
HITCBC 311   1 vt_->destroy_awaitable(cached_awaitable_); 308   1 vt_->destroy_awaitable(cached_awaitable_);
HITCBC 312   91 ::operator delete(cached_awaitable_); 309   91 ::operator delete(cached_awaitable_);
313   } 310   }
HITCBC 314   101 } 311   101 }
315   312  
316   inline any_read_stream& 313   inline any_read_stream&
HITCBC 317   5 any_read_stream::operator=(any_read_stream&& other) noexcept 314   5 any_read_stream::operator=(any_read_stream&& other) noexcept
318   { 315   {
HITCBC 319   5 if(this != &other) 316   5 if(this != &other)
320   { 317   {
HITCBC 321   5 if(storage_) 318   5 if(storage_)
322   { 319   {
MISUBC 323   vt_->destroy(stream_); 320   vt_->destroy(stream_);
MISUBC 324   ::operator delete(storage_); 321   ::operator delete(storage_);
325   } 322   }
HITCBC 326   5 if(cached_awaitable_) 323   5 if(cached_awaitable_)
327   { 324   {
HITCBC 328   2 if(awaitable_active_) 325   2 if(awaitable_active_)
HITCBC 329   1 vt_->destroy_awaitable(cached_awaitable_); 326   1 vt_->destroy_awaitable(cached_awaitable_);
HITCBC 330   2 ::operator delete(cached_awaitable_); 327   2 ::operator delete(cached_awaitable_);
331   } 328   }
HITCBC 332   5 stream_ = std::exchange(other.stream_, nullptr); 329   5 stream_ = std::exchange(other.stream_, nullptr);
HITCBC 333   5 vt_ = std::exchange(other.vt_, nullptr); 330   5 vt_ = std::exchange(other.vt_, nullptr);
HITCBC 334   5 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr); 331   5 cached_awaitable_ = std::exchange(other.cached_awaitable_, nullptr);
HITCBC 335   5 storage_ = std::exchange(other.storage_, nullptr); 332   5 storage_ = std::exchange(other.storage_, nullptr);
HITCBC 336   5 awaitable_active_ = std::exchange(other.awaitable_active_, false); 333   5 awaitable_active_ = std::exchange(other.awaitable_active_, false);
337   } 334   }
HITCBC 338   5 return *this; 335   5 return *this;
339   } 336   }
340   337  
341   template<ReadStream S> 338   template<ReadStream S>
342   requires (!std::same_as<std::decay_t<S>, any_read_stream>) 339   requires (!std::same_as<std::decay_t<S>, any_read_stream>)
HITCBC 343   1 any_read_stream::any_read_stream(S s) 340   1 any_read_stream::any_read_stream(S s)
HITCBC 344   1 : vt_(&vtable_for_impl<S>::value) 341   1 : vt_(&vtable_for_impl<S>::value)
345   { 342   {
346   struct guard { 343   struct guard {
347   any_read_stream* self; 344   any_read_stream* self;
348   bool committed = false; 345   bool committed = false;
HITCBC 349   1 ~guard() { 346   1 ~guard() {
HITCBC 350   1 if(!committed && self->storage_) { 347   1 if(!committed && self->storage_) {
MISUBC 351   self->vt_->destroy(self->stream_); 348   self->vt_->destroy(self->stream_);
MISUBC 352   ::operator delete(self->storage_); 349   ::operator delete(self->storage_);
MISUBC 353   self->storage_ = nullptr; 350   self->storage_ = nullptr;
MISUBC 354   self->stream_ = nullptr; 351   self->stream_ = nullptr;
355   } 352   }
HITCBC 356   1 } 353   1 }
HITCBC 357   1 } g{this}; 354   1 } g{this};
358   355  
HITCBC 359   1 storage_ = ::operator new(sizeof(S)); 356   1 storage_ = ::operator new(sizeof(S));
HITCBC 360   1 stream_ = ::new(storage_) S(std::move(s)); 357   1 stream_ = ::new(storage_) S(std::move(s));
361   358  
362   // Preallocate the awaitable storage 359   // Preallocate the awaitable storage
HITCBC 363   1 cached_awaitable_ = ::operator new(vt_->awaitable_size); 360   1 cached_awaitable_ = ::operator new(vt_->awaitable_size);
364   361  
HITCBC 365   1 g.committed = true; 362   1 g.committed = true;
HITCBC 366   1 } 363   1 }
367   364  
368   template<ReadStream S> 365   template<ReadStream S>
HITCBC 369   92 any_read_stream::any_read_stream(S* s) 366   92 any_read_stream::any_read_stream(S* s)
HITCBC 370   92 : stream_(s) 367   92 : stream_(s)
HITCBC 371   92 , vt_(&vtable_for_impl<S>::value) 368   92 , vt_(&vtable_for_impl<S>::value)
372   { 369   {
373   // Preallocate the awaitable storage 370   // Preallocate the awaitable storage
HITCBC 374   92 cached_awaitable_ = ::operator new(vt_->awaitable_size); 371   92 cached_awaitable_ = ::operator new(vt_->awaitable_size);
HITCBC 375   92 } 372   92 }
376 - //----------------------------------------------------------  
377 -  
378   373  
379   template<MutableBufferSequence MB> 374   template<MutableBufferSequence MB>
380   auto 375   auto
HITCBC 381   91 any_read_stream::read_some(MB buffers) 376   91 any_read_stream::read_some(MB buffers)
382   { 377   {
383   // VFALCO in theory, we could use if constexpr to detect a 378   // VFALCO in theory, we could use if constexpr to detect a
384   // span and then pass that through to read_some without the array 379   // span and then pass that through to read_some without the array
385   struct awaitable 380   struct awaitable
386   { 381   {
387   any_read_stream* self_; 382   any_read_stream* self_;
388 - mutable_buffer_array<detail::max_iovec_> ba_; 383 + detail::mutable_buffer_array<detail::max_iovec_> ba_;
389   384  
390   bool 385   bool
HITCBC 391   91 await_ready() 386   91 await_ready()
392   { 387   {
HITCBC 393   91 self_->vt_->construct_awaitable( 388   91 self_->vt_->construct_awaitable(
HITCBC 394   91 self_->stream_, 389   91 self_->stream_,
HITCBC 395   91 self_->cached_awaitable_, 390   91 self_->cached_awaitable_,
HITCBC 396   91 ba_.to_span()); 391   91 ba_.to_span());
HITCBC 397   91 self_->awaitable_active_ = true; 392   91 self_->awaitable_active_ = true;
398   393  
HITCBC 399   182 return self_->vt_->await_ready( 394   182 return self_->vt_->await_ready(
HITCBC 400   91 self_->cached_awaitable_); 395   91 self_->cached_awaitable_);
401   } 396   }
402   397  
403   std::coroutine_handle<> 398   std::coroutine_handle<>
MISUBC 404   await_suspend(std::coroutine_handle<> h, io_env const* env) 399   await_suspend(std::coroutine_handle<> h, io_env const* env)
405   { 400   {
MISUBC 406   return self_->vt_->await_suspend( 401   return self_->vt_->await_suspend(
MISUBC 407   self_->cached_awaitable_, h, env); 402   self_->cached_awaitable_, h, env);
408   } 403   }
409   404  
410   io_result<std::size_t> 405   io_result<std::size_t>
HITCBC 411   89 await_resume() 406   89 await_resume()
412   { 407   {
413   struct guard { 408   struct guard {
414   any_read_stream* self; 409   any_read_stream* self;
HITCBC 415   89 ~guard() { 410   89 ~guard() {
HITCBC 416   89 self->vt_->destroy_awaitable(self->cached_awaitable_); 411   89 self->vt_->destroy_awaitable(self->cached_awaitable_);
HITCBC 417   89 self->awaitable_active_ = false; 412   89 self->awaitable_active_ = false;
HITCBC 418   89 } 413   89 }
HITCBC 419   89 } g{self_}; 414   89 } g{self_};
HITCBC 420   89 return self_->vt_->await_resume( 415   89 return self_->vt_->await_resume(
HITCBC 421   154 self_->cached_awaitable_); 416   154 self_->cached_awaitable_);
HITCBC 422   89 } 417   89 }
423   }; 418   };
424   return awaitable{this, 419   return awaitable{this,
HITCBC 425 - 91 mutable_buffer_array<detail::max_iovec_>(buffers)}; 420 + 91 detail::mutable_buffer_array<detail::max_iovec_>(buffers)};
HITCBC 426   91 } 421   91 }
427   422  
428   } // namespace capy 423   } // namespace capy
429   } // namespace boost 424   } // namespace boost
430   425  
431   #endif 426   #endif