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_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
1/1
✓ Branch 1 taken 168 times.
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
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 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
194