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-08-14 20:51:18 Functions: 83.3 % 18 15 3

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/capy
       9                 : //
      10                 : 
      11                 : #ifndef BOOST_CAPY_TEST_BUFGRIND_HPP
      12                 : #define BOOST_CAPY_TEST_BUFGRIND_HPP
      13                 : 
      14                 : #include <boost/capy/detail/config.hpp>
      15                 : #include <boost/capy/buffers.hpp>
      16                 : #include <boost/capy/buffers/buffer_slice.hpp>
      17                 : #include <coroutine>
      18                 : #include <boost/capy/ex/io_env.hpp>
      19                 : 
      20                 : #include <algorithm>
      21                 : #include <cstddef>
      22                 : #include <type_traits>
      23                 : #include <utility>
      24                 : 
      25                 : namespace boost {
      26                 : namespace capy {
      27                 : namespace test {
      28                 : 
      29                 : /** Iterates split points of a buffer sequence into two adjacent halves.
      30                 : 
      31                 :     This class iterates through all possible ways to split a buffer
      32                 :     sequence into two parts (b1, b2) where concatenating them yields
      33                 :     the original sequence. It uses an async-generator-like pattern
      34                 :     that allows `co_await` between iterations.
      35                 : 
      36                 :     The split type automatically preserves mutability: passing a
      37                 :     `MutableBufferSequence` yields halves that model
      38                 :     @ref MutableBufferSequence, while passing a `ConstBufferSequence`
      39                 :     yields halves that model @ref ConstBufferSequence. Each half is
      40                 :     the buffer-sequence view exposed by a @ref buffer_slice over the
      41                 :     corresponding byte range, and can be passed directly to
      42                 :     `read_some`, `write_some`, `buffer_size`, etc.
      43                 : 
      44                 :     @par Thread Safety
      45                 :     Not thread-safe.
      46                 : 
      47                 :     @par Example
      48                 :     @code
      49                 :     // Test all split points of a buffer
      50                 :     std::string data = "hello world";
      51                 :     auto cb = make_buffer( data );
      52                 : 
      53                 :     fuse f;
      54                 :     auto r = f.inert( [&]( fuse& ) -> task<> {
      55                 :         bufgrind bg( cb );
      56                 :         while( bg ) {
      57                 :             auto [b1, b2] = co_await bg.next();
      58                 :             // b1 contains first N bytes (as a buffer sequence)
      59                 :             // b2 contains remaining bytes (as a buffer sequence)
      60                 :             // concatenating b1 + b2 equals original
      61                 :             co_await some_async_operation( b1, b2 );
      62                 :         }
      63                 :     } );
      64                 :     @endcode
      65                 : 
      66                 :     @par Mutable Buffer Example
      67                 :     @code
      68                 :     // Mutable buffers preserve mutability
      69                 :     char data[100];
      70                 :     mutable_buffer mb( data, sizeof( data ) );
      71                 : 
      72                 :     bufgrind bg( mb );
      73                 :     while( bg ) {
      74                 :         auto [b1, b2] = co_await bg.next();
      75                 :         // b1, b2 yield mutable_buffer when iterated
      76                 :     }
      77                 :     @endcode
      78                 : 
      79                 :     @par Step Size Example
      80                 :     @code
      81                 :     // Skip by 10 bytes for faster iteration
      82                 :     bufgrind bg( cb, 10 );
      83                 :     while( bg ) {
      84                 :         auto [b1, b2] = co_await bg.next();
      85                 :         // Visits positions 0, 10, 20, ..., and always size
      86                 :     }
      87                 :     @endcode
      88                 : 
      89                 :     @see buffer_slice
      90                 : */
      91                 : template<ConstBufferSequence BS>
      92                 : class bufgrind
      93                 : {
      94                 :     BS const& bs_;
      95                 :     std::size_t size_;
      96                 :     std::size_t step_;
      97                 :     std::size_t pos_ = 0;
      98                 : 
      99                 : public:
     100                 :     /// Names the buffer-sequence type `buffer_slice` yields for each half.
     101                 :     using slice_type = std::decay_t<
     102                 :         decltype(buffer_slice(std::declval<BS const&>()))>;
     103                 : 
     104                 :     /// Pairs the two `slice_type` halves that @ref next yields together.
     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          39 :     bufgrind(
     118                 :         BS const& bs,
     119                 :         std::size_t step = 1) noexcept
     120              39 :         : bs_(bs)
     121              39 :         , size_(buffer_size(bs))
     122              39 :         , step_(step > 0 ? step : 1)
     123                 :     {
     124              39 :     }
     125                 : 
     126                 :     /** Check if more split points remain.
     127                 : 
     128                 :         @return `true` if @ref next can be called, `false` otherwise.
     129                 :     */
     130             237 :     explicit operator bool() const noexcept
     131                 :     {
     132             237 :         return pos_ <= size_;
     133                 :     }
     134                 : 
     135                 :     /** Computes the current split synchronously, so awaiting it never suspends the caller.
     136                 :     */
     137                 :     struct next_awaitable
     138                 :     {
     139                 :         /// The grinder that produced this awaitable.
     140                 :         bufgrind* self_;
     141                 : 
     142                 :         /** Report whether the awaitable is ready.
     143                 : 
     144                 :             @return `true` always; the split is available without suspending.
     145                 :         */
     146             198 :         bool await_ready() const noexcept { return true; }
     147                 : 
     148                 :         /** Resume the caller inline without suspending.
     149                 : 
     150                 :             @param h The awaiting coroutine handle.
     151                 : 
     152                 :             @return @p h, so the caller resumes immediately.
     153                 :         */
     154 MIS           0 :         std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; }
     155                 : 
     156                 :         /** Return the current split and advance to the next.
     157                 : 
     158                 :             @return The `(b1, b2)` split at the current position.
     159                 :         */
     160                 :         split_type
     161 HIT         198 :         await_resume()
     162                 :         {
     163             198 :             split_type result{
     164             198 :                 buffer_slice(self_->bs_, 0, self_->pos_),
     165             198 :                 buffer_slice(self_->bs_, self_->pos_)
     166                 :             };
     167             198 :             if(self_->pos_ < self_->size_)
     168             161 :                 self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
     169                 :             else
     170              37 :                 ++self_->pos_;
     171             198 :             return result;
     172                 :         }
     173                 :     };
     174                 : 
     175                 :     /** Return the next split point.
     176                 : 
     177                 :         Returns an awaitable that yields the current (b1, b2) pair
     178                 :         and advances to the next split point.
     179                 : 
     180                 :         @par Preconditions
     181                 :         `static_cast<bool>( *this )` is `true`.
     182                 : 
     183                 :         @return An awaitable that await-returns `split_type`.
     184                 :     */
     185                 :     next_awaitable
     186             198 :     next() noexcept
     187                 :     {
     188             198 :         return {this};
     189                 :     }
     190                 : };
     191                 : 
     192                 : } // test
     193                 : } // capy
     194                 : } // boost
     195                 : 
     196                 : #endif
        

Generated by: LCOV version 2.3