include/boost/capy/buffers/buffer_param.hpp

100.0% Lines (31/0/31) 100.0% List of functions (18/0/18)
buffer_param.hpp
f(x) Functions (18)
Function Calls Lines Blocks
boost::capy::buffer_param<boost::capy::const_buffer, false>::refill() :158 8x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::refill() :158 1x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::refill() :158 16x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::refill() :158 3x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::buffer_param(boost::capy::const_buffer const&) :178 5x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::buffer_param(boost::capy::mutable_buffer const&) :178 1x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::buffer_param(std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> > const&) :178 7x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::buffer_param(std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> > const&) :178 2x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::data() :200 9x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::data() :200 2x 66.7% 60.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::data() :200 14x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::data() :200 2x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::more() const :219 1x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::more() const :219 4x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::consume(unsigned long) :233 5x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::consume(unsigned long) :233 2x 77.8% 90.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::consume(unsigned long) :233 8x 77.8% 80.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::consume(unsigned long) :233 1x 77.8% 80.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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 /*
12 COROUTINE BUFFER SEQUENCE LIFETIME REQUIREMENT
13 ===============================================
14 Buffer sequence parameters in coroutine APIs MUST be passed BY VALUE,
15 never by reference. When a coroutine suspends, reference parameters may
16 dangle if the caller's object goes out of scope before resumption.
17
18 CORRECT: task<> read_some(MutableBufferSequence auto buffers)
19 WRONG: task<> read_some(MutableBufferSequence auto& buffers)
20 WRONG: task<> read_some(MutableBufferSequence auto const& buffers)
21
22 The buffer_param class works with this model: it takes a const& in its
23 constructor (for the non-coroutine scope) but the caller's template
24 function accepts the buffer sequence by value, ensuring the sequence
25 lives in the coroutine frame.
26 */
27
28 #ifndef BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
29 #define BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
30
31 #include <boost/capy/detail/config.hpp>
32 #include <boost/capy/buffers.hpp>
33
34 #include <new>
35 #include <span>
36 #include <type_traits>
37
38 namespace boost {
39 namespace capy {
40
41 /** A buffer sequence wrapper providing windowed access.
42
43 This template class wraps any buffer sequence and provides
44 incremental access through a sliding window of buffer
45 descriptors. It handles both const and mutable buffer
46 sequences automatically.
47
48 @par Coroutine Lifetime Requirement
49
50 When used in coroutine APIs, the outer template function
51 MUST accept the buffer sequence parameter BY VALUE:
52
53 @code
54 task<> write(ConstBufferSequence auto buffers); // CORRECT
55 task<> write(ConstBufferSequence auto& buffers); // WRONG - dangling reference
56 @endcode
57
58 Pass-by-value ensures the buffer sequence is copied into
59 the coroutine frame and remains valid across suspension
60 points. References would dangle when the caller's scope
61 exits before the coroutine resumes.
62
63 @par Purpose
64
65 When iterating through large buffer sequences, it is often
66 more efficient to process buffers in batches rather than
67 one at a time. This class maintains a window of up to a
68 fixed, implementation-defined number of buffer descriptors
69 (currently 16). It refills the window from the underlying
70 sequence as buffers are consumed.
71
72 @par Example
73
74 Create a `buffer_param` from any buffer sequence and use
75 `data()` to get the current window of buffers. After
76 processing some bytes, call `consume()` to advance through
77 the sequence.
78
79 @code
80 task<> send(ConstBufferSequence auto buffers)
81 {
82 buffer_param bp(buffers);
83 while(true)
84 {
85 auto bufs = bp.data();
86 if(bufs.empty())
87 break;
88 auto n = co_await do_something(bufs);
89 bp.consume(n);
90 }
91 }
92 @endcode
93
94 @par Virtual Interface Pattern
95
96 This class enables passing arbitrary buffer sequences through
97 a virtual function boundary. The template function captures
98 the buffer sequence by value and drives the iteration, while
99 the virtual function receives a simple span. Plain CTAD
100 (`buffer_param bp(buffers)`) deduces `BS`'s own buffer type, so a
101 mutable sequence yields `span<mutable_buffer>`. That does not match
102 `write_impl`'s `span<const_buffer>` parameter. Use @ref const_buffer_param
103 to force `const_buffer` storage regardless of what `BS` is:
104
105 @code
106 class base
107 {
108 public:
109 template<ConstBufferSequence BS>
110 task<> write(BS buffers)
111 {
112 const_buffer_param<BS> bp(buffers);
113 while(true)
114 {
115 auto bufs = bp.data();
116 if(bufs.empty())
117 break;
118 std::size_t n = 0;
119 co_await write_impl(bufs, n);
120 bp.consume(n);
121 }
122 }
123
124 protected:
125 virtual task<> write_impl(
126 std::span<const_buffer> buffers,
127 std::size_t& bytes_written) = 0;
128 };
129 @endcode
130
131 @tparam BS The buffer sequence type. Must satisfy either
132 ConstBufferSequence or MutableBufferSequence.
133
134 @see ConstBufferSequence, MutableBufferSequence
135 */
136 template<class BS, bool MakeConst = false>
137 requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
138 class buffer_param
139 {
140 public:
141 /// Names `const_buffer` when `MakeConst`, else `BS`'s own buffer type.
142 using buffer_type = std::conditional_t<
143 MakeConst,
144 const_buffer,
145 capy::buffer_type<BS>>;
146
147 private:
148 decltype(begin(std::declval<BS const&>())) it_;
149 decltype(end(std::declval<BS const&>())) end_;
150 union {
151 int dummy_;
152 buffer_type arr_[detail::max_iovec_];
153 };
154 std::size_t size_ = 0;
155 std::size_t pos_ = 0;
156
157 void
158 28x refill()
159 {
160 28x pos_ = 0;
161 28x size_ = 0;
162 128x for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
163 {
164 100x buffer_type buf(*it_);
165 100x if(buf.size() > 0)
166 96x ::new(&arr_[size_++]) buffer_type(buf);
167 }
168 28x }
169
170 public:
171 /** Construct from a buffer sequence.
172
173 @param bs The buffer sequence to wrap. The caller must
174 ensure the buffer sequence remains valid for the
175 lifetime of this object.
176 */
177 explicit
178 15x buffer_param(BS const& bs)
179 15x : it_(begin(bs))
180 15x , end_(end(bs))
181 15x , dummy_(0)
182 {
183 15x refill();
184 15x }
185
186 /** Return the current window of buffer descriptors.
187
188 Returns a span of buffer descriptors representing the
189 currently available portion of the buffer sequence.
190 The span contains at most a fixed, implementation-defined
191 number of buffers (currently 16).
192
193 When the current window is exhausted, this function
194 automatically refills from the underlying sequence.
195
196 @return A span of buffer descriptors. Empty span
197 indicates no more data is available.
198 */
199 std::span<buffer_type>
200 27x data()
201 {
202 27x if(pos_ >= size_)
203 13x refill();
204 27x if(size_ == 0)
205 9x return {};
206 18x return {arr_ + pos_, size_ - pos_};
207 }
208
209 /** Check if more buffers exist beyond the current window.
210
211 Returns `true` if the underlying buffer sequence has
212 additional buffers that have not yet been loaded into
213 the current window. Call after @ref data to determine
214 whether the current window is the last one.
215
216 @return `true` if more buffers remain in the sequence.
217 */
218 bool
219 5x more() const noexcept
220 {
221 5x return it_ != end_;
222 }
223
224 /** Consume bytes from the buffer sequence.
225
226 Advances the current position by `n` bytes, consuming
227 data from the front of the sequence. Partially consumed
228 buffers are adjusted in place.
229
230 @param n Number of bytes to consume.
231 */
232 void
233 16x consume(std::size_t n)
234 {
235 98x while(n > 0 && pos_ < size_)
236 {
237 82x auto avail = arr_[pos_].size();
238 82x if(n < avail)
239 {
240 5x arr_[pos_] += n;
241 5x n = 0;
242 }
243 else
244 {
245 77x n -= avail;
246 77x ++pos_;
247 }
248 }
249 16x }
250 };
251
252 /** Deduce the sequence type from the constructor argument.
253
254 @tparam BS The buffer sequence type.
255 */
256 template<class BS>
257 buffer_param(BS const&) -> buffer_param<BS>;
258
259 /// Forces `buffer_param` to store windows as `const_buffer`, regardless of `BS`.
260 template<class BS>
261 using const_buffer_param = buffer_param<BS, true>;
262
263 } // namespace capy
264 } // namespace boost
265
266 #endif
267