TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/capy
9 : //
10 :
11 : #ifndef BOOST_CAPY_BUFFERS_CONSUMING_BUFFERS_HPP
12 : #define BOOST_CAPY_BUFFERS_CONSUMING_BUFFERS_HPP
13 :
14 : #include <boost/capy/detail/config.hpp>
15 : #include <boost/capy/buffers.hpp>
16 : #include <boost/capy/detail/slice_of.hpp>
17 :
18 : #include <cstddef>
19 : #include <utility>
20 :
21 : namespace boost {
22 : namespace capy {
23 :
24 : /** A cursor that drives consumption of a buffer sequence.
25 :
26 : `consuming_buffers` is the dedicated driver for `read_some`/`write_some`
27 : loops. It presents the not-yet-consumed bytes of a buffer sequence via
28 : `data()`, and `consume(n)` advances past `n` transferred bytes **in
29 : place**.
30 :
31 : It is deliberately **not** itself a buffer sequence — it hands out the
32 : remaining bytes through `data()` (returning a `slice_of` view). It
33 : **borrows** the underlying sequence (iterators + a consumed-byte offset).
34 : The sequence must outlive the cursor. That is the natural case when the
35 : cursor is a local of a composed operation that took its buffers by value.
36 :
37 : @par Example
38 : @code
39 : consuming_buffers consuming(buffers);
40 : std::size_t total = 0, want = buffer_size(buffers);
41 : while (total < want)
42 : {
43 : auto [ec, n] = co_await stream.read_some(consuming.data());
44 : consuming.consume(n);
45 : total += n;
46 : if (ec && total < want) co_return {ec, total};
47 : }
48 : @endcode
49 :
50 : @see buffer_slice, slice_of
51 : */
52 : template<class Seq>
53 : requires MutableBufferSequence<Seq> || ConstBufferSequence<Seq>
54 : class consuming_buffers
55 : {
56 : public:
57 : /// Names the buffer type the underlying sequence `Seq` yields.
58 : using buffer_type = capy::buffer_type<Seq>;
59 :
60 : private:
61 : using iterator_type =
62 : decltype(capy::begin(std::declval<Seq const&>()));
63 :
64 : iterator_type first_{};
65 : iterator_type last_{};
66 : std::size_t front_skip_ = 0; // bytes consumed from *first_
67 :
68 : public:
69 : /** Construct a cursor over `s`.
70 :
71 : @param s The sequence to consume. Must outlive the cursor.
72 : */
73 HIT 315 : explicit consuming_buffers(Seq const& s) noexcept
74 315 : : first_(capy::begin(s))
75 315 : , last_(capy::end(s))
76 : {
77 315 : }
78 :
79 : /** Reject construction from a temporary (the view would dangle).
80 :
81 : @param s The sequence that would be consumed.
82 : */
83 : consuming_buffers(Seq const&& s) = delete;
84 :
85 : /** Return the remaining (unconsumed) bytes as a buffer sequence.
86 :
87 : @return The bytes not yet consumed, as a buffer sequence.
88 : */
89 : detail::slice_of<Seq>
90 362 : data() const noexcept
91 : {
92 362 : return detail::slice_of<Seq>(first_, last_, front_skip_, 0);
93 : }
94 :
95 : /** Discard `n` bytes from the front, in place.
96 :
97 : Advances past `min(n, remaining)` bytes.
98 :
99 : @param n The number of bytes consumed.
100 : */
101 : void
102 272 : consume(std::size_t n) noexcept
103 : {
104 415 : while (n > 0 && first_ != last_)
105 : {
106 214 : std::size_t const sz = buffer_type(*first_).size();
107 214 : std::size_t const avail = sz - front_skip_;
108 214 : if (n < avail)
109 : {
110 71 : front_skip_ += n;
111 71 : return;
112 : }
113 143 : n -= avail;
114 143 : ++first_;
115 143 : front_skip_ = 0;
116 : }
117 : }
118 : };
119 :
120 : /** Deduce the sequence type from the constructor argument.
121 :
122 : @tparam Seq The buffer sequence type.
123 : */
124 : template<class Seq>
125 : consuming_buffers(Seq const&) -> consuming_buffers<Seq>;
126 :
127 : } // namespace capy
128 : } // namespace boost
129 :
130 : #endif
|