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-02-07 18:59:16 Functions: 97.5 % 40 39

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

Generated by: LCOV version 2.3