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