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