include/boost/capy/buffers/buffer_param.hpp

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