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