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