LCOV - code coverage report
Current view: top level - capy/test - buffer_source.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 94.1 % 34 32 2
Test Date: 2026-06-15 22:53:33 Functions: 88.9 % 9 8 1

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

Generated by: LCOV version 2.3