1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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_IO_PUSH_TO_HPP
10  
#ifndef BOOST_CAPY_IO_PUSH_TO_HPP
11  
#define BOOST_CAPY_IO_PUSH_TO_HPP
11  
#define BOOST_CAPY_IO_PUSH_TO_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/concept/buffer_source.hpp>
15  
#include <boost/capy/concept/buffer_source.hpp>
16  
#include <boost/capy/concept/write_sink.hpp>
16  
#include <boost/capy/concept/write_sink.hpp>
17  
#include <boost/capy/concept/write_stream.hpp>
17  
#include <boost/capy/concept/write_stream.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
19  
#include <boost/capy/task.hpp>
19  
#include <boost/capy/task.hpp>
20  

20  

21  
#include <cstddef>
21  
#include <cstddef>
22  
#include <span>
22  
#include <span>
23  

23  

24  
namespace boost {
24  
namespace boost {
25  
namespace capy {
25  
namespace capy {
26  

26  

27  
/** Transfer data from a BufferSource to a WriteSink.
27  
/** Transfer data from a BufferSource to a WriteSink.
28  

28  

29  
    This function pulls data from the source and writes it to the
29  
    This function pulls data from the source and writes it to the
30  
    sink until the source is exhausted or an error occurs. When
30  
    sink until the source is exhausted or an error occurs. When
31  
    the source signals completion, `write_eof()` is called on the
31  
    the source signals completion, `write_eof()` is called on the
32  
    sink to finalize the transfer.
32  
    sink to finalize the transfer.
33  

33  

34  
    @tparam Src The source type, must satisfy @ref BufferSource.
34  
    @tparam Src The source type, must satisfy @ref BufferSource.
35  
    @tparam Sink The sink type, must satisfy @ref WriteSink.
35  
    @tparam Sink The sink type, must satisfy @ref WriteSink.
36  

36  

37  
    @param source The source to pull data from.
37  
    @param source The source to pull data from.
38  
    @param sink The sink to write data to.
38  
    @param sink The sink to write data to.
39  

39  

40  
    @return A task that yields `(std::error_code, std::size_t)`.
40  
    @return A task that yields `(std::error_code, std::size_t)`.
41  
        On success, `ec` is default-constructed (no error) and `n` is
41  
        On success, `ec` is default-constructed (no error) and `n` is
42  
        the total number of bytes transferred. On error, `ec` contains
42  
        the total number of bytes transferred. On error, `ec` contains
43  
        the error code and `n` is the total number of bytes transferred
43  
        the error code and `n` is the total number of bytes transferred
44  
        before the error.
44  
        before the error.
45  

45  

46  
    @par Example
46  
    @par Example
47  
    @code
47  
    @code
48  
    task<void> transfer_body(BufferSource auto& source, WriteSink auto& sink)
48  
    task<void> transfer_body(BufferSource auto& source, WriteSink auto& sink)
49  
    {
49  
    {
50  
        auto [ec, n] = co_await push_to(source, sink);
50  
        auto [ec, n] = co_await push_to(source, sink);
51  
        if (ec)
51  
        if (ec)
52  
        {
52  
        {
53  
            // Handle error
53  
            // Handle error
54  
        }
54  
        }
55  
        // n bytes were transferred
55  
        // n bytes were transferred
56  
    }
56  
    }
57  
    @endcode
57  
    @endcode
58  

58  

59  
    @see BufferSource, WriteSink
59  
    @see BufferSource, WriteSink
60  
*/
60  
*/
61  
template<BufferSource Src, WriteSink Sink>
61  
template<BufferSource Src, WriteSink Sink>
62  
task<io_result<std::size_t>>
62  
task<io_result<std::size_t>>
63  
push_to(Src& source, Sink& sink)
63  
push_to(Src& source, Sink& sink)
64  
{
64  
{
65  
    const_buffer arr[detail::max_iovec_];
65  
    const_buffer arr[detail::max_iovec_];
66  
    std::size_t total = 0;
66  
    std::size_t total = 0;
67  

67  

68  
    for(;;)
68  
    for(;;)
69  
    {
69  
    {
70  
        auto [ec, bufs] = co_await source.pull(arr);
70  
        auto [ec, bufs] = co_await source.pull(arr);
71  
        if(ec)
71  
        if(ec)
72  
            co_return {ec, total};
72  
            co_return {ec, total};
73  

73  

74  
        if(bufs.empty())
74  
        if(bufs.empty())
75  
        {
75  
        {
76  
            auto [eof_ec] = co_await sink.write_eof();
76  
            auto [eof_ec] = co_await sink.write_eof();
77  
            co_return {eof_ec, total};
77  
            co_return {eof_ec, total};
78  
        }
78  
        }
79  

79  

80  
        auto [write_ec, n] = co_await sink.write(bufs);
80  
        auto [write_ec, n] = co_await sink.write(bufs);
81  
        total += n;
81  
        total += n;
82  
        source.consume(n);
82  
        source.consume(n);
83  
        if(write_ec)
83  
        if(write_ec)
84  
            co_return {write_ec, total};
84  
            co_return {write_ec, total};
85  
    }
85  
    }
86  
}
86  
}
87  

87  

88  
/** Transfer data from a BufferSource to a WriteStream.
88  
/** Transfer data from a BufferSource to a WriteStream.
89  

89  

90  
    This function pulls data from the source and writes it to the
90  
    This function pulls data from the source and writes it to the
91  
    stream until the source is exhausted or an error occurs. The
91  
    stream until the source is exhausted or an error occurs. The
92  
    stream uses `write_some()` which may perform partial writes,
92  
    stream uses `write_some()` which may perform partial writes,
93  
    so this function loops until all pulled data is consumed.
93  
    so this function loops until all pulled data is consumed.
94  

94  

95  
    Unlike the WriteSink overload, this function does not signal
95  
    Unlike the WriteSink overload, this function does not signal
96  
    EOF explicitly since WriteStream does not provide a write_eof
96  
    EOF explicitly since WriteStream does not provide a write_eof
97  
    method. The transfer completes when the source is exhausted.
97  
    method. The transfer completes when the source is exhausted.
98  

98  

99  
    @tparam Src The source type, must satisfy @ref BufferSource.
99  
    @tparam Src The source type, must satisfy @ref BufferSource.
100  
    @tparam Stream The stream type, must satisfy @ref WriteStream.
100  
    @tparam Stream The stream type, must satisfy @ref WriteStream.
101  

101  

102  
    @param source The source to pull data from.
102  
    @param source The source to pull data from.
103  
    @param stream The stream to write data to.
103  
    @param stream The stream to write data to.
104  

104  

105  
    @return A task that yields `(std::error_code, std::size_t)`.
105  
    @return A task that yields `(std::error_code, std::size_t)`.
106  
        On success, `ec` is default-constructed (no error) and `n` is
106  
        On success, `ec` is default-constructed (no error) and `n` is
107  
        the total number of bytes transferred. On error, `ec` contains
107  
        the total number of bytes transferred. On error, `ec` contains
108  
        the error code and `n` is the total number of bytes transferred
108  
        the error code and `n` is the total number of bytes transferred
109  
        before the error.
109  
        before the error.
110  

110  

111  
    @par Example
111  
    @par Example
112  
    @code
112  
    @code
113  
    task<void> transfer_body(BufferSource auto& source, WriteStream auto& stream)
113  
    task<void> transfer_body(BufferSource auto& source, WriteStream auto& stream)
114  
    {
114  
    {
115  
        auto [ec, n] = co_await push_to(source, stream);
115  
        auto [ec, n] = co_await push_to(source, stream);
116  
        if (ec)
116  
        if (ec)
117  
        {
117  
        {
118  
            // Handle error
118  
            // Handle error
119  
        }
119  
        }
120  
        // n bytes were transferred
120  
        // n bytes were transferred
121  
    }
121  
    }
122  
    @endcode
122  
    @endcode
123  

123  

124  
    @see BufferSource, WriteStream, pull_from
124  
    @see BufferSource, WriteStream, pull_from
125  
*/
125  
*/
126  
template<BufferSource Src, WriteStream Stream>
126  
template<BufferSource Src, WriteStream Stream>
127  
task<io_result<std::size_t>>
127  
task<io_result<std::size_t>>
128  
push_to(Src& source, Stream& stream)
128  
push_to(Src& source, Stream& stream)
129  
{
129  
{
130  
    const_buffer arr[detail::max_iovec_];
130  
    const_buffer arr[detail::max_iovec_];
131  
    std::size_t total = 0;
131  
    std::size_t total = 0;
132  

132  

133  
    for(;;)
133  
    for(;;)
134  
    {
134  
    {
135  
        auto [ec, bufs] = co_await source.pull(arr);
135  
        auto [ec, bufs] = co_await source.pull(arr);
136  
        if(ec)
136  
        if(ec)
137  
            co_return {ec, total};
137  
            co_return {ec, total};
138  

138  

139  
        if(bufs.empty())
139  
        if(bufs.empty())
140  
            co_return {{}, total};
140  
            co_return {{}, total};
141  

141  

142  
        auto [write_ec, n] = co_await stream.write_some(bufs);
142  
        auto [write_ec, n] = co_await stream.write_some(bufs);
143  
        if(write_ec)
143  
        if(write_ec)
144  
            co_return {write_ec, total};
144  
            co_return {write_ec, total};
145  

145  

146  
        total += n;
146  
        total += n;
147  
        source.consume(n);
147  
        source.consume(n);
148  
    }
148  
    }
149  
}
149  
}
150  

150  

151  
} // namespace capy
151  
} // namespace capy
152  
} // namespace boost
152  
} // namespace boost
153  

153  

154  
#endif
154  
#endif