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