libs/capy/include/boost/capy/buffers/buffer_param.hpp

100.0% Lines (28/28) 100.0% Functions (32/32) 90.9% Branches (20/22)
libs/capy/include/boost/capy/buffers/buffer_param.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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 /*
11 COROUTINE BUFFER SEQUENCE LIFETIME REQUIREMENT
12 ===============================================
13 Buffer sequence parameters in coroutine APIs MUST be passed BY VALUE,
14 never by reference. When a coroutine suspends, reference parameters may
15 dangle if the caller's object goes out of scope before resumption.
16
17 CORRECT: task<> read_some(MutableBufferSequence auto buffers)
18 WRONG: task<> read_some(MutableBufferSequence auto& buffers)
19 WRONG: task<> read_some(MutableBufferSequence auto const& buffers)
20
21 The buffer_param class works with this model: it takes a const& in its
22 constructor (for the non-coroutine scope) but the caller's template
23 function accepts the buffer sequence by value, ensuring the sequence
24 lives in the coroutine frame.
25 */
26
27 #ifndef BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
28 #define BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
29
30 #include <boost/capy/detail/config.hpp>
31 #include <boost/capy/buffers.hpp>
32
33 #include <span>
34
35 namespace boost {
36 namespace capy {
37
38 /** A buffer sequence wrapper providing windowed access.
39
40 This template class wraps any buffer sequence and provides
41 incremental access through a sliding window of buffer
42 descriptors. It handles both const and mutable buffer
43 sequences automatically.
44
45 @par Coroutine Lifetime Requirement
46
47 When used in coroutine APIs, the outer template function
48 MUST accept the buffer sequence parameter BY VALUE:
49
50 @code
51 task<> write(ConstBufferSequence auto buffers); // CORRECT
52 task<> write(ConstBufferSequence auto& buffers); // WRONG - dangling reference
53 @endcode
54
55 Pass-by-value ensures the buffer sequence is copied into
56 the coroutine frame and remains valid across suspension
57 points. References would dangle when the caller's scope
58 exits before the coroutine resumes.
59
60 @par Purpose
61
62 When iterating through large buffer sequences, it is often
63 more efficient to process buffers in batches rather than
64 one at a time. This class maintains a window of up to
65 @ref max_size buffer descriptors, automatically refilling
66 from the underlying sequence as buffers are consumed.
67
68 @par Usage
69
70 Create a `buffer_param` from any buffer sequence and use
71 `data()` to get the current window of buffers. After
72 processing some bytes, call `consume()` to advance through
73 the sequence.
74
75 @code
76 task<> send(ConstBufferSequence auto buffers)
77 {
78 buffer_param bp(buffers);
79 while(true)
80 {
81 auto bufs = bp.data();
82 if(bufs.empty())
83 break;
84 auto n = co_await do_something(bufs);
85 bp.consume(n);
86 }
87 }
88 @endcode
89
90 @par Virtual Interface Pattern
91
92 This class enables passing arbitrary buffer sequences through
93 a virtual function boundary. The template function captures
94 the buffer sequence by value and drives the iteration, while
95 the virtual function receives a simple span:
96
97 @code
98 class base
99 {
100 public:
101 task<> write(ConstBufferSequence auto buffers)
102 {
103 buffer_param bp(buffers);
104 while(true)
105 {
106 auto bufs = bp.data();
107 if(bufs.empty())
108 break;
109 std::size_t n = 0;
110 co_await write_impl(bufs, n);
111 bp.consume(n);
112 }
113 }
114
115 protected:
116 virtual task<> write_impl(
117 std::span<const_buffer> buffers,
118 std::size_t& bytes_written) = 0;
119 };
120 @endcode
121
122 @tparam BS The buffer sequence type. Must satisfy either
123 ConstBufferSequence or MutableBufferSequence.
124
125 @see ConstBufferSequence, MutableBufferSequence
126 */
127 template<class BS>
128 requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
129 class buffer_param
130 {
131 public:
132 /// The buffer type (const_buffer or mutable_buffer)
133 using buffer_type = capy::buffer_type<BS>;
134
135 private:
136 decltype(begin(std::declval<BS const&>())) it_;
137 decltype(end(std::declval<BS const&>())) end_;
138 buffer_type arr_[detail::max_iovec_];
139 std::size_t size_ = 0;
140 std::size_t pos_ = 0;
141
142 void
143 435 refill()
144 {
145 435 pos_ = 0;
146 435 size_ = 0;
147
9/10
✓ Branch 0 taken 269 times.
✓ Branch 1 taken 316 times.
✓ Branch 2 taken 182 times.
✓ Branch 2 taken 269 times.
✓ Branch 3 taken 116 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 179 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 179 times.
✓ Branch 7 taken 119 times.
883 for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
148 {
149 448 buffer_type buf(*it_);
150
2/2
✓ Branch 1 taken 436 times.
✓ Branch 2 taken 12 times.
448 if(buf.size() > 0)
151 436 arr_[size_++] = buf;
152 }
153 435 }
154
155 public:
156 /** Construct from a buffer sequence.
157
158 @param bs The buffer sequence to wrap. The caller must
159 ensure the buffer sequence remains valid for the
160 lifetime of this object.
161 */
162 explicit
163 345 buffer_param(BS const& bs)
164 345 : it_(begin(bs))
165 345 , end_(end(bs))
166 {
167 345 refill();
168 345 }
169
170 /** Return the current window of buffer descriptors.
171
172 Returns a span of buffer descriptors representing the
173 currently available portion of the buffer sequence.
174 The span contains at most @ref max_size buffers.
175
176 When the current window is exhausted, this function
177 automatically refills from the underlying sequence.
178
179 @return A span of buffer descriptors. Empty span
180 indicates no more data is available.
181 */
182 std::span<buffer_type>
183 487 data()
184 {
185
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 267 times.
487 if(pos_ >= size_)
186 90 refill();
187
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 270 times.
487 if(size_ == 0)
188 87 return {};
189 400 return {arr_ + pos_, size_ - pos_};
190 }
191
192 /** Consume bytes from the buffer sequence.
193
194 Advances the current position by `n` bytes, consuming
195 data from the front of the sequence. Partially consumed
196 buffers are adjusted in place.
197
198 @param n Number of bytes to consume.
199 */
200 void
201 146 consume(std::size_t n)
202 {
203
3/4
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 146 times.
✓ Branch 2 taken 205 times.
✗ Branch 3 not taken.
351 while(n > 0 && pos_ < size_)
204 {
205 205 auto avail = arr_[pos_].size();
206
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 140 times.
205 if(n < avail)
207 {
208 65 arr_[pos_] += n;
209 65 n = 0;
210 }
211 else
212 {
213 140 n -= avail;
214 140 ++pos_;
215 }
216 }
217 146 }
218 };
219
220 // CTAD deduction guide
221 template<class BS>
222 buffer_param(BS const&) -> buffer_param<BS>;
223
224 } // namespace capy
225 } // namespace boost
226
227 #endif
228