TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Michael Vandeberg
4 : //
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)
7 : //
8 : // Official repository: https://github.com/cppalliance/capy
9 : //
10 :
11 : #ifndef BOOST_CAPY_TEST_BUFGRIND_HPP
12 : #define BOOST_CAPY_TEST_BUFGRIND_HPP
13 :
14 : #include <boost/capy/detail/config.hpp>
15 : #include <boost/capy/buffers.hpp>
16 : #include <boost/capy/detail/slice_of.hpp>
17 : #include <coroutine>
18 : #include <boost/capy/ex/await_context.hpp>
19 :
20 : #include <algorithm>
21 : #include <cstddef>
22 : #include <limits>
23 : #include <type_traits>
24 : #include <utility>
25 :
26 : namespace boost {
27 : namespace capy {
28 : namespace test {
29 :
30 : /** Iterates split points of a buffer sequence into two adjacent halves.
31 :
32 : This class iterates through all possible ways to split a buffer
33 : sequence into two parts (b1, b2) where concatenating them yields
34 : the original sequence. It uses an async-generator-like pattern
35 : that allows `co_await` between iterations.
36 :
37 : The split type automatically preserves mutability: passing a
38 : `MutableBufferSequence` yields halves that model
39 : @ref MutableBufferSequence, while passing a `ConstBufferSequence`
40 : yields halves that model @ref ConstBufferSequence. A single buffer
41 : splits into two `mutable_buffer` or `const_buffer` values; any other
42 : sequence splits into two borrowed views. Either way each half can be
43 : passed directly to `read_some`, `write_some`, `buffer_size`, etc.
44 :
45 : @par Thread Safety
46 : Not thread-safe.
47 :
48 : @par Example
49 : @par !example example_1
50 :
51 :
52 : @par Mutable Buffer Example
53 : @par !example example_2
54 :
55 :
56 : @par Step Size Example
57 : @par !example example_3
58 :
59 : */
60 : template<ConstBufferSequence BS>
61 : class bufgrind
62 : {
63 : BS const& bs_;
64 : std::size_t size_;
65 : std::size_t step_;
66 : std::size_t pos_ = 0;
67 :
68 : public:
69 : /// Names the buffer-sequence type of each half.
70 : using slice_type = std::conditional_t<
71 : ConstBuffer<BS>,
72 : buffer_type<BS>,
73 : detail::slice_of<BS>>;
74 :
75 : /// Pairs the two `slice_type` halves that @ref next yields together.
76 : using split_type = std::pair<slice_type, slice_type>;
77 :
78 : private:
79 : static slice_type
80 HIT 396 : slice(
81 : BS const& bs,
82 : std::size_t offset,
83 : std::size_t length =
84 : (std::numeric_limits<std::size_t>::max)()) noexcept
85 : {
86 : if constexpr (ConstBuffer<BS>)
87 : {
88 382 : slice_type b = bs;
89 382 : b += offset;
90 382 : if(length < b.size())
91 155 : b = slice_type(b.data(), length);
92 382 : return b;
93 : }
94 : else
95 : {
96 14 : return slice_type(bs, offset, length);
97 : }
98 : }
99 :
100 : public:
101 :
102 : /** Construct a buffer grinder.
103 :
104 : @param bs The buffer sequence to iterate over.
105 :
106 : @param step The number of bytes to advance on each call to
107 : @ref next. A value of 0 is treated as 1. The final split
108 : at `buffer_size( bs )` is always included regardless of
109 : step alignment.
110 : */
111 : explicit
112 39 : bufgrind(
113 : BS const& bs,
114 : std::size_t step = 1) noexcept
115 39 : : bs_(bs)
116 39 : , size_(buffer_size(bs))
117 39 : , step_(step > 0 ? step : 1)
118 : {
119 39 : }
120 :
121 : /** Check if more split points remain.
122 :
123 : @return `true` if @ref next can be called, `false` otherwise.
124 : */
125 237 : explicit operator bool() const noexcept
126 : {
127 237 : return pos_ <= size_;
128 : }
129 :
130 : /** Computes the current split synchronously, so awaiting it never suspends the caller.
131 : */
132 : struct next_awaitable
133 : {
134 : /// The grinder that produced this awaitable.
135 : bufgrind* self_;
136 :
137 : /** Report whether the awaitable is ready.
138 :
139 : @return `true` always; the split is available without suspending.
140 : */
141 198 : bool await_ready() const noexcept { return true; }
142 :
143 : /** Resume the caller inline without suspending.
144 :
145 : @param h The awaiting coroutine handle.
146 :
147 : @return @p h, so the caller resumes immediately.
148 : */
149 MIS 0 : std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const*) const noexcept { return h; }
150 :
151 : /** Return the current split and advance to the next.
152 :
153 : @return The `(b1, b2)` split at the current position.
154 : */
155 : split_type
156 HIT 198 : await_resume()
157 : {
158 198 : split_type result{
159 198 : slice(self_->bs_, 0, self_->pos_),
160 198 : slice(self_->bs_, self_->pos_)
161 : };
162 198 : if(self_->pos_ < self_->size_)
163 161 : self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
164 : else
165 37 : ++self_->pos_;
166 198 : return result;
167 : }
168 : };
169 :
170 : /** Return the next split point.
171 :
172 : Returns an awaitable that yields the current (b1, b2) pair
173 : and advances to the next split point.
174 :
175 : @par Preconditions
176 : `static_cast<bool>( *this )` is `true`.
177 :
178 : @return An awaitable that await-returns `split_type`.
179 : */
180 : next_awaitable
181 198 : next() noexcept
182 : {
183 198 : return {this};
184 : }
185 : };
186 :
187 : } // test
188 : } // capy
189 : } // boost
190 :
191 : #endif
|