LCOV - code coverage report
Current view: top level - capy/test - bufgrind.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 94.4 % 18 17
Test Date: 2026-02-12 16:53:28 Functions: 83.3 % 18 15

            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_BUFGRIND_HPP
      11              : #define BOOST_CAPY_TEST_BUFGRIND_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/buffers.hpp>
      15              : #include <boost/capy/buffers/slice.hpp>
      16              : #include <coroutine>
      17              : #include <boost/capy/ex/io_env.hpp>
      18              : 
      19              : #include <algorithm>
      20              : #include <cstddef>
      21              : #include <utility>
      22              : 
      23              : namespace boost {
      24              : namespace capy {
      25              : namespace test {
      26              : 
      27              : /** A test utility for iterating buffer sequence split points.
      28              : 
      29              :     This class iterates through all possible ways to split a buffer
      30              :     sequence into two parts (b1, b2) where concatenating them yields
      31              :     the original sequence. It uses an async-generator-like pattern
      32              :     that allows `co_await` between iterations.
      33              : 
      34              :     The split type automatically preserves mutability: passing a
      35              :     `MutableBufferSequence` yields mutable slices, while passing a
      36              :     `ConstBufferSequence` yields const slices. This is handled
      37              :     automatically through `slice_type<BS>`.
      38              : 
      39              :     @par Thread Safety
      40              :     Not thread-safe.
      41              : 
      42              :     @par Example
      43              :     @code
      44              :     // Test all split points of a buffer
      45              :     std::string data = "hello world";
      46              :     auto cb = make_buffer( data );
      47              : 
      48              :     fuse f;
      49              :     auto r = f.inert( [&]( fuse& ) -> task<> {
      50              :         bufgrind bg( cb );
      51              :         while( bg ) {
      52              :             auto [b1, b2] = co_await bg.next();
      53              :             // b1 contains first N bytes
      54              :             // b2 contains remaining bytes
      55              :             // concatenating b1 + b2 equals original
      56              :             co_await some_async_operation( b1, b2 );
      57              :         }
      58              :     } );
      59              :     @endcode
      60              : 
      61              :     @par Mutable Buffer Example
      62              :     @code
      63              :     // Mutable buffers preserve mutability
      64              :     char data[100];
      65              :     mutable_buffer mb( data, sizeof( data ) );
      66              : 
      67              :     bufgrind bg( mb );
      68              :     while( bg ) {
      69              :         auto [b1, b2] = co_await bg.next();
      70              :         // b1, b2 yield mutable_buffer when iterated
      71              :     }
      72              :     @endcode
      73              : 
      74              :     @par Step Size Example
      75              :     @code
      76              :     // Skip by 10 bytes for faster iteration
      77              :     bufgrind bg( cb, 10 );
      78              :     while( bg ) {
      79              :         auto [b1, b2] = co_await bg.next();
      80              :         // Visits positions 0, 10, 20, ..., and always size
      81              :     }
      82              :     @endcode
      83              : 
      84              :     @see prefix, sans_prefix, slice_type
      85              : */
      86              : template<ConstBufferSequence BS>
      87              : class bufgrind
      88              : {
      89              :     BS const& bs_;
      90              :     std::size_t size_;
      91              :     std::size_t step_;
      92              :     std::size_t pos_ = 0;
      93              : 
      94              : public:
      95              :     /// The type returned by @ref next.
      96              :     using split_type = std::pair<slice_type<BS>, slice_type<BS>>;
      97              : 
      98              :     /** Construct a buffer grinder.
      99              : 
     100              :         @param bs The buffer sequence to iterate over.
     101              : 
     102              :         @param step The number of bytes to advance on each call to
     103              :         @ref next. A value of 0 is treated as 1. The final split
     104              :         at `buffer_size( bs )` is always included regardless of
     105              :         step alignment.
     106              :     */
     107              :     explicit
     108          178 :     bufgrind(
     109              :         BS const& bs,
     110              :         std::size_t step = 1) noexcept
     111          178 :         : bs_(bs)
     112          178 :         , size_(buffer_size(bs))
     113          178 :         , step_(step > 0 ? step : 1)
     114              :     {
     115          178 :     }
     116              : 
     117              :     /** Check if more split points remain.
     118              : 
     119              :         @return `true` if @ref next can be called, `false` otherwise.
     120              :     */
     121          986 :     explicit operator bool() const noexcept
     122              :     {
     123          986 :         return pos_ <= size_;
     124              :     }
     125              : 
     126              :     /** Awaitable returned by @ref next.
     127              :     */
     128              :     struct next_awaitable
     129              :     {
     130              :         bufgrind* self_;
     131              : 
     132          944 :         bool await_ready() const noexcept { return true; }
     133            0 :         std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; }
     134              : 
     135              :         split_type
     136          944 :         await_resume()
     137              :         {
     138          944 :             auto b1 = prefix(self_->bs_, self_->pos_);
     139          944 :             auto b2 = sans_prefix(self_->bs_, self_->pos_);
     140          944 :             if(self_->pos_ < self_->size_)
     141          888 :                 self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
     142              :             else
     143           56 :                 ++self_->pos_;
     144          944 :             return {std::move(b1), std::move(b2)};
     145              :         }
     146              :     };
     147              : 
     148              :     /** Return the next split point.
     149              : 
     150              :         Returns an awaitable that yields the current (b1, b2) pair
     151              :         and advances to the next split point.
     152              : 
     153              :         @par Preconditions
     154              :         `static_cast<bool>( *this )` is `true`.
     155              : 
     156              :         @return An awaitable yielding `split_type`.
     157              :     */
     158              :     next_awaitable
     159          944 :     next() noexcept
     160              :     {
     161          944 :         return {this};
     162              :     }
     163              : };
     164              : 
     165              : } // test
     166              : } // capy
     167              : } // boost
     168              : 
     169              : #endif
        

Generated by: LCOV version 2.3