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/buffers/buffer_slice.hpp>
17 : #include <coroutine>
18 : #include <boost/capy/ex/io_env.hpp>
19 :
20 : #include <algorithm>
21 : #include <cstddef>
22 : #include <type_traits>
23 : #include <utility>
24 :
25 : namespace boost {
26 : namespace capy {
27 : namespace test {
28 :
29 : /** Iterates split points of a buffer sequence into two adjacent halves.
30 :
31 : This class iterates through all possible ways to split a buffer
32 : sequence into two parts (b1, b2) where concatenating them yields
33 : the original sequence. It uses an async-generator-like pattern
34 : that allows `co_await` between iterations.
35 :
36 : The split type automatically preserves mutability: passing a
37 : `MutableBufferSequence` yields halves that model
38 : @ref MutableBufferSequence, while passing a `ConstBufferSequence`
39 : yields halves that model @ref ConstBufferSequence. Each half is
40 : the buffer-sequence view exposed by a @ref buffer_slice over the
41 : corresponding byte range, and can be passed directly to
42 : `read_some`, `write_some`, `buffer_size`, etc.
43 :
44 : @par Thread Safety
45 : Not thread-safe.
46 :
47 : @par Example
48 : @par !example example_1
49 :
50 :
51 : @par Mutable Buffer Example
52 : @par !example example_2
53 :
54 :
55 : @par Step Size Example
56 : @par !example example_3
57 :
58 :
59 : @see buffer_slice
60 : */
61 : template<ConstBufferSequence BS>
62 : class bufgrind
63 : {
64 : BS const& bs_;
65 : std::size_t size_;
66 : std::size_t step_;
67 : std::size_t pos_ = 0;
68 :
69 : public:
70 : /// Names the buffer-sequence type `buffer_slice` yields for each half.
71 : using slice_type = std::decay_t<
72 : decltype(buffer_slice(std::declval<BS const&>()))>;
73 :
74 : /// Pairs the two `slice_type` halves that @ref next yields together.
75 : using split_type = std::pair<slice_type, slice_type>;
76 :
77 : /** Construct a buffer grinder.
78 :
79 : @param bs The buffer sequence to iterate over.
80 :
81 : @param step The number of bytes to advance on each call to
82 : @ref next. A value of 0 is treated as 1. The final split
83 : at `buffer_size( bs )` is always included regardless of
84 : step alignment.
85 : */
86 : explicit
87 HIT 39 : bufgrind(
88 : BS const& bs,
89 : std::size_t step = 1) noexcept
90 39 : : bs_(bs)
91 39 : , size_(buffer_size(bs))
92 39 : , step_(step > 0 ? step : 1)
93 : {
94 39 : }
95 :
96 : /** Check if more split points remain.
97 :
98 : @return `true` if @ref next can be called, `false` otherwise.
99 : */
100 237 : explicit operator bool() const noexcept
101 : {
102 237 : return pos_ <= size_;
103 : }
104 :
105 : /** Computes the current split synchronously, so awaiting it never suspends the caller.
106 : */
107 : struct next_awaitable
108 : {
109 : /// The grinder that produced this awaitable.
110 : bufgrind* self_;
111 :
112 : /** Report whether the awaitable is ready.
113 :
114 : @return `true` always; the split is available without suspending.
115 : */
116 198 : bool await_ready() const noexcept { return true; }
117 :
118 : /** Resume the caller inline without suspending.
119 :
120 : @param h The awaiting coroutine handle.
121 :
122 : @return @p h, so the caller resumes immediately.
123 : */
124 MIS 0 : std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; }
125 :
126 : /** Return the current split and advance to the next.
127 :
128 : @return The `(b1, b2)` split at the current position.
129 : */
130 : split_type
131 HIT 198 : await_resume()
132 : {
133 198 : split_type result{
134 198 : buffer_slice(self_->bs_, 0, self_->pos_),
135 198 : buffer_slice(self_->bs_, self_->pos_)
136 : };
137 198 : if(self_->pos_ < self_->size_)
138 161 : self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
139 : else
140 37 : ++self_->pos_;
141 198 : return result;
142 : }
143 : };
144 :
145 : /** Return the next split point.
146 :
147 : Returns an awaitable that yields the current (b1, b2) pair
148 : and advances to the next split point.
149 :
150 : @par Preconditions
151 : `static_cast<bool>( *this )` is `true`.
152 :
153 : @return An awaitable that await-returns `split_type`.
154 : */
155 : next_awaitable
156 198 : next() noexcept
157 : {
158 198 : return {this};
159 : }
160 : };
161 :
162 : } // test
163 : } // capy
164 : } // boost
165 :
166 : #endif
|