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