LCOV - code coverage report
Current view: top level - boost/capy/buffers - buffer_param.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 28 28
Test Date: 2026-01-30 23:43:15 Functions: 100.0 % 36 36

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3              : //
       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)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/capy
       8              : //
       9              : 
      10              : /*
      11              :     COROUTINE BUFFER SEQUENCE LIFETIME REQUIREMENT
      12              :     ===============================================
      13              :     Buffer sequence parameters in coroutine APIs MUST be passed BY VALUE,
      14              :     never by reference. When a coroutine suspends, reference parameters may
      15              :     dangle if the caller's object goes out of scope before resumption.
      16              : 
      17              :     CORRECT:   task<> read_some(MutableBufferSequence auto buffers)
      18              :     WRONG:     task<> read_some(MutableBufferSequence auto& buffers)
      19              :     WRONG:     task<> read_some(MutableBufferSequence auto const& buffers)
      20              : 
      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
      23              :     function accepts the buffer sequence by value, ensuring the sequence
      24              :     lives in the coroutine frame.
      25              : */
      26              : 
      27              : #ifndef BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
      28              : #define BOOST_CAPY_BUFFERS_BUFFER_PARAM_HPP
      29              : 
      30              : #include <boost/capy/detail/config.hpp>
      31              : #include <boost/capy/buffers.hpp>
      32              : 
      33              : #include <span>
      34              : 
      35              : namespace boost {
      36              : namespace capy {
      37              : 
      38              : /** A buffer sequence wrapper providing windowed access.
      39              : 
      40              :     This template class wraps any buffer sequence and provides
      41              :     incremental access through a sliding window of buffer
      42              :     descriptors. It handles both const and mutable buffer
      43              :     sequences automatically.
      44              : 
      45              :     @par Coroutine Lifetime Requirement
      46              : 
      47              :     When used in coroutine APIs, the outer template function
      48              :     MUST accept the buffer sequence parameter BY VALUE:
      49              : 
      50              :     @code
      51              :     task<> write(ConstBufferSequence auto buffers);   // CORRECT
      52              :     task<> write(ConstBufferSequence auto& buffers);  // WRONG - dangling reference
      53              :     @endcode
      54              : 
      55              :     Pass-by-value ensures the buffer sequence is copied into
      56              :     the coroutine frame and remains valid across suspension
      57              :     points. References would dangle when the caller's scope
      58              :     exits before the coroutine resumes.
      59              : 
      60              :     @par Purpose
      61              : 
      62              :     When iterating through large buffer sequences, it is often
      63              :     more efficient to process buffers in batches rather than
      64              :     one at a time. This class maintains a window of up to
      65              :     @ref max_size buffer descriptors, automatically refilling
      66              :     from the underlying sequence as buffers are consumed.
      67              : 
      68              :     @par Usage
      69              : 
      70              :     Create a `buffer_param` from any buffer sequence and use
      71              :     `data()` to get the current window of buffers. After
      72              :     processing some bytes, call `consume()` to advance through
      73              :     the sequence.
      74              : 
      75              :     @code
      76              :     task<> send(ConstBufferSequence auto buffers)
      77              :     {
      78              :         buffer_param bp(buffers);
      79              :         while(true)
      80              :         {
      81              :             auto bufs = bp.data();
      82              :             if(bufs.empty())
      83              :                 break;
      84              :             auto n = co_await do_something(bufs);
      85              :             bp.consume(n);
      86              :         }
      87              :     }
      88              :     @endcode
      89              : 
      90              :     @par Virtual Interface Pattern
      91              : 
      92              :     This class enables passing arbitrary buffer sequences through
      93              :     a virtual function boundary. The template function captures
      94              :     the buffer sequence by value and drives the iteration, while
      95              :     the virtual function receives a simple span:
      96              : 
      97              :     @code
      98              :     class base
      99              :     {
     100              :     public:
     101              :         task<> write(ConstBufferSequence auto buffers)
     102              :         {
     103              :             buffer_param bp(buffers);
     104              :             while(true)
     105              :             {
     106              :                 auto bufs = bp.data();
     107              :                 if(bufs.empty())
     108              :                     break;
     109              :                 std::size_t n = 0;
     110              :                 co_await write_impl(bufs, n);
     111              :                 bp.consume(n);
     112              :             }
     113              :         }
     114              : 
     115              :     protected:
     116              :         virtual task<> write_impl(
     117              :             std::span<const_buffer> buffers,
     118              :             std::size_t& bytes_written) = 0;
     119              :     };
     120              :     @endcode
     121              : 
     122              :     @tparam BS The buffer sequence type. Must satisfy either
     123              :         ConstBufferSequence or MutableBufferSequence.
     124              : 
     125              :     @see ConstBufferSequence, MutableBufferSequence
     126              : */
     127              : template<class BS>
     128              :     requires ConstBufferSequence<BS> || MutableBufferSequence<BS>
     129              : class buffer_param
     130              : {
     131              : public:
     132              :     /// The buffer type (const_buffer or mutable_buffer)
     133              :     using buffer_type = capy::buffer_type<BS>;
     134              : 
     135              : private:
     136              :     decltype(begin(std::declval<BS const&>())) it_;
     137              :     decltype(end(std::declval<BS const&>())) end_;
     138              :     buffer_type arr_[detail::max_iovec_];
     139              :     std::size_t size_ = 0;
     140              :     std::size_t pos_ = 0;
     141              : 
     142              :     void
     143          435 :     refill()
     144              :     {
     145          435 :         pos_ = 0;
     146          435 :         size_ = 0;
     147          883 :         for(; it_ != end_ && size_ < detail::max_iovec_; ++it_)
     148              :         {
     149          448 :             buffer_type buf(*it_);
     150          448 :             if(buf.size() > 0)
     151          436 :                 arr_[size_++] = buf;
     152              :         }
     153          435 :     }
     154              : 
     155              : public:
     156              :     /** Construct from a buffer sequence.
     157              : 
     158              :         @param bs The buffer sequence to wrap. The caller must
     159              :             ensure the buffer sequence remains valid for the
     160              :             lifetime of this object.
     161              :     */
     162              :     explicit
     163          345 :     buffer_param(BS const& bs)
     164          345 :         : it_(begin(bs))
     165          345 :         , end_(end(bs))
     166              :     {
     167          345 :         refill();
     168          345 :     }
     169              : 
     170              :     /** Return the current window of buffer descriptors.
     171              : 
     172              :         Returns a span of buffer descriptors representing the
     173              :         currently available portion of the buffer sequence.
     174              :         The span contains at most @ref max_size buffers.
     175              : 
     176              :         When the current window is exhausted, this function
     177              :         automatically refills from the underlying sequence.
     178              : 
     179              :         @return A span of buffer descriptors. Empty span
     180              :             indicates no more data is available.
     181              :     */
     182              :     std::span<buffer_type>
     183          487 :     data()
     184              :     {
     185          487 :         if(pos_ >= size_)
     186           90 :             refill();
     187          487 :         if(size_ == 0)
     188           87 :             return {};
     189          400 :         return {arr_ + pos_, size_ - pos_};
     190              :     }
     191              : 
     192              :     /** Consume bytes from the buffer sequence.
     193              : 
     194              :         Advances the current position by `n` bytes, consuming
     195              :         data from the front of the sequence. Partially consumed
     196              :         buffers are adjusted in place.
     197              : 
     198              :         @param n Number of bytes to consume.
     199              :     */
     200              :     void
     201          146 :     consume(std::size_t n)
     202              :     {
     203          351 :         while(n > 0 && pos_ < size_)
     204              :         {
     205          205 :             auto avail = arr_[pos_].size();
     206          205 :             if(n < avail)
     207              :             {
     208           65 :                 arr_[pos_] += n;
     209           65 :                 n = 0;
     210              :             }
     211              :             else
     212              :             {
     213          140 :                 n -= avail;
     214          140 :                 ++pos_;
     215              :             }
     216              :         }
     217          146 :     }
     218              : };
     219              : 
     220              : // CTAD deduction guide
     221              : template<class BS>
     222              : buffer_param(BS const&) -> buffer_param<BS>;
     223              : 
     224              : } // namespace capy
     225              : } // namespace boost
     226              : 
     227              : #endif
        

Generated by: LCOV version 2.3