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