libs/capy/include/boost/capy/io/pull_from.hpp

100.0% Lines (4/4) 100.0% Functions (4/4) 100.0% Branches (2/2)
libs/capy/include/boost/capy/io/pull_from.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_PULL_FROM_HPP
11 #define BOOST_CAPY_IO_PULL_FROM_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/buffers.hpp>
15 #include <boost/capy/cond.hpp>
16 #include <boost/capy/concept/buffer_sink.hpp>
17 #include <boost/capy/concept/read_source.hpp>
18 #include <boost/capy/concept/read_stream.hpp>
19 #include <boost/capy/io_task.hpp>
20
21 #include <cstddef>
22 #include <span>
23
24 namespace boost {
25 namespace capy {
26
27 /** Transfer data from a ReadSource to a BufferSink.
28
29 This function reads data from the source directly into the sink's
30 internal buffers using the callee-owns-buffers model. The sink
31 provides writable buffers via `prepare()`, the source reads into
32 them, and the sink commits the data. When the source signals EOF,
33 `commit_eof()` is called on the sink to finalize the transfer.
34
35 @tparam Src The source type, must satisfy @ref ReadSource.
36 @tparam Sink The sink type, must satisfy @ref BufferSink.
37
38 @param source The source to read data from.
39 @param sink The sink to write data to.
40
41 @return A task that yields `(std::error_code, std::size_t)`.
42 On success, `ec` is default-constructed (no error) and `n` is
43 the total number of bytes transferred. On error, `ec` contains
44 the error code and `n` is the total number of bytes transferred
45 before the error.
46
47 @par Example
48 @code
49 task<void> transfer_body(ReadSource auto& source, BufferSink auto& sink)
50 {
51 auto [ec, n] = co_await pull_from(source, sink);
52 if (ec)
53 {
54 // Handle error
55 }
56 // n bytes were transferred
57 }
58 @endcode
59
60 @see ReadSource, BufferSink, push_to
61 */
62 template<ReadSource Src, BufferSink Sink>
63 io_task<std::size_t>
64
1/1
✓ Branch 1 taken 132 times.
132 pull_from(Src& source, Sink& sink)
65 {
66 mutable_buffer dst_arr[detail::max_iovec_];
67 std::size_t total = 0;
68
69 for(;;)
70 {
71 auto dst_bufs = sink.prepare(dst_arr);
72 if(dst_bufs.empty())
73 {
74 // No buffer space available; commit nothing to flush
75 auto [flush_ec] = co_await sink.commit(0);
76 if(flush_ec)
77 co_return {flush_ec, total};
78 continue;
79 }
80
81 auto [ec, n] = co_await source.read(
82 std::span<mutable_buffer const>(dst_bufs));
83
84 if(n > 0)
85 {
86 auto [commit_ec] = co_await sink.commit(n);
87 if(commit_ec)
88 co_return {commit_ec, total};
89 total += n;
90 }
91
92 if(ec == cond::eof)
93 {
94 auto [eof_ec] = co_await sink.commit_eof(0);
95 co_return {eof_ec, total};
96 }
97
98 if(ec)
99 co_return {ec, total};
100 }
101 264 }
102
103 /** Transfer data from a ReadStream to a BufferSink.
104
105 This function reads data from the stream directly into the sink's
106 internal buffers using the callee-owns-buffers model. The sink
107 provides writable buffers via `prepare()`, the stream reads into
108 them using `read_some()`, and the sink commits the data. When the
109 stream signals EOF, `commit_eof()` is called on the sink to
110 finalize the transfer.
111
112 This overload handles partial reads from the stream, committing
113 data incrementally as it arrives. It loops until EOF is encountered
114 or an error occurs.
115
116 @tparam Src The source type, must satisfy @ref ReadStream.
117 @tparam Sink The sink type, must satisfy @ref BufferSink.
118
119 @param source The stream to read data from.
120 @param sink The sink to write data to.
121
122 @return A task that yields `(std::error_code, std::size_t)`.
123 On success, `ec` is default-constructed (no error) and `n` is
124 the total number of bytes transferred. On error, `ec` contains
125 the error code and `n` is the total number of bytes transferred
126 before the error.
127
128 @par Example
129 @code
130 task<void> transfer_body(ReadStream auto& stream, BufferSink auto& sink)
131 {
132 auto [ec, n] = co_await pull_from(stream, sink);
133 if (ec)
134 {
135 // Handle error
136 }
137 // n bytes were transferred
138 }
139 @endcode
140
141 @see ReadStream, BufferSink, push_to
142 */
143 template<ReadStream Src, BufferSink Sink>
144 requires (!ReadSource<Src>)
145 io_task<std::size_t>
146
1/1
✓ Branch 1 taken 200 times.
200 pull_from(Src& source, Sink& sink)
147 {
148 mutable_buffer dst_arr[detail::max_iovec_];
149 std::size_t total = 0;
150
151 for(;;)
152 {
153 // Prepare destination buffers from the sink
154 auto dst_bufs = sink.prepare(dst_arr);
155 if(dst_bufs.empty())
156 {
157 // No buffer space available; commit nothing to flush
158 auto [flush_ec] = co_await sink.commit(0);
159 if(flush_ec)
160 co_return {flush_ec, total};
161 continue;
162 }
163
164 // Read data from the stream into the sink's buffers
165 auto [ec, n] = co_await source.read_some(
166 std::span<mutable_buffer const>(dst_bufs));
167
168 // Commit any data that was read
169 if(n > 0)
170 {
171 auto [commit_ec] = co_await sink.commit(n);
172 if(commit_ec)
173 co_return {commit_ec, total};
174 total += n;
175 }
176
177 // Check for EOF condition
178 if(ec == cond::eof)
179 {
180 auto [eof_ec] = co_await sink.commit_eof(0);
181 co_return {eof_ec, total};
182 }
183
184 // Check for other errors
185 if(ec)
186 co_return {ec, total};
187 }
188 400 }
189
190 } // namespace capy
191 } // namespace boost
192
193 #endif
194