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 : /** Read data until the buffer sequence is full or an error occurs.
30 :
31 : This function reads data from the stream into the buffer sequence
32 : until either the entire buffer sequence is filled or an error
33 : occurs (including end-of-file).
34 :
35 : @tparam Stream The stream type, must satisfy @ref ReadStream.
36 : @tparam MB The buffer sequence type, must satisfy
37 : @ref MutableBufferSequence.
38 :
39 : @param stream The stream to read from.
40 : @param buffers The buffer sequence to read into.
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 : `buffer_size(buffers)`. On error or EOF, `ec` contains the
45 : error code and `n` is the total number of bytes written before
46 : the error.
47 :
48 : @par Example
49 : @code
50 : task<void> example(ReadStream auto& stream)
51 : {
52 : char buf[1024];
53 : auto [ec, n] = co_await read(stream, mutable_buffer(buf, sizeof(buf)));
54 : if (ec == cond::eof)
55 : {
56 : // Handle end-of-file
57 : }
58 : else if (ec)
59 : {
60 : // Handle other error
61 : }
62 : // n bytes were read into buf
63 : }
64 : @endcode
65 :
66 : @see ReadStream, MutableBufferSequence
67 : */
68 : auto
69 50 : read(
70 : ReadStream auto& stream,
71 : MutableBufferSequence auto const& buffers) ->
72 : task<io_result<std::size_t>>
73 : {
74 : consuming_buffers consuming(buffers);
75 : std::size_t const total_size = buffer_size(buffers);
76 : std::size_t total_read = 0;
77 :
78 : while(total_read < total_size)
79 : {
80 : auto [ec, n] = co_await stream.read_some(consuming);
81 : if(ec)
82 : co_return {ec, total_read};
83 : consuming.consume(n);
84 : total_read += n;
85 : }
86 :
87 : co_return {{}, total_read};
88 100 : }
89 :
90 : /** Read data from a stream into a dynamic buffer.
91 :
92 : This function reads data from the stream into the dynamic buffer
93 : until end-of-file is reached or an error occurs. Data is appended
94 : to the buffer using prepare/commit semantics.
95 :
96 : The buffer grows using a strategy that starts with `initial_amount`
97 : bytes and grows by a factor of 1.5 when filled.
98 :
99 : @param stream The stream to read from, must satisfy @ref ReadStream.
100 : @param buffers The dynamic buffer to read into.
101 : @param initial_amount The initial number of bytes to prepare.
102 :
103 : @return A task that yields `(std::error_code, std::size_t)`.
104 : On success (EOF reached), `ec` is default-constructed and `n`
105 : is the total number of bytes read. On error, `ec` contains the
106 : error code and `n` is the total number of bytes read before
107 : the error.
108 :
109 : @par Example
110 : @code
111 : task<void> example(ReadStream auto& stream)
112 : {
113 : std::string body;
114 : auto [ec, n] = co_await read(stream, string_dynamic_buffer(&body));
115 : // body contains n bytes of data
116 : }
117 : @endcode
118 :
119 : @see ReadStream, DynamicBufferParam
120 : */
121 : auto
122 80 : read(
123 : ReadStream auto& stream,
124 : DynamicBufferParam auto&& buffers,
125 : std::size_t initial_amount = 2048) ->
126 : task<io_result<std::size_t>>
127 : {
128 : std::size_t amount = initial_amount;
129 : std::size_t total_read = 0;
130 : for(;;)
131 : {
132 : auto mb = buffers.prepare(amount);
133 : auto const mb_size = buffer_size(mb);
134 : auto [ec, n] = co_await stream.read_some(mb);
135 : buffers.commit(n);
136 : total_read += n;
137 : if(ec == cond::eof)
138 : co_return {{}, total_read};
139 : if(ec)
140 : co_return {ec, total_read};
141 : if(n == mb_size)
142 : amount = amount / 2 + amount;
143 : }
144 160 : }
145 :
146 : /** Read data from a source into a dynamic buffer.
147 :
148 : This function reads data from the source into the dynamic buffer
149 : until end-of-file is reached or an error occurs. Data is appended
150 : to the buffer using prepare/commit semantics.
151 :
152 : The buffer grows using a strategy that starts with `initial_amount`
153 : bytes and grows by a factor of 1.5 when filled.
154 :
155 : @tparam Source The source type, must satisfy @ref ReadSource.
156 :
157 : @param source The source to read from.
158 : @param buffers The dynamic buffer to read into.
159 : @param initial_amount The initial number of bytes to prepare.
160 :
161 : @return A task that yields `(std::error_code, std::size_t)`.
162 : On success (EOF reached), `ec` is default-constructed and `n`
163 : is the total number of bytes read. On error, `ec` contains the
164 : error code and `n` is the total number of bytes read before
165 : the error.
166 :
167 : @par Example
168 : @code
169 : task<void> example(ReadSource auto& source)
170 : {
171 : std::string body;
172 : auto [ec, n] = co_await read(source, string_buffers(body));
173 : if (ec)
174 : {
175 : // Handle error
176 : }
177 : // body contains n bytes of data
178 : }
179 : @endcode
180 :
181 : @see ReadSource, DynamicBufferParam
182 : */
183 : auto
184 66 : read(
185 : ReadSource auto& source,
186 : DynamicBufferParam auto&& buffers,
187 : std::size_t initial_amount = 2048) ->
188 : task<io_result<std::size_t>>
189 : {
190 : std::size_t amount = initial_amount;
191 : std::size_t total_read = 0;
192 : for(;;)
193 : {
194 : auto mb = buffers.prepare(amount);
195 : auto const mb_size = buffer_size(mb);
196 : auto [ec, n] = co_await source.read(mb);
197 : buffers.commit(n);
198 : total_read += n;
199 : if(ec == cond::eof)
200 : co_return {{}, total_read};
201 : if(ec)
202 : co_return {ec, total_read};
203 : if(n == mb_size)
204 : amount = amount / 2 + amount; // 1.5x growth
205 : }
206 132 : }
207 :
208 : } // namespace capy
209 : } // namespace boost
210 :
211 : #endif
|