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