100.00% Lines (2/2) 100.00% Functions (1/1)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
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_AT_LEAST_HPP 10   #ifndef BOOST_CAPY_READ_AT_LEAST_HPP
11   #define BOOST_CAPY_READ_AT_LEAST_HPP 11   #define BOOST_CAPY_READ_AT_LEAST_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/results.hpp>
  16 + #include <boost/capy/task.hpp>
16   #include <boost/capy/buffers.hpp> 17   #include <boost/capy/buffers.hpp>
17   #include <boost/capy/buffers/consuming_buffers.hpp> 18   #include <boost/capy/buffers/consuming_buffers.hpp>
18   #include <boost/capy/concept/read_stream.hpp> 19   #include <boost/capy/concept/read_stream.hpp>
19   20  
20   #include <cstddef> 21   #include <cstddef>
21   #include <system_error> 22   #include <system_error>
22   23  
23   namespace boost { 24   namespace boost {
24   namespace capy { 25   namespace capy {
25   26  
26   /** Read at least a minimum number of bytes from a stream. 27   /** Read at least a minimum number of bytes from a stream.
27   28  
28   This is a straightforward extension of @ref read. While @ref read 29   This is a straightforward extension of @ref read. While @ref read
29   transfers exactly `buffer_size(buffers)` bytes, `read_at_least` 30   transfers exactly `buffer_size(buffers)` bytes, `read_at_least`
30   transfers at least `n` bytes. The loop stops as soon as `n` bytes 31   transfers at least `n` bytes. The loop stops as soon as `n` bytes
31   have been read, even if `buffers` is not yet full. Any bytes beyond 32   have been read, even if `buffers` is not yet full. Any bytes beyond
32   `n` that a single `stream.read_some` happens to deliver are kept, up 33   `n` that a single `stream.read_some` happens to deliver are kept, up
33   to the capacity of `buffers`. No further awaiting is performed to 34   to the capacity of `buffers`. No further awaiting is performed to
34   fill the remainder. 35   fill the remainder.
35   36  
36   This is useful when a caller has a required amount of data `n` that 37   This is useful when a caller has a required amount of data `n` that
37   must be met or exceeded. The subsequent capacity of `buffers` is then 38   must be met or exceeded. The subsequent capacity of `buffers` is then
38   optional, and filling it should not block. 39   optional, and filling it should not block.
39   40  
40   @par Await-effects 41   @par Await-effects
41   42  
42   If `n > buffer_size(buffers)` the request is impossible to satisfy 43   If `n > buffer_size(buffers)` the request is impossible to satisfy
43   and the operation fails immediately with 44   and the operation fails immediately with
44   `{std::errc::invalid_argument, 0}` without awaiting `stream.read_some`. 45   `{std::errc::invalid_argument, 0}` without awaiting `stream.read_some`.
45   46  
46   Otherwise reads data from `stream` via awaiting `stream.read_some` 47   Otherwise reads data from `stream` via awaiting `stream.read_some`
47   repeatedly until: 48   repeatedly until:
48   49  
49   @li either at least `n` bytes have been read, 50   @li either at least `n` bytes have been read,
50   @li or a contingency occurs on `stream.read_some`. 51   @li or a contingency occurs on `stream.read_some`.
51   52  
52   If `n == 0` then no awaiting `stream.read_some` is performed. This is 53   If `n == 0` then no awaiting `stream.read_some` is performed. This is
53   not a contingency. 54   not a contingency.
54   55  
55   @par Await-returns 56   @par Await-returns
56 - An object of type `io_result<std::size_t>` destructuring as `[ec, n]`. 57 + An object of type `results<std::size_t>` destructuring as `[ec, n]`.
57   58  
58   Upon a contingency, the count represents the number of bytes read so 59   Upon a contingency, the count represents the number of bytes read so
59   far, inclusive of the last partial read. 60   far, inclusive of the last partial read.
60   61  
61   Contingencies: 62   Contingencies:
62   63  
63   @li The first contingency reported from awaiting @c stream.read_some 64   @li The first contingency reported from awaiting @c stream.read_some
64   while fewer than `n` bytes have been read. A contingency that 65   while fewer than `n` bytes have been read. A contingency that
65   accompanies the read which reaches `n` is not reported: a 66   accompanies the read which reaches `n` is not reported: a
66   satisfied request is a success. 67   satisfied request is a success.
67   68  
68   Notable conditions: 69   Notable conditions:
69   70  
70   @li @c std::errc::invalid_argument — `n` exceeds `buffer_size(buffers)`, 71   @li @c std::errc::invalid_argument — `n` exceeds `buffer_size(buffers)`,
71   @li @c cond::canceled — Operation was cancelled, 72   @li @c cond::canceled — Operation was cancelled,
72   @li @c cond::eof — Stream reached end before `n` bytes were read. 73   @li @c cond::eof — Stream reached end before `n` bytes were read.
73   74  
74   @par Await-postcondition 75   @par Await-postcondition
75   On success the returned count is greater than or equal to `n` and 76   On success the returned count is greater than or equal to `n` and
76   less than or equal to `buffer_size(buffers)`, and `ec` is success. 77   less than or equal to `buffer_size(buffers)`, and `ec` is success.
77   Otherwise `ec` is set. 78   Otherwise `ec` is set.
78   79  
79   @param stream The stream to read from. If the lifetime of `stream` ends 80   @param stream The stream to read from. If the lifetime of `stream` ends
80   before the coroutine finishes, the behavior is undefined. 81   before the coroutine finishes, the behavior is undefined.
81   82  
82   @param buffers The buffer sequence to read into. If the lifetime of the 83   @param buffers The buffer sequence to read into. If the lifetime of the
83   buffer sequence represented by `buffers` ends before the coroutine 84   buffer sequence represented by `buffers` ends before the coroutine
84   finishes, the behavior is undefined. 85   finishes, the behavior is undefined.
85   86  
86   @param n The minimum number of bytes to read. Must not exceed 87   @param n The minimum number of bytes to read. Must not exceed
87   `buffer_size(buffers)`. 88   `buffer_size(buffers)`.
88   89  
89 - @return A task yielding `io_result<std::size_t>` whose second element 90 + @return A task yielding `results<std::size_t>` whose second element
90   is the number of bytes read. 91   is the number of bytes read.
91   92  
92   @par Remarks 93   @par Remarks
93 - Supports _IoAwaitable cancellation_. 94 + Supports _ContextAwaitable cancellation_.
94   95  
95   @par Example 96   @par Example
96   97  
97   @par !example example 98   @par !example example
98   99  
99   100  
100   @see read, ReadStream, MutableBufferSequence 101   @see read, ReadStream, MutableBufferSequence
101   */ 102   */
102   template <typename S, typename MB> 103   template <typename S, typename MB>
103   requires ReadStream<S> && MutableBufferSequence<MB> 104   requires ReadStream<S> && MutableBufferSequence<MB>
104   auto 105   auto
HITCBC 105   34 read_at_least(S& stream, MB buffers, std::size_t n) -> 106   34 read_at_least(S& stream, MB buffers, std::size_t n) ->
106 - io_task<std::size_t> 107 + task<results<std::size_t>>
107   { 108   {
108   consuming_buffers consuming(buffers); 109   consuming_buffers consuming(buffers);
109   std::size_t const total_size = buffer_size(buffers); 110   std::size_t const total_size = buffer_size(buffers);
110   111  
111   if(n > total_size) 112   if(n > total_size)
112   co_return {make_error_code(std::errc::invalid_argument), 0}; 113   co_return {make_error_code(std::errc::invalid_argument), 0};
113   114  
114   std::size_t total_read = 0; 115   std::size_t total_read = 0;
115   116  
116   while(total_read < n) 117   while(total_read < n)
117   { 118   {
118   auto [ec, m] = co_await stream.read_some(consuming.data()); 119   auto [ec, m] = co_await stream.read_some(consuming.data());
119   consuming.consume(m); 120   consuming.consume(m);
120   total_read += m; 121   total_read += m;
121   // A contingency that still satisfied the request is a success: 122   // A contingency that still satisfied the request is a success:
122   // report it only when fewer than n bytes were read. 123   // report it only when fewer than n bytes were read.
123   if(ec && total_read < n) 124   if(ec && total_read < n)
124   co_return {ec, total_read}; 125   co_return {ec, total_read};
125   } 126   }
126   127  
127   co_return {std::error_code(), total_read}; 128   co_return {std::error_code(), total_read};
HITCBC 128   68 } 129   68 }
129   130  
130   } // namespace capy 131   } // namespace capy
131   } // namespace boost 132   } // namespace boost
132   133  
133   #endif 134   #endif