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