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