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

18  

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

22  

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

26  

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

28  

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

33  

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

38  

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

41  

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

47  

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

60  

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

66  

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

73  

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

83  

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

93  

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

97  

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

99  

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

101  

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

116  

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

118  

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

125  

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

131  

132  
        bool await_ready() const noexcept { return true; }
132  
        bool await_ready() const noexcept { return true; }
133  
        std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; }
133  
        std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; }
134  

134  

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

147  

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

149  

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

152  

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

155  

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

164  

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

168  

169  
#endif
169  
#endif