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_result.hpp>
20 : #include <boost/capy/task.hpp>
21 :
22 : #include <cstddef>
23 : #include <span>
24 :
25 : namespace boost {
26 : namespace capy {
27 :
28 : /** Transfer data from a ReadSource to a BufferSink.
29 :
30 : This function reads data from the source directly into the sink's
31 : internal buffers using the callee-owns-buffers model. The sink
32 : provides writable buffers via `prepare()`, the source reads into
33 : them, and the sink commits the data. When the source signals EOF,
34 : `commit_eof()` is called on the sink to finalize the transfer.
35 :
36 : @tparam Src The source type, must satisfy @ref ReadSource.
37 : @tparam Sink The sink type, must satisfy @ref BufferSink.
38 :
39 : @param source The source to read data from.
40 : @param sink The sink to write data to.
41 :
42 : @return A task that yields `(std::error_code, std::size_t)`.
43 : On success, `ec` is default-constructed (no error) and `n` is
44 : the total number of bytes transferred. On error, `ec` contains
45 : the error code and `n` is the total number of bytes transferred
46 : before the error.
47 :
48 : @par Example
49 : @code
50 : task<void> transfer_body(ReadSource auto& source, BufferSink auto& sink)
51 : {
52 : auto [ec, n] = co_await pull_from(source, sink);
53 : if (ec)
54 : {
55 : // Handle error
56 : }
57 : // n bytes were transferred
58 : }
59 : @endcode
60 :
61 : @see ReadSource, BufferSink, push_to
62 : */
63 : template<ReadSource Src, BufferSink Sink>
64 : task<io_result<std::size_t>>
65 168 : pull_from(Src& source, Sink& sink)
66 : {
67 : mutable_buffer dst_arr[detail::max_iovec_];
68 : std::size_t total = 0;
69 :
70 : for(;;)
71 : {
72 : std::size_t dst_count = sink.prepare(dst_arr, detail::max_iovec_);
73 : if(dst_count == 0)
74 : {
75 : // No buffer space available; commit nothing to flush
76 : auto [flush_ec] = co_await sink.commit(0);
77 : if(flush_ec)
78 : co_return {flush_ec, total};
79 : continue;
80 : }
81 :
82 : auto [ec, n] = co_await source.read(
83 : std::span<mutable_buffer const>(dst_arr, dst_count));
84 :
85 : if(n > 0)
86 : {
87 : auto [commit_ec] = co_await sink.commit(n);
88 : if(commit_ec)
89 : co_return {commit_ec, total};
90 : total += n;
91 : }
92 :
93 : if(ec == cond::eof)
94 : {
95 : auto [eof_ec] = co_await sink.commit_eof();
96 : co_return {eof_ec, total};
97 : }
98 :
99 : if(ec)
100 : co_return {ec, total};
101 : }
102 336 : }
103 :
104 : /** Transfer data from a ReadStream to a BufferSink.
105 :
106 : This function reads data from the stream directly into the sink's
107 : internal buffers using the callee-owns-buffers model. The sink
108 : provides writable buffers via `prepare()`, the stream reads into
109 : them using `read_some()`, and the sink commits the data. When the
110 : stream signals EOF, `commit_eof()` is called on the sink to
111 : finalize the transfer.
112 :
113 : This overload handles partial reads from the stream, committing
114 : data incrementally as it arrives. It loops until EOF is encountered
115 : or an error occurs.
116 :
117 : @tparam Src The source type, must satisfy @ref ReadStream.
118 : @tparam Sink The sink type, must satisfy @ref BufferSink.
119 :
120 : @param source The stream to read data from.
121 : @param sink The sink to write data to.
122 :
123 : @return A task that yields `(std::error_code, std::size_t)`.
124 : On success, `ec` is default-constructed (no error) and `n` is
125 : the total number of bytes transferred. On error, `ec` contains
126 : the error code and `n` is the total number of bytes transferred
127 : before the error.
128 :
129 : @par Example
130 : @code
131 : task<void> transfer_body(ReadStream auto& stream, BufferSink auto& sink)
132 : {
133 : auto [ec, n] = co_await pull_from(stream, sink);
134 : if (ec)
135 : {
136 : // Handle error
137 : }
138 : // n bytes were transferred
139 : }
140 : @endcode
141 :
142 : @see ReadStream, BufferSink, push_to
143 : */
144 : template<ReadStream Src, BufferSink Sink>
145 : task<io_result<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 : std::size_t dst_count = sink.prepare(dst_arr, detail::max_iovec_);
155 : if(dst_count == 0)
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_arr, dst_count));
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();
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
|