include/boost/capy/read_at_least.hpp

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