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/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
10  
#ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
11  
#define BOOST_HTTP_JSON_JSON_SINK_HPP
11  
#define BOOST_HTTP_JSON_JSON_SINK_HPP
12  

12  

13  
#include <boost/http/config.hpp>
13  
#include <boost/http/config.hpp>
14  

14  

15  
#include <boost/capy/buffers.hpp>
15  
#include <boost/capy/buffers.hpp>
16  
#include <boost/capy/concept/const_buffer_sequence.hpp>
16  
#include <boost/capy/concept/const_buffer_sequence.hpp>
17  
#include <boost/capy/ex/immediate.hpp>
17  
#include <boost/capy/ex/immediate.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
19  
#include <boost/json/stream_parser.hpp>
19  
#include <boost/json/stream_parser.hpp>
20  
#include <boost/json/value.hpp>
20  
#include <boost/json/value.hpp>
21  

21  

22  
namespace boost {
22  
namespace boost {
23  
namespace http {
23  
namespace http {
24  

24  

25  
/** A sink for streaming JSON data to a parser.
25  
/** A sink for streaming JSON data to a parser.
26  

26  

27  
    This class wraps a `boost::json::stream_parser` and satisfies the
27  
    This class wraps a `boost::json::stream_parser` and satisfies the
28  
    @ref capy::WriteSink concept, enabling incremental JSON parsing
28  
    @ref capy::WriteSink concept, enabling incremental JSON parsing
29  
    from any data source that produces buffer sequences.
29  
    from any data source that produces buffer sequences.
30  

30  

31  
    Since JSON parsing is synchronous, all operations return
31  
    Since JSON parsing is synchronous, all operations return
32  
    @ref capy::immediate awaitables with zero suspension overhead.
32  
    @ref capy::immediate awaitables with zero suspension overhead.
33  

33  

34  
    @par Example
34  
    @par Example
35  
    @code
35  
    @code
36  
    json_sink sink;
36  
    json_sink sink;
37  

37  

38  
    // Write JSON data incrementally
38  
    // Write JSON data incrementally
39  
    auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":"));
39  
    auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":"));
40  
    auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true);
40  
    auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true);
41  

41  

42  
    // Or use write_eof() separately
42  
    // Or use write_eof() separately
43  
    auto [ec3] = co_await sink.write_eof();
43  
    auto [ec3] = co_await sink.write_eof();
44  

44  

45  
    // Retrieve the parsed value
45  
    // Retrieve the parsed value
46  
    json::value v = sink.release();
46  
    json::value v = sink.release();
47  
    @endcode
47  
    @endcode
48  

48  

49  
    @par Thread Safety
49  
    @par Thread Safety
50  
    Distinct objects: Safe.
50  
    Distinct objects: Safe.
51  
    Shared objects: Unsafe.
51  
    Shared objects: Unsafe.
52  

52  

53  
    @see capy::WriteSink, json::stream_parser
53  
    @see capy::WriteSink, json::stream_parser
54  
*/
54  
*/
55  
class json_sink
55  
class json_sink
56  
{
56  
{
57  
    json::stream_parser parser_;
57  
    json::stream_parser parser_;
58  

58  

59  
    template<capy::ConstBufferSequence CB>
59  
    template<capy::ConstBufferSequence CB>
60  
    capy::immediate<capy::io_result<std::size_t>>
60  
    capy::immediate<capy::io_result<std::size_t>>
61  
    write_impl(CB const& buffers, bool eof)
61  
    write_impl(CB const& buffers, bool eof)
62  
    {
62  
    {
63  
        system::error_code ec;
63  
        system::error_code ec;
64  
        std::size_t total = 0;
64  
        std::size_t total = 0;
65  
        auto const end = capy::end(buffers);
65  
        auto const end = capy::end(buffers);
66  
        for(auto it = capy::begin(buffers); it != end; ++it)
66  
        for(auto it = capy::begin(buffers); it != end; ++it)
67  
        {
67  
        {
68  
            capy::const_buffer buf(*it);
68  
            capy::const_buffer buf(*it);
69  
            auto n = parser_.write(
69  
            auto n = parser_.write(
70  
                static_cast<char const*>(buf.data()),
70  
                static_cast<char const*>(buf.data()),
71  
                buf.size(),
71  
                buf.size(),
72  
                ec);
72  
                ec);
73  
            total += n;
73  
            total += n;
74  
            if(ec.failed())
74  
            if(ec.failed())
75  
                return {ec, total};
75  
                return {ec, total};
76  
        }
76  
        }
77  
        if(eof)
77  
        if(eof)
78  
        {
78  
        {
79  
            parser_.finish(ec);
79  
            parser_.finish(ec);
80  
            if(ec.failed())
80  
            if(ec.failed())
81  
                return {ec, total};
81  
                return {ec, total};
82  
        }
82  
        }
83  
        return capy::ready(total);
83  
        return capy::ready(total);
84  
    }
84  
    }
85  

85  

86  
public:
86  
public:
87  
    /** Default constructor.
87  
    /** Default constructor.
88  

88  

89  
        Constructs a sink with a default-initialized stream parser.
89  
        Constructs a sink with a default-initialized stream parser.
90  
    */
90  
    */
91  
    json_sink() = default;
91  
    json_sink() = default;
92  

92  

93  
    /** Constructor with parse options.
93  
    /** Constructor with parse options.
94  

94  

95  
        @param opt Options controlling JSON parsing behavior.
95  
        @param opt Options controlling JSON parsing behavior.
96  
    */
96  
    */
97  
    explicit
97  
    explicit
98  
    json_sink(json::parse_options const& opt)
98  
    json_sink(json::parse_options const& opt)
99  
        : parser_(json::storage_ptr(), opt)
99  
        : parser_(json::storage_ptr(), opt)
100  
    {
100  
    {
101  
    }
101  
    }
102  

102  

103  
    /** Constructor with storage and parse options.
103  
    /** Constructor with storage and parse options.
104  

104  

105  
        @param sp The storage to use for parsed values.
105  
        @param sp The storage to use for parsed values.
106  
        @param opt Options controlling JSON parsing behavior.
106  
        @param opt Options controlling JSON parsing behavior.
107  
    */
107  
    */
108  
    json_sink(
108  
    json_sink(
109  
        json::storage_ptr sp,
109  
        json::storage_ptr sp,
110  
        json::parse_options const& opt = {})
110  
        json::parse_options const& opt = {})
111  
        : parser_(std::move(sp), opt)
111  
        : parser_(std::move(sp), opt)
112  
    {
112  
    {
113  
    }
113  
    }
114  

114  

115  
    /** Write some data to the JSON parser.
115  
    /** Write some data to the JSON parser.
116  

116  

117  
        Writes bytes from the buffer sequence to the stream parser.
117  
        Writes bytes from the buffer sequence to the stream parser.
118  

118  

119  
        @param buffers Buffer sequence containing JSON data.
119  
        @param buffers Buffer sequence containing JSON data.
120  

120  

121  
        @return An awaitable yielding `(error_code,std::size_t)`.
121  
        @return An awaitable yielding `(error_code,std::size_t)`.
122  
            On success, returns the total bytes written.
122  
            On success, returns the total bytes written.
123  
    */
123  
    */
124  
    template<capy::ConstBufferSequence CB>
124  
    template<capy::ConstBufferSequence CB>
125  
    capy::immediate<capy::io_result<std::size_t>>
125  
    capy::immediate<capy::io_result<std::size_t>>
126  
    write_some(CB const& buffers)
126  
    write_some(CB const& buffers)
127  
    {
127  
    {
128  
        return write_impl(buffers, false);
128  
        return write_impl(buffers, false);
129  
    }
129  
    }
130  

130  

131  
    /** Write data to the JSON parser.
131  
    /** Write data to the JSON parser.
132  

132  

133  
        Writes all bytes from the buffer sequence to the stream parser.
133  
        Writes all bytes from the buffer sequence to the stream parser.
134  

134  

135  
        @param buffers Buffer sequence containing JSON data.
135  
        @param buffers Buffer sequence containing JSON data.
136  

136  

137  
        @return An awaitable yielding `(error_code,std::size_t)`.
137  
        @return An awaitable yielding `(error_code,std::size_t)`.
138  
            On success, returns the total bytes written.
138  
            On success, returns the total bytes written.
139  
    */
139  
    */
140  
    template<capy::ConstBufferSequence CB>
140  
    template<capy::ConstBufferSequence CB>
141  
    capy::immediate<capy::io_result<std::size_t>>
141  
    capy::immediate<capy::io_result<std::size_t>>
142  
    write(CB const& buffers)
142  
    write(CB const& buffers)
143  
    {
143  
    {
144  
        return write_impl(buffers, false);
144  
        return write_impl(buffers, false);
145  
    }
145  
    }
146  

146  

147  
    /** Write data with optional end-of-stream.
147  
    /** Write data with optional end-of-stream.
148  

148  

149  
        Writes all bytes from the buffer sequence to the stream parser.
149  
        Writes all bytes from the buffer sequence to the stream parser.
150  
        If @p eof is true, also finishes parsing.
150  
        If @p eof is true, also finishes parsing.
151  

151  

152  
        @param buffers Buffer sequence containing JSON data.
152  
        @param buffers Buffer sequence containing JSON data.
153  
        @param eof If true, signals end of JSON data after writing.
153  
        @param eof If true, signals end of JSON data after writing.
154  

154  

155  
        @return An awaitable yielding `(error_code,std::size_t)`.
155  
        @return An awaitable yielding `(error_code,std::size_t)`.
156  
            On success, returns the total bytes written.
156  
            On success, returns the total bytes written.
157  
    */
157  
    */
158  
    template<capy::ConstBufferSequence CB>
158  
    template<capy::ConstBufferSequence CB>
159  
    capy::immediate<capy::io_result<std::size_t>>
159  
    capy::immediate<capy::io_result<std::size_t>>
160  
    write(CB const& buffers, bool eof)
160  
    write(CB const& buffers, bool eof)
161  
    {
161  
    {
162  
        return write_impl(buffers, eof);
162  
        return write_impl(buffers, eof);
163  
    }
163  
    }
164  

164  

165  
    /** Write final data and signal end of JSON data.
165  
    /** Write final data and signal end of JSON data.
166  

166  

167  
        Writes all bytes from the buffer sequence to the stream
167  
        Writes all bytes from the buffer sequence to the stream
168  
        parser, then finishes parsing.
168  
        parser, then finishes parsing.
169  

169  

170  
        @param buffers Buffer sequence containing JSON data.
170  
        @param buffers Buffer sequence containing JSON data.
171  

171  

172  
        @return An awaitable yielding `(error_code,std::size_t)`.
172  
        @return An awaitable yielding `(error_code,std::size_t)`.
173  
            On success, returns the total bytes written.
173  
            On success, returns the total bytes written.
174  
    */
174  
    */
175  
    template<capy::ConstBufferSequence CB>
175  
    template<capy::ConstBufferSequence CB>
176  
    capy::immediate<capy::io_result<std::size_t>>
176  
    capy::immediate<capy::io_result<std::size_t>>
177  
    write_eof(CB const& buffers)
177  
    write_eof(CB const& buffers)
178  
    {
178  
    {
179  
        return write_impl(buffers, true);
179  
        return write_impl(buffers, true);
180  
    }
180  
    }
181  

181  

182  
    /** Signal end of JSON data.
182  
    /** Signal end of JSON data.
183  

183  

184  
        Finishes parsing and validates the JSON is complete.
184  
        Finishes parsing and validates the JSON is complete.
185  

185  

186  
        @return An awaitable yielding `(error_code)`.
186  
        @return An awaitable yielding `(error_code)`.
187  
    */
187  
    */
188  
    capy::immediate<capy::io_result<>>
188  
    capy::immediate<capy::io_result<>>
189  
    write_eof()
189  
    write_eof()
190  
    {
190  
    {
191  
        system::error_code ec;
191  
        system::error_code ec;
192  
        parser_.finish(ec);
192  
        parser_.finish(ec);
193  
        if(ec.failed())
193  
        if(ec.failed())
194  
            return {ec};
194  
            return {ec};
195  
        return {};
195  
        return {};
196  
    }
196  
    }
197  

197  

198  
    /** Check if parsing is complete.
198  
    /** Check if parsing is complete.
199  

199  

200  
        @return `true` if a complete JSON value has been parsed.
200  
        @return `true` if a complete JSON value has been parsed.
201  
    */
201  
    */
202  
    bool
202  
    bool
203  
    done() const noexcept
203  
    done() const noexcept
204  
    {
204  
    {
205  
        return parser_.done();
205  
        return parser_.done();
206  
    }
206  
    }
207  

207  

208  
    /** Release the parsed JSON value.
208  
    /** Release the parsed JSON value.
209  

209  

210  
        Returns the parsed value and resets the parser for reuse.
210  
        Returns the parsed value and resets the parser for reuse.
211  

211  

212  
        @par Preconditions
212  
        @par Preconditions
213  
        `this->done() == true`
213  
        `this->done() == true`
214  

214  

215  
        @return The parsed JSON value.
215  
        @return The parsed JSON value.
216  
    */
216  
    */
217  
    json::value
217  
    json::value
218  
    release()
218  
    release()
219  
    {
219  
    {
220  
        return parser_.release();
220  
        return parser_.release();
221  
    }
221  
    }
222  

222  

223  
    /** Reset the parser for a new JSON value.
223  
    /** Reset the parser for a new JSON value.
224  

224  

225  
        Clears all state and prepares to parse a new value.
225  
        Clears all state and prepares to parse a new value.
226  
    */
226  
    */
227  
    void
227  
    void
228  
    reset()
228  
    reset()
229  
    {
229  
    {
230  
        parser_.reset();
230  
        parser_.reset();
231  
    }
231  
    }
232  
};
232  
};
233  

233  

234  
} // namespace http
234  
} // namespace http
235  
} // namespace boost
235  
} // namespace boost
236  

236  

237  
#endif
237  
#endif