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() :121 8x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::refill() :121 1x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::refill() :121 16x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::refill() :121 3x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::buffer_param(boost::capy::const_buffer const&) :141 5x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::buffer_param(boost::capy::mutable_buffer const&) :141 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&) :141 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&) :141 2x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::data() :163 9x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::data() :163 2x 66.7% 60.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::data() :163 14x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::mutable_buffer, std::allocator<boost::capy::mutable_buffer> >, false>::data() :163 2x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::more() const :182 1x 100.0% 100.0% boost::capy::buffer_param<std::vector<boost::capy::const_buffer, std::allocator<boost::capy::const_buffer> >, false>::more() const :182 4x 100.0% 100.0% boost::capy::buffer_param<boost::capy::const_buffer, false>::consume(unsigned long) :196 5x 100.0% 100.0% boost::capy::buffer_param<boost::capy::mutable_buffer, false>::consume(unsigned long) :196 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) :196 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) :196 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 @par !example example_1
54
55
56 Pass-by-value ensures the buffer sequence is copied into
57 the coroutine frame and remains valid across suspension
58 points. References would dangle when the caller's scope
59 exits before the coroutine resumes.
60
61 @par Purpose
62
63 When iterating through large buffer sequences, it is often
64 more efficient to process buffers in batches rather than
65 one at a time. This class maintains a window of up to a
66 fixed, implementation-defined number of buffer descriptors
67 (currently 16). It refills the window from the underlying
68 sequence as buffers are consumed.
69
70 @par Example
71
72 Create a `buffer_param` from any buffer sequence and use
73 `data()` to get the current window of buffers. After
74 processing some bytes, call `consume()` to advance through
75 the sequence.
76
77 @par !example example_2
78
79
80 @par Virtual Interface Pattern
81
82 This class enables passing arbitrary buffer sequences through
83 a virtual function boundary. The template function captures
84 the buffer sequence by value and drives the iteration, while
85 the virtual function receives a simple span. Plain CTAD
86 (`buffer_param bp(buffers)`) deduces `BS`'s own buffer type, so a
87 mutable sequence yields `span<mutable_buffer>`. That does not match
88 `write_impl`'s `span<const_buffer>` parameter. Use @ref const_buffer_param
89 to force `const_buffer` storage regardless of what `BS` is:
90
91 @par !example example_3
92
93
94 @tparam BS The buffer sequence type. Must satisfy either
95 ConstBufferSequence or MutableBufferSequence.
96
97 @see ConstBufferSequence, MutableBufferSequence
98 */
99 template<class BS, bool MakeConst = false>
100 requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
101 class buffer_param
102 {
103 public:
104 /// Names `const_buffer` when `MakeConst`, else `BS`'s own buffer type.
105 using buffer_type = std::conditional_t<
106 MakeConst,
107 const_buffer,
108 capy::buffer_type<BS>>;
109
110 private:
111 decltype(begin(std::declval<BS const&>())) it_;
112 decltype(end(std::declval<BS const&>())) end_;
113 union {
114 int dummy_;
115 buffer_type arr_[detail::max_iovec_];
116 };
117 std::size_t size_ = 0;
118 std::size_t pos_ = 0;
119
120 void
121 28x refill()
122 {
123 28x pos_ = 0;
124 28x size_ = 0;
125 128x for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
126 {
127 100x buffer_type buf(*it_);
128 100x if(buf.size() > 0)
129 96x ::new(&arr_[size_++]) buffer_type(buf);
130 }
131 28x }
132
133 public:
134 /** Construct from a buffer sequence.
135
136 @param bs The buffer sequence to wrap. The caller must
137 ensure the buffer sequence remains valid for the
138 lifetime of this object.
139 */
140 explicit
141 15x buffer_param(BS const& bs)
142 15x : it_(begin(bs))
143 15x , end_(end(bs))
144 15x , dummy_(0)
145 {
146 15x refill();
147 15x }
148
149 /** Return the current window of buffer descriptors.
150
151 Returns a span of buffer descriptors representing the
152 currently available portion of the buffer sequence.
153 The span contains at most a fixed, implementation-defined
154 number of buffers (currently 16).
155
156 When the current window is exhausted, this function
157 automatically refills from the underlying sequence.
158
159 @return A span of buffer descriptors. Empty span
160 indicates no more data is available.
161 */
162 std::span<buffer_type>
163 27x data()
164 {
165 27x if(pos_ >= size_)
166 13x refill();
167 27x if(size_ == 0)
168 9x return {};
169 18x return {arr_ + pos_, size_ - pos_};
170 }
171
172 /** Check if more buffers exist beyond the current window.
173
174 Returns `true` if the underlying buffer sequence has
175 additional buffers that have not yet been loaded into
176 the current window. Call after @ref data to determine
177 whether the current window is the last one.
178
179 @return `true` if more buffers remain in the sequence.
180 */
181 bool
182 5x more() const noexcept
183 {
184 5x return it_ != end_;
185 }
186
187 /** Consume bytes from the buffer sequence.
188
189 Advances the current position by `n` bytes, consuming
190 data from the front of the sequence. Partially consumed
191 buffers are adjusted in place.
192
193 @param n Number of bytes to consume.
194 */
195 void
196 16x consume(std::size_t n)
197 {
198 98x while(n > 0 && pos_ < size_)
199 {
200 82x auto avail = arr_[pos_].size();
201 82x if(n < avail)
202 {
203 5x arr_[pos_] += n;
204 5x n = 0;
205 }
206 else
207 {
208 77x n -= avail;
209 77x ++pos_;
210 }
211 }
212 16x }
213 };
214
215 /** Deduce the sequence type from the constructor argument.
216
217 @tparam BS The buffer sequence type.
218 */
219 template<class BS>
220 buffer_param(BS const&) -> buffer_param<BS>;
221
222 /// Forces `buffer_param` to store windows as `const_buffer`, regardless of `BS`.
223 template<class BS>
224 using const_buffer_param = buffer_param<BS, true>;
225
226 } // namespace capy
227 } // namespace boost
228
229 #endif
230