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 <boost/capy/coro.hpp>
16  
#include <boost/capy/coro.hpp>
17  
#include <boost/capy/ex/executor_ref.hpp>
17  
#include <boost/capy/ex/executor_ref.hpp>
18  

18  

19  
#include <algorithm>
19  
#include <algorithm>
20  
#include <cstddef>
20  
#include <cstddef>
21  
#include <stop_token>
21  
#include <stop_token>
22  
#include <utility>
22  
#include <utility>
23  

23  

24  
namespace boost {
24  
namespace boost {
25  
namespace capy {
25  
namespace capy {
26  
namespace test {
26  
namespace test {
27  

27  

28  
/** A test utility for iterating buffer sequence split points.
28  
/** A test utility for iterating buffer sequence split points.
29  

29  

30  
    This class iterates through all possible ways to split a buffer
30  
    This class iterates through all possible ways to split a buffer
31  
    sequence into two parts (b1, b2) where concatenating them yields
31  
    sequence into two parts (b1, b2) where concatenating them yields
32  
    the original sequence. It uses an async-generator-like pattern
32  
    the original sequence. It uses an async-generator-like pattern
33  
    that allows `co_await` between iterations.
33  
    that allows `co_await` between iterations.
34  

34  

35  
    The split type automatically preserves mutability: passing a
35  
    The split type automatically preserves mutability: passing a
36  
    `MutableBufferSequence` yields mutable slices, while passing a
36  
    `MutableBufferSequence` yields mutable slices, while passing a
37  
    `ConstBufferSequence` yields const slices. This is handled
37  
    `ConstBufferSequence` yields const slices. This is handled
38  
    automatically through `slice_type<BS>`.
38  
    automatically through `slice_type<BS>`.
39  

39  

40  
    @par Thread Safety
40  
    @par Thread Safety
41  
    Not thread-safe.
41  
    Not thread-safe.
42  

42  

43  
    @par Example
43  
    @par Example
44  
    @code
44  
    @code
45  
    // Test all split points of a buffer
45  
    // Test all split points of a buffer
46  
    std::string data = "hello world";
46  
    std::string data = "hello world";
47  
    auto cb = make_buffer( data );
47  
    auto cb = make_buffer( data );
48  

48  

49  
    fuse f;
49  
    fuse f;
50  
    auto r = f.inert( [&]( fuse& ) -> task<> {
50  
    auto r = f.inert( [&]( fuse& ) -> task<> {
51  
        bufgrind bg( cb );
51  
        bufgrind bg( cb );
52  
        while( bg ) {
52  
        while( bg ) {
53  
            auto [b1, b2] = co_await bg.next();
53  
            auto [b1, b2] = co_await bg.next();
54  
            // b1 contains first N bytes
54  
            // b1 contains first N bytes
55  
            // b2 contains remaining bytes
55  
            // b2 contains remaining bytes
56  
            // concatenating b1 + b2 equals original
56  
            // concatenating b1 + b2 equals original
57  
            co_await some_async_operation( b1, b2 );
57  
            co_await some_async_operation( b1, b2 );
58  
        }
58  
        }
59  
    } );
59  
    } );
60  
    @endcode
60  
    @endcode
61  

61  

62  
    @par Mutable Buffer Example
62  
    @par Mutable Buffer Example
63  
    @code
63  
    @code
64  
    // Mutable buffers preserve mutability
64  
    // Mutable buffers preserve mutability
65  
    char data[100];
65  
    char data[100];
66  
    mutable_buffer mb( data, sizeof( data ) );
66  
    mutable_buffer mb( data, sizeof( data ) );
67  

67  

68  
    bufgrind bg( mb );
68  
    bufgrind bg( mb );
69  
    while( bg ) {
69  
    while( bg ) {
70  
        auto [b1, b2] = co_await bg.next();
70  
        auto [b1, b2] = co_await bg.next();
71  
        // b1, b2 yield mutable_buffer when iterated
71  
        // b1, b2 yield mutable_buffer when iterated
72  
    }
72  
    }
73  
    @endcode
73  
    @endcode
74  

74  

75  
    @par Step Size Example
75  
    @par Step Size Example
76  
    @code
76  
    @code
77  
    // Skip by 10 bytes for faster iteration
77  
    // Skip by 10 bytes for faster iteration
78  
    bufgrind bg( cb, 10 );
78  
    bufgrind bg( cb, 10 );
79  
    while( bg ) {
79  
    while( bg ) {
80  
        auto [b1, b2] = co_await bg.next();
80  
        auto [b1, b2] = co_await bg.next();
81  
        // Visits positions 0, 10, 20, ..., and always size
81  
        // Visits positions 0, 10, 20, ..., and always size
82  
    }
82  
    }
83  
    @endcode
83  
    @endcode
84  

84  

85  
    @see prefix, sans_prefix, slice_type
85  
    @see prefix, sans_prefix, slice_type
86  
*/
86  
*/
87  
template<ConstBufferSequence BS>
87  
template<ConstBufferSequence BS>
88  
class bufgrind
88  
class bufgrind
89  
{
89  
{
90  
    BS const& bs_;
90  
    BS const& bs_;
91  
    std::size_t size_;
91  
    std::size_t size_;
92  
    std::size_t step_;
92  
    std::size_t step_;
93  
    std::size_t pos_ = 0;
93  
    std::size_t pos_ = 0;
94  

94  

95  
public:
95  
public:
96  
    /// The type returned by @ref next.
96  
    /// The type returned by @ref next.
97  
    using split_type = std::pair<slice_type<BS>, slice_type<BS>>;
97  
    using split_type = std::pair<slice_type<BS>, slice_type<BS>>;
98  

98  

99  
    /** Construct a buffer grinder.
99  
    /** Construct a buffer grinder.
100  

100  

101  
        @param bs The buffer sequence to iterate over.
101  
        @param bs The buffer sequence to iterate over.
102  

102  

103  
        @param step The number of bytes to advance on each call to
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
104  
        @ref next. A value of 0 is treated as 1. The final split
105  
        at `buffer_size( bs )` is always included regardless of
105  
        at `buffer_size( bs )` is always included regardless of
106  
        step alignment.
106  
        step alignment.
107  
    */
107  
    */
108  
    explicit
108  
    explicit
109  
    bufgrind(
109  
    bufgrind(
110  
        BS const& bs,
110  
        BS const& bs,
111  
        std::size_t step = 1) noexcept
111  
        std::size_t step = 1) noexcept
112  
        : bs_(bs)
112  
        : bs_(bs)
113  
        , size_(buffer_size(bs))
113  
        , size_(buffer_size(bs))
114  
        , step_(step > 0 ? step : 1)
114  
        , step_(step > 0 ? step : 1)
115  
    {
115  
    {
116  
    }
116  
    }
117  

117  

118  
    /** Check if more split points remain.
118  
    /** Check if more split points remain.
119  

119  

120  
        @return `true` if @ref next can be called, `false` otherwise.
120  
        @return `true` if @ref next can be called, `false` otherwise.
121  
    */
121  
    */
122  
    explicit operator bool() const noexcept
122  
    explicit operator bool() const noexcept
123  
    {
123  
    {
124  
        return pos_ <= size_;
124  
        return pos_ <= size_;
125  
    }
125  
    }
126  

126  

127  
    /** Awaitable returned by @ref next.
127  
    /** Awaitable returned by @ref next.
128  
    */
128  
    */
129  
    struct next_awaitable
129  
    struct next_awaitable
130  
    {
130  
    {
131  
        bufgrind* self_;
131  
        bufgrind* self_;
132  

132  

133  
        bool await_ready() const noexcept { return true; }
133  
        bool await_ready() const noexcept { return true; }
134  
        coro await_suspend(coro h, executor_ref, std::stop_token) const noexcept { return h; }
134  
        coro await_suspend(coro h, executor_ref, std::stop_token) const noexcept { return h; }
135  

135  

136  
        split_type
136  
        split_type
137  
        await_resume()
137  
        await_resume()
138  
        {
138  
        {
139  
            auto b1 = prefix(self_->bs_, self_->pos_);
139  
            auto b1 = prefix(self_->bs_, self_->pos_);
140  
            auto b2 = sans_prefix(self_->bs_, self_->pos_);
140  
            auto b2 = sans_prefix(self_->bs_, self_->pos_);
141  
            if(self_->pos_ < self_->size_)
141  
            if(self_->pos_ < self_->size_)
142  
                self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
142  
                self_->pos_ = (std::min)(self_->pos_ + self_->step_, self_->size_);
143  
            else
143  
            else
144  
                ++self_->pos_;
144  
                ++self_->pos_;
145  
            return {std::move(b1), std::move(b2)};
145  
            return {std::move(b1), std::move(b2)};
146  
        }
146  
        }
147  
    };
147  
    };
148  

148  

149  
    /** Return the next split point.
149  
    /** Return the next split point.
150  

150  

151  
        Returns an awaitable that yields the current (b1, b2) pair
151  
        Returns an awaitable that yields the current (b1, b2) pair
152  
        and advances to the next split point.
152  
        and advances to the next split point.
153  

153  

154  
        @par Preconditions
154  
        @par Preconditions
155  
        `static_cast<bool>( *this )` is `true`.
155  
        `static_cast<bool>( *this )` is `true`.
156  

156  

157  
        @return An awaitable yielding `split_type`.
157  
        @return An awaitable yielding `split_type`.
158  
    */
158  
    */
159  
    next_awaitable
159  
    next_awaitable
160  
    next() noexcept
160  
    next() noexcept
161  
    {
161  
    {
162  
        return {this};
162  
        return {this};
163  
    }
163  
    }
164  
};
164  
};
165  

165  

166  
} // test
166  
} // test
167  
} // capy
167  
} // capy
168  
} // boost
168  
} // boost
169  

169  

170  
#endif
170  
#endif