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_READ_HPP
11 : #define BOOST_CAPY_READ_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/cond.hpp>
15 : #include <boost/capy/io_result.hpp>
16 : #include <boost/capy/task.hpp>
17 : #include <boost/capy/buffers.hpp>
18 : #include <boost/capy/buffers/consuming_buffers.hpp>
19 : #include <boost/capy/concept/dynamic_buffer.hpp>
20 : #include <boost/capy/concept/read_source.hpp>
21 : #include <boost/capy/concept/read_stream.hpp>
22 : #include <system_error>
23 :
24 : #include <cstddef>
25 :
26 : namespace boost {
27 : namespace capy {
28 :
29 : /** Asynchronously read until the buffer sequence is full.
30 :
31 : Reads data from the stream by calling `read_some` repeatedly
32 : until the entire buffer sequence is filled or an error occurs.
33 :
34 : @li The operation completes when:
35 : @li The buffer sequence is completely filled
36 : @li An error occurs (including `cond::eof`)
37 : @li The operation is cancelled
38 :
39 : @par Cancellation
40 : Supports cancellation via `stop_token` propagated through the
41 : IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
42 :
43 : @param stream The stream to read from. The caller retains ownership.
44 : @param buffers The buffer sequence to fill. The caller retains
45 : ownership and must ensure validity until the operation completes.
46 :
47 : @return An awaitable yielding `(error_code, std::size_t)`.
48 : On success, `n` equals `buffer_size(buffers)`. On error,
49 : `n` is the number of bytes read before the error. Compare
50 : error codes to conditions:
51 : @li `cond::eof` - Stream reached end before buffer was filled
52 : @li `cond::canceled` - Operation was cancelled
53 :
54 : @par Example
55 :
56 : @code
57 : task<> read_message( ReadStream auto& stream )
58 : {
59 : char header[16];
60 : auto [ec, n] = co_await read( stream, mutable_buffer( header ) );
61 : if( ec == cond::eof )
62 : co_return; // Connection closed
63 : if( ec.failed() )
64 : detail::throw_system_error( ec );
65 : // header contains exactly 16 bytes
66 : }
67 : @endcode
68 :
69 : @see read_some, ReadStream, MutableBufferSequence
70 : */
71 : auto
72 50 : read(
73 : ReadStream auto& stream,
74 : MutableBufferSequence auto const& buffers) ->
75 : task<io_result<std::size_t>>
76 : {
77 : consuming_buffers consuming(buffers);
78 : std::size_t const total_size = buffer_size(buffers);
79 : std::size_t total_read = 0;
80 :
81 : while(total_read < total_size)
82 : {
83 : auto [ec, n] = co_await stream.read_some(consuming);
84 : if(ec)
85 : co_return {ec, total_read};
86 : consuming.consume(n);
87 : total_read += n;
88 : }
89 :
90 : co_return {{}, total_read};
91 100 : }
92 :
93 : /** Asynchronously read all data from a stream into a dynamic buffer.
94 :
95 : Reads data by calling `read_some` repeatedly until EOF is reached
96 : or an error occurs. Data is appended using prepare/commit semantics.
97 : The buffer grows with 1.5x factor when filled.
98 :
99 : @li The operation completes when:
100 : @li End-of-stream is reached (`cond::eof`)
101 : @li An error occurs
102 : @li The operation is cancelled
103 :
104 : @par Cancellation
105 : Supports cancellation via `stop_token` propagated through the
106 : IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
107 :
108 : @param stream The stream to read from. The caller retains ownership.
109 : @param buffers The dynamic buffer to append data to. Must remain
110 : valid until the operation completes.
111 : @param initial_amount Initial bytes to prepare (default 2048).
112 :
113 : @return An awaitable yielding `(error_code, std::size_t)`.
114 : On success (EOF), `ec` is clear and `n` is total bytes read.
115 : On error, `n` is bytes read before the error. Compare error
116 : codes to conditions:
117 : @li `cond::canceled` - Operation was cancelled
118 :
119 : @par Example
120 :
121 : @code
122 : task<std::string> read_body( ReadStream auto& stream )
123 : {
124 : std::string body;
125 : auto [ec, n] = co_await read( stream, string_dynamic_buffer( &body ) );
126 : if( ec.failed() )
127 : detail::throw_system_error( ec );
128 : return body;
129 : }
130 : @endcode
131 :
132 : @see read_some, ReadStream, DynamicBufferParam
133 : */
134 : auto
135 80 : read(
136 : ReadStream auto& stream,
137 : DynamicBufferParam auto&& buffers,
138 : std::size_t initial_amount = 2048) ->
139 : task<io_result<std::size_t>>
140 : {
141 : std::size_t amount = initial_amount;
142 : std::size_t total_read = 0;
143 : for(;;)
144 : {
145 : auto mb = buffers.prepare(amount);
146 : auto const mb_size = buffer_size(mb);
147 : auto [ec, n] = co_await stream.read_some(mb);
148 : buffers.commit(n);
149 : total_read += n;
150 : if(ec == cond::eof)
151 : co_return {{}, total_read};
152 : if(ec)
153 : co_return {ec, total_read};
154 : if(n == mb_size)
155 : amount = amount / 2 + amount;
156 : }
157 160 : }
158 :
159 : /** Asynchronously read all data from a source into a dynamic buffer.
160 :
161 : Reads data by calling `source.read` repeatedly until EOF is reached
162 : or an error occurs. Data is appended using prepare/commit semantics.
163 : The buffer grows with 1.5x factor when filled.
164 :
165 : @li The operation completes when:
166 : @li End-of-stream is reached (`cond::eof`)
167 : @li An error occurs
168 : @li The operation is cancelled
169 :
170 : @par Cancellation
171 : Supports cancellation via `stop_token` propagated through the
172 : IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
173 :
174 : @param source The source to read from. The caller retains ownership.
175 : @param buffers The dynamic buffer to append data to. Must remain
176 : valid until the operation completes.
177 : @param initial_amount Initial bytes to prepare (default 2048).
178 :
179 : @return An awaitable yielding `(error_code, std::size_t)`.
180 : On success (EOF), `ec` is clear and `n` is total bytes read.
181 : On error, `n` is bytes read before the error. Compare error
182 : codes to conditions:
183 : @li `cond::canceled` - Operation was cancelled
184 :
185 : @par Example
186 :
187 : @code
188 : task<std::string> read_body( ReadSource auto& source )
189 : {
190 : std::string body;
191 : auto [ec, n] = co_await read( source, string_dynamic_buffer( &body ) );
192 : if( ec.failed() )
193 : detail::throw_system_error( ec );
194 : return body;
195 : }
196 : @endcode
197 :
198 : @see ReadSource, DynamicBufferParam
199 : */
200 : auto
201 66 : read(
202 : ReadSource auto& source,
203 : DynamicBufferParam auto&& buffers,
204 : std::size_t initial_amount = 2048) ->
205 : task<io_result<std::size_t>>
206 : {
207 : std::size_t amount = initial_amount;
208 : std::size_t total_read = 0;
209 : for(;;)
210 : {
211 : auto mb = buffers.prepare(amount);
212 : auto const mb_size = buffer_size(mb);
213 : auto [ec, n] = co_await source.read(mb);
214 : buffers.commit(n);
215 : total_read += n;
216 : if(ec == cond::eof)
217 : co_return {{}, total_read};
218 : if(ec)
219 : co_return {ec, total_read};
220 : if(n == mb_size)
221 : amount = amount / 2 + amount; // 1.5x growth
222 : }
223 132 : }
224 :
225 : } // namespace capy
226 : } // namespace boost
227 :
228 : #endif
|