LCOV - code coverage report
Current view: top level - capy/test - bufgrind.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 94.7 % 19 18 1
Test Date: 2026-05-14 20:50:55 Functions: 83.3 % 18 15 3

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

Generated by: LCOV version 2.3