Line data 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 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 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
|