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