LCOV - code coverage report
Current view: top level - capy/buffers - buffer_param.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 31 31
Test Date: 2026-08-14 20:51:18 Functions: 100.0 % 18 18

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

Generated by: LCOV version 2.3