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