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