include/boost/capy/read_at_least.hpp

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