100.00% Lines (6/6) 100.00% Functions (3/3)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_READ_HPP 10   #ifndef BOOST_CAPY_READ_HPP
11   #define BOOST_CAPY_READ_HPP 11   #define BOOST_CAPY_READ_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/cond.hpp> 14   #include <boost/capy/cond.hpp>
15   #include <boost/capy/io_task.hpp> 15   #include <boost/capy/io_task.hpp>
16   #include <boost/capy/buffers.hpp> 16   #include <boost/capy/buffers.hpp>
17   #include <boost/capy/buffers/buffer_slice.hpp> 17   #include <boost/capy/buffers/buffer_slice.hpp>
18   #include <boost/capy/concept/dynamic_buffer.hpp> 18   #include <boost/capy/concept/dynamic_buffer.hpp>
19   #include <boost/capy/concept/read_source.hpp> 19   #include <boost/capy/concept/read_source.hpp>
20   #include <boost/capy/concept/read_stream.hpp> 20   #include <boost/capy/concept/read_stream.hpp>
21   #include <system_error> 21   #include <system_error>
22   22  
23   #include <cstddef> 23   #include <cstddef>
24   24  
25   namespace boost { 25   namespace boost {
26   namespace capy { 26   namespace capy {
27   27  
28   /** Read data from a stream until the buffer sequence is full. 28   /** Read data from a stream until the buffer sequence is full.
29   29  
30   @par Await-effects 30   @par Await-effects
31   31  
32   Reads data from `stream` via awaiting `stream.read_some` repeatedly 32   Reads data from `stream` via awaiting `stream.read_some` repeatedly
33   until: 33   until:
34   34  
35   @li either the entire buffer sequence @c buffers is filled, 35   @li either the entire buffer sequence @c buffers is filled,
36   @li or a contingency occurs. 36   @li or a contingency occurs.
37   37  
38   If `buffer_size(buffers) == 0` then no awaiting `stream.read_some` 38   If `buffer_size(buffers) == 0` then no awaiting `stream.read_some`
39   is performed. This is not a contingency. 39   is performed. This is not a contingency.
40   40  
41   @par Await-returns 41   @par Await-returns
42   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 42   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`.
43   43  
44   Upon a contingency, `n` represents the number of bytes read so far, 44   Upon a contingency, `n` represents the number of bytes read so far,
45   inclusive of the last partial read. 45   inclusive of the last partial read.
46   46  
47   Contingencies: 47   Contingencies:
48   48  
49   @li The first contingency reported from awaiting @c stream.read_some . 49   @li The first contingency reported from awaiting @c stream.read_some .
50   50  
51   Notable conditions: 51   Notable conditions:
52   52  
53   @li @c cond::canceled — Operation was cancelled, 53   @li @c cond::canceled — Operation was cancelled,
54   @li @c cond::eof — Stream reached end before `buffers` was filled. 54   @li @c cond::eof — Stream reached end before `buffers` was filled.
55   55  
56   @par Await-postcondition 56   @par Await-postcondition
57   `ec || n == buffer_size(buffers)`. 57   `ec || n == buffer_size(buffers)`.
58   58  
59   @param stream The stream to read from. If the lifetime of `stream` ends 59   @param stream The stream to read from. If the lifetime of `stream` ends
60   before the coroutine finishes, the behavior is undefined. 60   before the coroutine finishes, the behavior is undefined.
61   61  
62   @param buffers The buffer sequence to fill. If the lifetime of the buffer 62   @param buffers The buffer sequence to fill. If the lifetime of the buffer
63   sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined. 63   sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined.
64   64  
65   65  
66   @par Remarks 66   @par Remarks
67   Supports _IoAwaitable cancellation_. 67   Supports _IoAwaitable cancellation_.
68   68  
69   69  
70   @par Example 70   @par Example
71   71  
72   @code 72   @code
73   capy::task<> process_message(capy::ReadStream auto& stream) 73   capy::task<> process_message(capy::ReadStream auto& stream)
74   { 74   {
75   std::vector<char> header(16); // known header size for some protocol 75   std::vector<char> header(16); // known header size for some protocol
76   auto [ec, n] = co_await capy::read(stream, capy::mutable_buffer(header)); 76   auto [ec, n] = co_await capy::read(stream, capy::mutable_buffer(header));
77   if (ec == capy::cond::eof) 77   if (ec == capy::cond::eof)
78   co_return; // Connection closed 78   co_return; // Connection closed
79   if (ec) 79   if (ec)
80   throw std::system_error(ec); 80   throw std::system_error(ec);
81   81  
82   // at this point `header` contains exactly 16 bytes 82   // at this point `header` contains exactly 16 bytes
83   } 83   }
84   @endcode 84   @endcode
85   85  
86   @see ReadStream, MutableBufferSequence 86   @see ReadStream, MutableBufferSequence
87   */ 87   */
88   template <typename S, typename MB> 88   template <typename S, typename MB>
89   requires ReadStream<S> && MutableBufferSequence<MB> 89   requires ReadStream<S> && MutableBufferSequence<MB>
90   auto 90   auto
HITCBC 91   94 read(S& stream, MB buffers) -> 91   94 read(S& stream, MB buffers) ->
92   io_task<std::size_t> 92   io_task<std::size_t>
93   { 93   {
94   auto consuming = buffer_slice(buffers); 94   auto consuming = buffer_slice(buffers);
95   std::size_t const total_size = buffer_size(buffers); 95   std::size_t const total_size = buffer_size(buffers);
96   std::size_t total_read = 0; 96   std::size_t total_read = 0;
97   97  
98   while(total_read < total_size) 98   while(total_read < total_size)
99   { 99   {
100   auto [ec, n] = co_await stream.read_some(consuming.data()); 100   auto [ec, n] = co_await stream.read_some(consuming.data());
101   consuming.remove_prefix(n); 101   consuming.remove_prefix(n);
102   total_read += n; 102   total_read += n;
103   if(ec) 103   if(ec)
104   co_return {ec, total_read}; 104   co_return {ec, total_read};
105   } 105   }
106   106  
107   co_return {{}, total_read}; 107   co_return {{}, total_read};
HITCBC 108   188 } 108   188 }
109   109  
110   /** Read all data from a stream into a dynamic buffer. 110   /** Read all data from a stream into a dynamic buffer.
111   111  
112   @par Await-effects 112   @par Await-effects
113   113  
114   Reads data from `stream` via awaiting `stream.read_some` repeatedly 114   Reads data from `stream` via awaiting `stream.read_some` repeatedly
115   and appending the results to `dynbuf`, 115   and appending the results to `dynbuf`,
116   until a contingency occurs. 116   until a contingency occurs.
117   117  
118   Data is appended using prepare/commit semantics. 118   Data is appended using prepare/commit semantics.
119   The buffer grows with 1.5x factor when filled. 119   The buffer grows with 1.5x factor when filled.
120   120  
121   @par Await-returns 121   @par Await-returns
122   122  
123   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 123   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`.
124   124  
125   `n` represents the total number of bytes read, 125   `n` represents the total number of bytes read,
126   inclusive of the last partial read. 126   inclusive of the last partial read.
127   127  
128   Contingencies: 128   Contingencies:
129   129  
130   @li The first contingency, other than one matching to @c cond::eof, reported from awaiting @c stream.read_some . 130   @li The first contingency, other than one matching to @c cond::eof, reported from awaiting @c stream.read_some .
131   131  
132   @par Await-throws 132   @par Await-throws
133   `std::bad_alloc` when append to `dynbuf` fails. 133   `std::bad_alloc` when append to `dynbuf` fails.
134   134  
135   @param stream The stream to read from. If the lifetime of `stream` ends 135   @param stream The stream to read from. If the lifetime of `stream` ends
136   before the coroutine finishes, the behavior is undefined. 136   before the coroutine finishes, the behavior is undefined.
137   137  
138   @param dynbuf The dynamic buffer to append data to. If the lifetime of the buffer 138   @param dynbuf The dynamic buffer to append data to. If the lifetime of the buffer
139   sequence represented by `dynbuf` ends before the coroutine finishes, the behavior is undefined. 139   sequence represented by `dynbuf` ends before the coroutine finishes, the behavior is undefined.
140   140  
141   @param initial_amount Initial bytes to prepare (default 2048). 141   @param initial_amount Initial bytes to prepare (default 2048).
142   142  
143   143  
144   @par Remarks 144   @par Remarks
145   Supports _IoAwaitable cancellation_. 145   Supports _IoAwaitable cancellation_.
146   146  
147   @par Example 147   @par Example
148   148  
149   @code 149   @code
150   capy::task<std::string> read_body(capy::ReadStream auto& stream) 150   capy::task<std::string> read_body(capy::ReadStream auto& stream)
151   { 151   {
152   std::string body; 152   std::string body;
153   auto [ec, n] = co_await capy::read(stream, capy::dynamic_buffer(body)); 153   auto [ec, n] = co_await capy::read(stream, capy::dynamic_buffer(body));
154   if (ec) 154   if (ec)
155   throw std::system_error(ec); 155   throw std::system_error(ec);
156   return body; 156   return body;
157   } 157   }
158   @endcode 158   @endcode
159   159  
160   @see read_some, ReadStream, DynamicBufferParam 160   @see read_some, ReadStream, DynamicBufferParam
161   */ 161   */
162   template <typename S, typename DB> 162   template <typename S, typename DB>
163   requires ReadStream<S> && DynamicBufferParam<DB> 163   requires ReadStream<S> && DynamicBufferParam<DB>
164   auto 164   auto
HITCBC 165   80 read( 165   80 read(
166   S& stream, 166   S& stream,
167   DB&& dynbuf, 167   DB&& dynbuf,
168   std::size_t initial_amount = 2048) -> 168   std::size_t initial_amount = 2048) ->
169   io_task<std::size_t> 169   io_task<std::size_t>
170   { 170   {
171   std::size_t amount = initial_amount; 171   std::size_t amount = initial_amount;
172   std::size_t total_read = 0; 172   std::size_t total_read = 0;
173   for(;;) 173   for(;;)
174   { 174   {
175   auto mb = dynbuf.prepare(amount); 175   auto mb = dynbuf.prepare(amount);
176   auto const mb_size = buffer_size(mb); 176   auto const mb_size = buffer_size(mb);
177   auto [ec, n] = co_await stream.read_some(mb); 177   auto [ec, n] = co_await stream.read_some(mb);
178   dynbuf.commit(n); 178   dynbuf.commit(n);
179   total_read += n; 179   total_read += n;
180   if(ec == cond::eof) 180   if(ec == cond::eof)
181   co_return {{}, total_read}; 181   co_return {{}, total_read};
182   if(ec) 182   if(ec)
183   co_return {ec, total_read}; 183   co_return {ec, total_read};
184   if(n == mb_size) 184   if(n == mb_size)
185   amount = amount / 2 + amount; 185   amount = amount / 2 + amount;
186   } 186   }
HITCBC 187   160 } 187   160 }
188   188  
189   /** Read all data from a source into a dynamic buffer. 189   /** Read all data from a source into a dynamic buffer.
190   190  
191   @par Await-effects 191   @par Await-effects
192   192  
193   Reads data from `stream` by calling `source.read` repeatedly 193   Reads data from `stream` by calling `source.read` repeatedly
194   and appending it to `dynbuf` until a contingency occurs. 194   and appending it to `dynbuf` until a contingency occurs.
195   The last, potenitally partial, read is also appended. 195   The last, potenitally partial, read is also appended.
196   196  
197   Data is appended using prepare/commit semantics. 197   Data is appended using prepare/commit semantics.
198   The buffer grows with 1.5x factor when filled. 198   The buffer grows with 1.5x factor when filled.
199   199  
200   @par Await-returns 200   @par Await-returns
201   201  
202   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 202   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`.
203   203  
204   `n` represents the total number of bytes read, 204   `n` represents the total number of bytes read,
205   inclusive of the last partial read. 205   inclusive of the last partial read.
206   206  
207   207  
208   Contingencies: 208   Contingencies:
209   209  
210   @li The first contingency, other than one matching to @c cond::eof, reported from awaiting @c stream.read_some . 210   @li The first contingency, other than one matching to @c cond::eof, reported from awaiting @c stream.read_some .
211   211  
212   @par Await-throws 212   @par Await-throws
213   213  
214   `std::bad_alloc` when append to `dynbuf` fails. 214   `std::bad_alloc` when append to `dynbuf` fails.
215   215  
216   @param source The source to read from. If the lifetime of `source` ends 216   @param source The source to read from. If the lifetime of `source` ends
217   before the coroutine finishes, the behavior is undefined. 217   before the coroutine finishes, the behavior is undefined.
218   218  
219   @param dynbuf The dynamic buffer to append data to. If the lifetime of the 219   @param dynbuf The dynamic buffer to append data to. If the lifetime of the
220   buffer sequence represented by `dynbuf` ends before the coroutine finishes, 220   buffer sequence represented by `dynbuf` ends before the coroutine finishes,
221   the behavior is undefined. 221   the behavior is undefined.
222   222  
223   @param initial_amount Initial bytes to prepare (default 2048). 223   @param initial_amount Initial bytes to prepare (default 2048).
224   224  
225   @par Remarks 225   @par Remarks
226   Supports _IoAwaitable cancellation_. 226   Supports _IoAwaitable cancellation_.
227   227  
228   @par Example 228   @par Example
229   229  
230   @code 230   @code
231   capy::task<std::string> read_body(capy::ReadSource auto& source) 231   capy::task<std::string> read_body(capy::ReadSource auto& source)
232   { 232   {
233   std::string body; 233   std::string body;
234   auto [ec, n] = co_await capy::read(source, capy::dynamic_buffer(body)); 234   auto [ec, n] = co_await capy::read(source, capy::dynamic_buffer(body));
235   if (ec) 235   if (ec)
236   throw std::system_error(ec); 236   throw std::system_error(ec);
237   return body; 237   return body;
238   } 238   }
239   @endcode 239   @endcode
240   240  
241   @see ReadSource, DynamicBufferParam 241   @see ReadSource, DynamicBufferParam
242   */ 242   */
243   template <typename S, typename DB> 243   template <typename S, typename DB>
244   requires ReadSource<S> && DynamicBufferParam<DB> 244   requires ReadSource<S> && DynamicBufferParam<DB>
245   auto 245   auto
HITCBC 246   54 read( 246   54 read(
247   S& source, 247   S& source,
248   DB&& dynbuf, 248   DB&& dynbuf,
249   std::size_t initial_amount = 2048) -> 249   std::size_t initial_amount = 2048) ->
250   io_task<std::size_t> 250   io_task<std::size_t>
251   { 251   {
252   std::size_t amount = initial_amount; 252   std::size_t amount = initial_amount;
253   std::size_t total_read = 0; 253   std::size_t total_read = 0;
254   for(;;) 254   for(;;)
255   { 255   {
256   auto mb = dynbuf.prepare(amount); 256   auto mb = dynbuf.prepare(amount);
257   auto const mb_size = buffer_size(mb); 257   auto const mb_size = buffer_size(mb);
258   auto [ec, n] = co_await source.read(mb); 258   auto [ec, n] = co_await source.read(mb);
259   dynbuf.commit(n); 259   dynbuf.commit(n);
260   total_read += n; 260   total_read += n;
261   if(ec == cond::eof) 261   if(ec == cond::eof)
262   co_return {{}, total_read}; 262   co_return {{}, total_read};
263   if(ec) 263   if(ec)
264   co_return {ec, total_read}; 264   co_return {ec, total_read};
265   if(n == mb_size) 265   if(n == mb_size)
266   amount = amount / 2 + amount; // 1.5x growth 266   amount = amount / 2 + amount; // 1.5x growth
267   } 267   }
HITCBC 268   108 } 268   108 }
269   269  
270   } // namespace capy 270   } // namespace capy
271   } // namespace boost 271   } // namespace boost
272   272  
273   #endif 273   #endif