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 @code
98 capy::task<> fill_buffer(capy::ReadStream auto& stream)
99 {
100 std::vector<char> storage(4096); // generous capacity
101 // Require 16 header bytes; opportunistically take more.
102 auto [ec, n] = co_await capy::read_at_least(
103 stream, capy::make_buffer(storage), 16);
104 if(ec)
105 throw std::system_error(ec);
106
107 // at least 16 bytes are available; n may be larger
108 }
109 @endcode
110
111 @see read, ReadStream, MutableBufferSequence
112 */
113 template <typename S, typename MB>
114 requires ReadStream<S> && MutableBufferSequence<MB>
115 auto
116 34x read_at_least(S& stream, MB buffers, std::size_t n) ->
117 io_task<std::size_t>
118 {
119 consuming_buffers consuming(buffers);
120 std::size_t const total_size = buffer_size(buffers);
121
122 if(n > total_size)
123 co_return {make_error_code(std::errc::invalid_argument), 0};
124
125 std::size_t total_read = 0;
126
127 while(total_read < n)
128 {
129 auto [ec, m] = co_await stream.read_some(consuming.data());
130 consuming.consume(m);
131 total_read += m;
132 // A contingency that still satisfied the request is a success:
133 // report it only when fewer than n bytes were read.
134 if(ec && total_read < n)
135 co_return {ec, total_read};
136 }
137
138 co_return {{}, total_read};
139 68x }
140
141 } // namespace capy
142 } // namespace boost
143
144 #endif
145