LCOV - code coverage report
Current view: top level - boost/capy/test - buffer_sink.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 87.5 % 56 49
Test Date: 2026-01-30 23:43:15 Functions: 82.4 % 17 14

            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_SINK_HPP
      11              : #define BOOST_CAPY_TEST_BUFFER_SINK_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 <boost/capy/coro.hpp>
      17              : #include <boost/capy/ex/executor_ref.hpp>
      18              : #include <boost/capy/io_result.hpp>
      19              : #include <boost/capy/test/fuse.hpp>
      20              : 
      21              : #include <algorithm>
      22              : #include <stop_token>
      23              : #include <string>
      24              : #include <string_view>
      25              : 
      26              : namespace boost {
      27              : namespace capy {
      28              : namespace test {
      29              : 
      30              : /** A mock buffer sink for testing callee-owns-buffers write operations.
      31              : 
      32              :     Use this to verify code that writes data using the callee-owns-buffers
      33              :     pattern without needing real I/O. Call @ref prepare to get writable
      34              :     buffers, write into them, then call @ref commit to finalize. The
      35              :     associated @ref fuse enables error injection at controlled points.
      36              : 
      37              :     This class satisfies the @ref BufferSink concept by providing
      38              :     internal storage that callers write into directly.
      39              : 
      40              :     @par Thread Safety
      41              :     Not thread-safe.
      42              : 
      43              :     @par Example
      44              :     @code
      45              :     fuse f;
      46              :     buffer_sink bs( f );
      47              : 
      48              :     auto r = f.armed( [&]( fuse& ) -> task<void> {
      49              :         mutable_buffer arr[16];
      50              :         std::size_t count = bs.prepare( arr, 16 );
      51              :         if( count == 0 )
      52              :             co_return;
      53              : 
      54              :         // Write data into arr[0]
      55              :         std::memcpy( arr[0].data(), "Hello", 5 );
      56              : 
      57              :         auto [ec] = co_await bs.commit( 5 );
      58              :         if( ec )
      59              :             co_return;
      60              : 
      61              :         auto [ec2] = co_await bs.commit_eof();
      62              :         // bs.data() returns "Hello"
      63              :     } );
      64              :     @endcode
      65              : 
      66              :     @see fuse, BufferSink
      67              : */
      68              : class buffer_sink
      69              : {
      70              :     fuse* f_;
      71              :     std::string data_;
      72              :     std::string prepare_buf_;
      73              :     std::size_t prepare_size_ = 0;
      74              :     std::size_t max_prepare_size_;
      75              :     bool eof_called_ = false;
      76              : 
      77              : public:
      78              :     /** Construct a buffer sink.
      79              : 
      80              :         @param f The fuse used to inject errors during commits.
      81              : 
      82              :         @param max_prepare_size Maximum bytes available per prepare.
      83              :         Use to simulate limited buffer space.
      84              :     */
      85          404 :     explicit buffer_sink(
      86              :         fuse& f,
      87              :         std::size_t max_prepare_size = 4096) noexcept
      88          404 :         : f_(&f)
      89          404 :         , max_prepare_size_(max_prepare_size)
      90              :     {
      91          404 :         prepare_buf_.resize(max_prepare_size_);
      92          404 :     }
      93              : 
      94              :     /// Return the written data as a string view.
      95              :     std::string_view
      96           48 :     data() const noexcept
      97              :     {
      98           48 :         return data_;
      99              :     }
     100              : 
     101              :     /// Return the number of bytes written.
     102              :     std::size_t
     103            4 :     size() const noexcept
     104              :     {
     105            4 :         return data_.size();
     106              :     }
     107              : 
     108              :     /// Return whether commit_eof has been called.
     109              :     bool
     110           50 :     eof_called() const noexcept
     111              :     {
     112           50 :         return eof_called_;
     113              :     }
     114              : 
     115              :     /// Clear all data and reset state.
     116              :     void
     117              :     clear() noexcept
     118              :     {
     119              :         data_.clear();
     120              :         prepare_size_ = 0;
     121              :         eof_called_ = false;
     122              :     }
     123              : 
     124              :     /** Prepare writable buffers.
     125              : 
     126              :         Fills the provided array with mutable buffer descriptors pointing
     127              :         to internal storage. The caller writes data into these buffers,
     128              :         then calls @ref commit to finalize.
     129              : 
     130              :         @param arr Pointer to array of mutable_buffer to fill.
     131              :         @param max_count Maximum number of buffers to fill.
     132              : 
     133              :         @return The number of buffers filled (0 or 1 in this implementation).
     134              :     */
     135              :     std::size_t
     136          800 :     prepare(mutable_buffer* arr, std::size_t max_count)
     137              :     {
     138          800 :         if(max_count == 0)
     139            0 :             return 0;
     140              : 
     141          800 :         prepare_size_ = max_prepare_size_;
     142          800 :         arr[0] = make_buffer(prepare_buf_.data(), prepare_size_);
     143          800 :         return 1;
     144              :     }
     145              : 
     146              :     /** Commit bytes written to the prepared buffers.
     147              : 
     148              :         Transfers `n` bytes from the prepared buffer to the internal
     149              :         data buffer. Before committing, the attached @ref fuse is
     150              :         consulted to possibly inject an error for testing fault scenarios.
     151              : 
     152              :         @param n The number of bytes to commit.
     153              : 
     154              :         @return An awaitable yielding `(error_code)`.
     155              : 
     156              :         @see fuse
     157              :     */
     158              :     auto
     159          506 :     commit(std::size_t n)
     160              :     {
     161              :         struct awaitable
     162              :         {
     163              :             buffer_sink* self_;
     164              :             std::size_t n_;
     165              : 
     166          506 :             bool await_ready() const noexcept { return true; }
     167              : 
     168            0 :             void await_suspend(
     169              :                 coro,
     170              :                 executor_ref,
     171              :                 std::stop_token) const noexcept
     172              :             {
     173            0 :             }
     174              : 
     175              :             io_result<>
     176          506 :             await_resume()
     177              :             {
     178          506 :                 auto ec = self_->f_->maybe_fail();
     179          458 :                 if(ec)
     180           48 :                     return {ec};
     181              : 
     182          410 :                 std::size_t to_commit = (std::min)(n_, self_->prepare_size_);
     183          410 :                 self_->data_.append(self_->prepare_buf_.data(), to_commit);
     184          410 :                 self_->prepare_size_ = 0;
     185              : 
     186          410 :                 return {};
     187              :             }
     188              :         };
     189          506 :         return awaitable{this, n};
     190              :     }
     191              : 
     192              :     /** Commit bytes written with optional end-of-stream.
     193              : 
     194              :         Transfers `n` bytes from the prepared buffer to the internal
     195              :         data buffer. If `eof` is true, marks the sink as finalized.
     196              : 
     197              :         @param n The number of bytes to commit.
     198              :         @param eof If true, signals end-of-stream after committing.
     199              : 
     200              :         @return An awaitable yielding `(error_code)`.
     201              : 
     202              :         @see fuse
     203              :     */
     204              :     auto
     205           10 :     commit(std::size_t n, bool eof)
     206              :     {
     207              :         struct awaitable
     208              :         {
     209              :             buffer_sink* self_;
     210              :             std::size_t n_;
     211              :             bool eof_;
     212              : 
     213           10 :             bool await_ready() const noexcept { return true; }
     214              : 
     215            0 :             void await_suspend(
     216              :                 coro,
     217              :                 executor_ref,
     218              :                 std::stop_token) const noexcept
     219              :             {
     220            0 :             }
     221              : 
     222              :             io_result<>
     223           10 :             await_resume()
     224              :             {
     225           10 :                 auto ec = self_->f_->maybe_fail();
     226            7 :                 if(ec)
     227            3 :                     return {ec};
     228              : 
     229            4 :                 std::size_t to_commit = (std::min)(n_, self_->prepare_size_);
     230            4 :                 self_->data_.append(self_->prepare_buf_.data(), to_commit);
     231            4 :                 self_->prepare_size_ = 0;
     232              : 
     233            4 :                 if(eof_)
     234            4 :                     self_->eof_called_ = true;
     235              : 
     236            4 :                 return {};
     237              :             }
     238              :         };
     239           10 :         return awaitable{this, n, eof};
     240              :     }
     241              : 
     242              :     /** Signal end-of-stream.
     243              : 
     244              :         Marks the sink as finalized, indicating no more data will be
     245              :         written. Before signaling, the attached @ref fuse is consulted
     246              :         to possibly inject an error for testing fault scenarios.
     247              : 
     248              :         @return An awaitable yielding `(error_code)`.
     249              : 
     250              :         @see fuse
     251              :     */
     252              :     auto
     253          110 :     commit_eof()
     254              :     {
     255              :         struct awaitable
     256              :         {
     257              :             buffer_sink* self_;
     258              : 
     259          110 :             bool await_ready() const noexcept { return true; }
     260              : 
     261            0 :             void await_suspend(
     262              :                 coro,
     263              :                 executor_ref,
     264              :                 std::stop_token) const noexcept
     265              :             {
     266            0 :             }
     267              : 
     268              :             io_result<>
     269          110 :             await_resume()
     270              :             {
     271          110 :                 auto ec = self_->f_->maybe_fail();
     272           82 :                 if(ec)
     273           28 :                     return {ec};
     274              : 
     275           54 :                 self_->eof_called_ = true;
     276           54 :                 return {};
     277              :             }
     278              :         };
     279          110 :         return awaitable{this};
     280              :     }
     281              : };
     282              : 
     283              : } // test
     284              : } // capy
     285              : } // boost
     286              : 
     287              : #endif
        

Generated by: LCOV version 2.3