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