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 66   one at a time. This class maintains a window of up to
67   @ref max_size buffer descriptors, automatically refilling 67   @ref max_size buffer descriptors, automatically refilling
68   from the underlying sequence as buffers are consumed. 68   from the underlying sequence as buffers are consumed.
69   69  
70   @par Example 70   @par Example
71   71  
72   Create a `buffer_param` from any buffer sequence and use 72   Create a `buffer_param` from any buffer sequence and use
73   `data()` to get the current window of buffers. After 73   `data()` to get the current window of buffers. After
74   processing some bytes, call `consume()` to advance through 74   processing some bytes, call `consume()` to advance through
75   the sequence. 75   the sequence.
76   76  
77   @code 77   @code
78   task<> send(ConstBufferSequence auto buffers) 78   task<> send(ConstBufferSequence auto buffers)
79   { 79   {
80   buffer_param bp(buffers); 80   buffer_param bp(buffers);
81   while(true) 81   while(true)
82   { 82   {
83   auto bufs = bp.data(); 83   auto bufs = bp.data();
84   if(bufs.empty()) 84   if(bufs.empty())
85   break; 85   break;
86   auto n = co_await do_something(bufs); 86   auto n = co_await do_something(bufs);
87   bp.consume(n); 87   bp.consume(n);
88   } 88   }
89   } 89   }
90   @endcode 90   @endcode
91   91  
92   @par Virtual Interface Pattern 92   @par Virtual Interface Pattern
93   93  
94   This class enables passing arbitrary buffer sequences through 94   This class enables passing arbitrary buffer sequences through
95   a virtual function boundary. The template function captures 95   a virtual function boundary. The template function captures
96   the buffer sequence by value and drives the iteration, while 96   the buffer sequence by value and drives the iteration, while
97   the virtual function receives a simple span: 97   the virtual function receives a simple span:
98   98  
99   @code 99   @code
100   class base 100   class base
101   { 101   {
102   public: 102   public:
103   task<> write(ConstBufferSequence auto buffers) 103   task<> write(ConstBufferSequence auto buffers)
104   { 104   {
105   buffer_param bp(buffers); 105   buffer_param bp(buffers);
106   while(true) 106   while(true)
107   { 107   {
108   auto bufs = bp.data(); 108   auto bufs = bp.data();
109   if(bufs.empty()) 109   if(bufs.empty())
110   break; 110   break;
111   std::size_t n = 0; 111   std::size_t n = 0;
112   co_await write_impl(bufs, n); 112   co_await write_impl(bufs, n);
113   bp.consume(n); 113   bp.consume(n);
114   } 114   }
115   } 115   }
116   116  
117   protected: 117   protected:
118   virtual task<> write_impl( 118   virtual task<> write_impl(
119   std::span<const_buffer> buffers, 119   std::span<const_buffer> buffers,
120   std::size_t& bytes_written) = 0; 120   std::size_t& bytes_written) = 0;
121   }; 121   };
122   @endcode 122   @endcode
123   123  
124   @tparam BS The buffer sequence type. Must satisfy either 124   @tparam BS The buffer sequence type. Must satisfy either
125   ConstBufferSequence or MutableBufferSequence. 125   ConstBufferSequence or MutableBufferSequence.
126   126  
127   @see ConstBufferSequence, MutableBufferSequence 127   @see ConstBufferSequence, MutableBufferSequence
128   */ 128   */
129   template<class BS, bool MakeConst = false> 129   template<class BS, bool MakeConst = false>
130   requires ConstBufferSequence<BS> || MutableBufferSequence<BS> 130   requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
131   class buffer_param 131   class buffer_param
132   { 132   {
133   public: 133   public:
134   /// The buffer type (const_buffer or mutable_buffer) 134   /// The buffer type (const_buffer or mutable_buffer)
135   using buffer_type = std::conditional_t< 135   using buffer_type = std::conditional_t<
136   MakeConst, 136   MakeConst,
137   const_buffer, 137   const_buffer,
138   capy::buffer_type<BS>>; 138   capy::buffer_type<BS>>;
139   139  
140   private: 140   private:
141   decltype(begin(std::declval<BS const&>())) it_; 141   decltype(begin(std::declval<BS const&>())) it_;
142   decltype(end(std::declval<BS const&>())) end_; 142   decltype(end(std::declval<BS const&>())) end_;
143   union { 143   union {
144   int dummy_; 144   int dummy_;
145   buffer_type arr_[detail::max_iovec_]; 145   buffer_type arr_[detail::max_iovec_];
146   }; 146   };
147   std::size_t size_ = 0; 147   std::size_t size_ = 0;
148   std::size_t pos_ = 0; 148   std::size_t pos_ = 0;
149   149  
150   void 150   void
HITCBC 151   556 refill() 151   556 refill()
152   { 152   {
HITCBC 153   556 pos_ = 0; 153   556 pos_ = 0;
HITCBC 154   556 size_ = 0; 154   556 size_ = 0;
HITCBC 155   1610 for(; it_ != end_ && size_ < detail::max_iovec_; ++it_) 155   1610 for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
156   { 156   {
HITCBC 157   1054 buffer_type buf(*it_); 157   1054 buffer_type buf(*it_);
HITCBC 158   1054 if(buf.size() > 0) 158   1054 if(buf.size() > 0)
HITCBC 159   1016 ::new(&arr_[size_++]) buffer_type(buf); 159   1016 ::new(&arr_[size_++]) buffer_type(buf);
160   } 160   }
HITCBC 161   556 } 161   556 }
162   162  
163   public: 163   public:
164   /** Construct from a buffer sequence. 164   /** Construct from a buffer sequence.
165   165  
166   @param bs The buffer sequence to wrap. The caller must 166   @param bs The buffer sequence to wrap. The caller must
167   ensure the buffer sequence remains valid for the 167   ensure the buffer sequence remains valid for the
168   lifetime of this object. 168   lifetime of this object.
169   */ 169   */
170   explicit 170   explicit
HITCBC 171   401 buffer_param(BS const& bs) 171   401 buffer_param(BS const& bs)
HITCBC 172   401 : it_(begin(bs)) 172   401 : it_(begin(bs))
HITCBC 173   401 , end_(end(bs)) 173   401 , end_(end(bs))
HITCBC 174   401 , dummy_(0) 174   401 , dummy_(0)
175   { 175   {
HITCBC 176   401 refill(); 176   401 refill();
HITCBC 177   401 } 177   401 }
178   178  
179   /** Return the current window of buffer descriptors. 179   /** Return the current window of buffer descriptors.
180   180  
181   Returns a span of buffer descriptors representing the 181   Returns a span of buffer descriptors representing the
182   currently available portion of the buffer sequence. 182   currently available portion of the buffer sequence.
183   The span contains at most @ref max_size buffers. 183   The span contains at most @ref max_size buffers.
184   184  
185   When the current window is exhausted, this function 185   When the current window is exhausted, this function
186   automatically refills from the underlying sequence. 186   automatically refills from the underlying sequence.
187   187  
188   @return A span of buffer descriptors. Empty span 188   @return A span of buffer descriptors. Empty span
189   indicates no more data is available. 189   indicates no more data is available.
190   */ 190   */
191   std::span<buffer_type> 191   std::span<buffer_type>
HITCBC 192   521 data() 192   521 data()
193   { 193   {
HITCBC 194   521 if(pos_ >= size_) 194   521 if(pos_ >= size_)
HITCBC 195   155 refill(); 195   155 refill();
HITCBC 196   521 if(size_ == 0) 196   521 if(size_ == 0)
HITCBC 197   135 return {}; 197   135 return {};
HITCBC 198   386 return {arr_ + pos_, size_ - pos_}; 198   386 return {arr_ + pos_, size_ - pos_};
199   } 199   }
200   200  
201   /** Check if more buffers exist beyond the current window. 201   /** Check if more buffers exist beyond the current window.
202   202  
203   Returns `true` if the underlying buffer sequence has 203   Returns `true` if the underlying buffer sequence has
204   additional buffers that have not yet been loaded into 204   additional buffers that have not yet been loaded into
205   the current window. Call after @ref data to determine 205   the current window. Call after @ref data to determine
206   whether the current window is the last one. 206   whether the current window is the last one.
207   207  
208   @return `true` if more buffers remain in the sequence. 208   @return `true` if more buffers remain in the sequence.
209   */ 209   */
210   bool 210   bool
HITCBC 211   41 more() const noexcept 211   41 more() const noexcept
212   { 212   {
HITCBC 213   41 return it_ != end_; 213   41 return it_ != end_;
214   } 214   }
215   215  
216   /** Consume bytes from the buffer sequence. 216   /** Consume bytes from the buffer sequence.
217   217  
218   Advances the current position by `n` bytes, consuming 218   Advances the current position by `n` bytes, consuming
219   data from the front of the sequence. Partially consumed 219   data from the front of the sequence. Partially consumed
220   buffers are adjusted in place. 220   buffers are adjusted in place.
221   221  
222   @param n Number of bytes to consume. 222   @param n Number of bytes to consume.
223   */ 223   */
224   void 224   void
HITCBC 225   124 consume(std::size_t n) 225   124 consume(std::size_t n)
226   { 226   {
HITCBC 227   574 while(n > 0 && pos_ < size_) 227   574 while(n > 0 && pos_ < size_)
228   { 228   {
HITCBC 229   450 auto avail = arr_[pos_].size(); 229   450 auto avail = arr_[pos_].size();
HITCBC 230   450 if(n < avail) 230   450 if(n < avail)
231   { 231   {
HITCBC 232   5 arr_[pos_] += n; 232   5 arr_[pos_] += n;
HITCBC 233   5 n = 0; 233   5 n = 0;
234   } 234   }
235   else 235   else
236   { 236   {
HITCBC 237   445 n -= avail; 237   445 n -= avail;
HITCBC 238   445 ++pos_; 238   445 ++pos_;
239   } 239   }
240   } 240   }
HITCBC 241   124 } 241   124 }
242   }; 242   };
243   243  
244   // CTAD deduction guide 244   // CTAD deduction guide
245   template<class BS> 245   template<class BS>
246   buffer_param(BS const&) -> buffer_param<BS>; 246   buffer_param(BS const&) -> buffer_param<BS>;
247   247  
248   /// Alias for buffer_param that always uses const_buffer storage. 248   /// Alias for buffer_param that always uses const_buffer storage.
249   template<class BS> 249   template<class BS>
250   using const_buffer_param = buffer_param<BS, true>; 250   using const_buffer_param = buffer_param<BS, true>;
251   251  
252   } // namespace capy 252   } // namespace capy
253   } // namespace boost 253   } // namespace boost
254   254  
255   #endif 255   #endif