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