LCOV - code coverage report
Current view: top level - capy/test - buffer_source.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 89.3 % 28 25
Test Date: 2026-02-12 22:58:59 Functions: 85.7 % 7 6

            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              : #ifndef BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
      11              : #define BOOST_CAPY_TEST_BUFFER_SOURCE_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/buffers.hpp>
      15              : #include <boost/capy/buffers/make_buffer.hpp>
      16              : #include <coroutine>
      17              : #include <boost/capy/error.hpp>
      18              : #include <boost/capy/ex/io_env.hpp>
      19              : #include <boost/capy/io_result.hpp>
      20              : #include <boost/capy/test/fuse.hpp>
      21              : 
      22              : #include <algorithm>
      23              : #include <string>
      24              : #include <string_view>
      25              : 
      26              : namespace boost {
      27              : namespace capy {
      28              : namespace test {
      29              : 
      30              : /** A mock buffer source for testing push operations.
      31              : 
      32              :     Use this to verify code that transfers data from a buffer source to
      33              :     a sink without needing real I/O. Call @ref provide to supply data,
      34              :     then @ref pull to retrieve buffer descriptors. The associated
      35              :     @ref fuse enables error injection at controlled points.
      36              : 
      37              :     This class satisfies the @ref BufferSource concept by providing
      38              :     a pull interface that fills an array of buffer descriptors and
      39              :     a consume interface to indicate bytes used.
      40              : 
      41              :     @par Thread Safety
      42              :     Not thread-safe.
      43              : 
      44              :     @par Example
      45              :     @code
      46              :     fuse f;
      47              :     buffer_source bs( f );
      48              :     bs.provide( "Hello, " );
      49              :     bs.provide( "World!" );
      50              : 
      51              :     auto r = f.armed( [&]( fuse& ) -> task<void> {
      52              :         const_buffer arr[16];
      53              :         auto [ec, bufs] = co_await bs.pull( arr );
      54              :         if( ec )
      55              :             co_return;
      56              :         // bufs contains buffer descriptors
      57              :         std::size_t n = buffer_size( bufs );
      58              :         bs.consume( n );
      59              :     } );
      60              :     @endcode
      61              : 
      62              :     @see fuse, BufferSource
      63              : */
      64              : class buffer_source
      65              : {
      66              :     fuse f_;
      67              :     std::string data_;
      68              :     std::size_t pos_ = 0;
      69              :     std::size_t max_pull_size_;
      70              : 
      71              : public:
      72              :     /** Construct a buffer source.
      73              : 
      74              :         @param f The fuse used to inject errors during pulls.
      75              : 
      76              :         @param max_pull_size Maximum bytes returned per pull.
      77              :         Use to simulate chunked delivery.
      78              :     */
      79          302 :     explicit buffer_source(
      80              :         fuse f = {},
      81              :         std::size_t max_pull_size = std::size_t(-1)) noexcept
      82          302 :         : f_(std::move(f))
      83          302 :         , max_pull_size_(max_pull_size)
      84              :     {
      85          302 :     }
      86              : 
      87              :     /** Append data to be returned by subsequent pulls.
      88              : 
      89              :         Multiple calls accumulate data that @ref pull returns.
      90              : 
      91              :         @param sv The data to append.
      92              :     */
      93              :     void
      94          316 :     provide(std::string_view sv)
      95              :     {
      96          316 :         data_.append(sv);
      97          316 :     }
      98              : 
      99              :     /// Clear all data and reset the read position.
     100              :     void
     101              :     clear() noexcept
     102              :     {
     103              :         data_.clear();
     104              :         pos_ = 0;
     105              :     }
     106              : 
     107              :     /// Return the number of bytes available for pulling.
     108              :     std::size_t
     109              :     available() const noexcept
     110              :     {
     111              :         return data_.size() - pos_;
     112              :     }
     113              : 
     114              :     /** Consume bytes from the source.
     115              : 
     116              :         Advances the internal read position by the specified number
     117              :         of bytes. The next call to @ref pull returns data starting
     118              :         after the consumed bytes.
     119              : 
     120              :         @param n The number of bytes to consume. Must not exceed the
     121              :         total size of buffers returned by the previous @ref pull.
     122              :     */
     123              :     void
     124          271 :     consume(std::size_t n) noexcept
     125              :     {
     126          271 :         pos_ += n;
     127          271 :     }
     128              : 
     129              :     /** Pull buffer data from the source.
     130              : 
     131              :         Fills the provided span with buffer descriptors pointing to
     132              :         internal data starting from the current unconsumed position.
     133              :         Returns a span of filled buffers. When no data remains,
     134              :         returns an empty span to signal completion.
     135              : 
     136              :         Calling pull multiple times without intervening @ref consume
     137              :         returns the same data. Use consume to advance past processed
     138              :         bytes.
     139              : 
     140              :         @param dest Span of const_buffer to fill.
     141              : 
     142              :         @return An awaitable yielding `(error_code,std::span<const_buffer>)`.
     143              : 
     144              :         @see consume, fuse
     145              :     */
     146              :     auto
     147          552 :     pull(std::span<const_buffer> dest)
     148              :     {
     149              :         struct awaitable
     150              :         {
     151              :             buffer_source* self_;
     152              :             std::span<const_buffer> dest_;
     153              : 
     154          552 :             bool await_ready() const noexcept { return true; }
     155              : 
     156              :             // This method is required to satisfy Capy's IoAwaitable concept,
     157              :             // but is never called because await_ready() returns true.
     158              :             //
     159              :             // Capy uses a two-layer awaitable system: the promise's
     160              :             // await_transform wraps awaitables in a transform_awaiter whose
     161              :             // standard await_suspend(coroutine_handle) calls this custom
     162              :             // 2-argument overload, passing the io_env from the coroutine's
     163              :             // context. For synchronous test awaitables like this one, the
     164              :             // coroutine never suspends, so this is not invoked. The signature
     165              :             // exists to allow the same awaitable type to work with both
     166              :             // synchronous (test) and asynchronous (real I/O) code.
     167            0 :             void await_suspend(
     168              :                 std::coroutine_handle<>,
     169              :                 io_env const*) const noexcept
     170              :             {
     171            0 :             }
     172              : 
     173              :             io_result<std::span<const_buffer>>
     174          552 :             await_resume()
     175              :             {
     176          552 :                 auto ec = self_->f_.maybe_fail();
     177          467 :                 if(ec)
     178           85 :                     return {ec, {}};
     179              : 
     180          382 :                 if(self_->pos_ >= self_->data_.size())
     181           66 :                     return {error::eof, {}};
     182              : 
     183          316 :                 std::size_t avail = self_->data_.size() - self_->pos_;
     184          316 :                 std::size_t to_return = (std::min)(avail, self_->max_pull_size_);
     185              : 
     186          316 :                 if(dest_.empty())
     187            0 :                     return {{}, {}};
     188              : 
     189              :                 // Fill a single buffer descriptor
     190          316 :                 dest_[0] = make_buffer(
     191          316 :                     self_->data_.data() + self_->pos_,
     192              :                     to_return);
     193              : 
     194          316 :                 return {{}, dest_.first(1)};
     195              :             }
     196              :         };
     197          552 :         return awaitable{this, dest};
     198              :     }
     199              : };
     200              : 
     201              : } // test
     202              : } // capy
     203              : } // boost
     204              : 
     205              : #endif
        

Generated by: LCOV version 2.3