100.00% Lines (2/2) 100.00% Functions (1/1)
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   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_READ_HPP 11   #ifndef BOOST_CAPY_READ_HPP
12   #define BOOST_CAPY_READ_HPP 12   #define BOOST_CAPY_READ_HPP
13   13  
14   #include <boost/capy/detail/config.hpp> 14   #include <boost/capy/detail/config.hpp>
15   #include <boost/capy/cond.hpp> 15   #include <boost/capy/cond.hpp>
16   #include <boost/capy/io_task.hpp> 16   #include <boost/capy/io_task.hpp>
17   #include <boost/capy/buffers.hpp> 17   #include <boost/capy/buffers.hpp>
18   #include <boost/capy/buffers/consuming_buffers.hpp> 18   #include <boost/capy/buffers/consuming_buffers.hpp>
19   #include <boost/capy/concept/read_stream.hpp> 19   #include <boost/capy/concept/read_stream.hpp>
20   20  
21   #include <algorithm> 21   #include <algorithm>
22   #include <cstddef> 22   #include <cstddef>
23   23  
24   namespace boost { 24   namespace boost {
25   namespace capy { 25   namespace capy {
26   26  
27   /** Read data from a stream until the buffer sequence is full. 27   /** Read data from a stream until the buffer sequence is full.
28   28  
29   @par Await-effects 29   @par Await-effects
30   30  
31   Reads data from `stream` via awaiting `stream.read_some` repeatedly 31   Reads data from `stream` via awaiting `stream.read_some` repeatedly
32   until: 32   until:
33   33  
34   @li either the entire buffer sequence @c buffers is filled, 34   @li either the entire buffer sequence @c buffers is filled,
35   @li or a contingency occurs on `stream.read_some`. 35   @li or a contingency occurs on `stream.read_some`.
36   36  
37   If `buffer_size(buffers) == 0` then no awaiting `stream.read_some` 37   If `buffer_size(buffers) == 0` then no awaiting `stream.read_some`
38   is performed. This is not a contingency. 38   is performed. This is not a contingency.
39   39  
40   @par Await-returns 40   @par Await-returns
41   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 41   An object of type `io_result<std::size_t>` destructuring as `[ec, n]`.
42   42  
43   Upon a contingency, `n` represents the number of bytes read so far, 43   Upon a contingency, `n` represents the number of bytes read so far,
44   inclusive of the last partial read. 44   inclusive of the last partial read.
45   45  
46   Contingencies: 46   Contingencies:
47   47  
48   @li The first contingency reported from awaiting @c stream.read_some 48   @li The first contingency reported from awaiting @c stream.read_some
49   while `buffers` is not yet filled. A contingency that accompanies 49   while `buffers` is not yet filled. A contingency that accompanies
50   the read which fills `buffers` is not reported: a completed 50   the read which fills `buffers` is not reported: a completed
51   transfer is a success. 51   transfer is a success.
52   52  
53   Notable conditions: 53   Notable conditions:
54   54  
55   @li @c cond::canceled — Operation was cancelled, 55   @li @c cond::canceled — Operation was cancelled,
56   @li @c cond::eof — Stream reached end before @c buffers was filled. 56   @li @c cond::eof — Stream reached end before @c buffers was filled.
57   57  
58   @par Await-postcondition 58   @par Await-postcondition
59   If `n == buffer_size(buffers)` the transfer completed and `ec` is 59   If `n == buffer_size(buffers)` the transfer completed and `ec` is
60   success; otherwise `ec` is set. 60   success; otherwise `ec` is set.
61   61  
62   @param stream The stream to read from. If the lifetime of `stream` ends 62   @param stream The stream to read from. If the lifetime of `stream` ends
63   before the coroutine finishes, the behavior is undefined. 63   before the coroutine finishes, the behavior is undefined.
64   64  
65   @param buffers The buffer sequence to fill. If the lifetime of the buffer 65   @param buffers The buffer sequence to fill. If the lifetime of the buffer
66   sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined. 66   sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined.
67   67  
68   @return A task yielding `io_result<std::size_t>` whose second element 68   @return A task yielding `io_result<std::size_t>` whose second element
69   is the number of bytes read. 69   is the number of bytes read.
70   70  
71   @par Remarks 71   @par Remarks
72   Supports _IoAwaitable cancellation_. 72   Supports _IoAwaitable cancellation_.
73   73  
74   74  
75   @par Example 75   @par Example
76   76  
77   @code 77   @code
78   capy::task<> process_message(capy::ReadStream auto& stream) 78   capy::task<> process_message(capy::ReadStream auto& stream)
79   { 79   {
80   std::vector<char> header(16); // known header size for some protocol 80   std::vector<char> header(16); // known header size for some protocol
81   auto [ec, n] = co_await capy::read(stream, capy::make_buffer(header)); 81   auto [ec, n] = co_await capy::read(stream, capy::make_buffer(header));
82   if (ec == capy::cond::eof) 82   if (ec == capy::cond::eof)
83   co_return; // Connection closed 83   co_return; // Connection closed
84   if (ec) 84   if (ec)
85   throw std::system_error(ec); 85   throw std::system_error(ec);
86   86  
87   // at this point `header` contains exactly 16 bytes 87   // at this point `header` contains exactly 16 bytes
88   } 88   }
89   @endcode 89   @endcode
90   90  
91   @see ReadStream, MutableBufferSequence 91   @see ReadStream, MutableBufferSequence
92   */ 92   */
93   template <typename S, typename MB> 93   template <typename S, typename MB>
94   requires ReadStream<S> && MutableBufferSequence<MB> 94   requires ReadStream<S> && MutableBufferSequence<MB>
95   auto 95   auto
HITCBC 96   99 read(S& stream, MB buffers) -> 96   99 read(S& stream, MB buffers) ->
97   io_task<std::size_t> 97   io_task<std::size_t>
98   { 98   {
99   consuming_buffers consuming(buffers); 99   consuming_buffers consuming(buffers);
100   std::size_t const total_size = buffer_size(buffers); 100   std::size_t const total_size = buffer_size(buffers);
101   std::size_t total_read = 0; 101   std::size_t total_read = 0;
102   102  
103   while(total_read < total_size) 103   while(total_read < total_size)
104   { 104   {
105   auto [ec, n] = co_await stream.read_some(consuming.data()); 105   auto [ec, n] = co_await stream.read_some(consuming.data());
106   consuming.consume(n); 106   consuming.consume(n);
107   total_read += n; 107   total_read += n;
108   // A contingency that still completed the transfer is a success: 108   // A contingency that still completed the transfer is a success:
109   // report it only when the buffer was not filled. 109   // report it only when the buffer was not filled.
110   if(ec && total_read < total_size) 110   if(ec && total_read < total_size)
111   co_return {ec, total_read}; 111   co_return {ec, total_read};
112   } 112   }
113   113  
114   co_return {std::error_code(), total_read}; 114   co_return {std::error_code(), total_read};
HITCBC 115   198 } 115   198 }
116   116  
117   } // namespace capy 117   } // namespace capy
118   } // namespace boost 118   } // namespace boost
119   119  
120   #endif 120   #endif