Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/http
8 : //
9 :
10 : #ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
11 : #define BOOST_HTTP_JSON_JSON_SINK_HPP
12 :
13 : #include <boost/http/config.hpp>
14 :
15 : #include <boost/capy/buffers.hpp>
16 : #include <boost/capy/concept/const_buffer_sequence.hpp>
17 : #include <boost/capy/ex/immediate.hpp>
18 : #include <boost/capy/io_result.hpp>
19 : #include <boost/json/stream_parser.hpp>
20 : #include <boost/json/value.hpp>
21 :
22 : namespace boost {
23 : namespace http {
24 :
25 : /** A sink for streaming JSON data to a parser.
26 :
27 : This class wraps a `boost::json::stream_parser` and satisfies the
28 : @ref capy::WriteSink concept, enabling incremental JSON parsing
29 : from any data source that produces buffer sequences.
30 :
31 : Since JSON parsing is synchronous, all operations return
32 : @ref capy::immediate awaitables with zero suspension overhead.
33 :
34 : @par Example
35 : @code
36 : json_sink sink;
37 :
38 : // Write JSON data incrementally
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);
41 :
42 : // Or use write_eof() separately
43 : auto [ec3] = co_await sink.write_eof();
44 :
45 : // Retrieve the parsed value
46 : json::value v = sink.release();
47 : @endcode
48 :
49 : @par Thread Safety
50 : Distinct objects: Safe.
51 : Shared objects: Unsafe.
52 :
53 : @see capy::WriteSink, json::stream_parser
54 : */
55 : class json_sink
56 : {
57 : json::stream_parser parser_;
58 :
59 : template<capy::ConstBufferSequence CB>
60 : capy::immediate<capy::io_result<std::size_t>>
61 12 : write_impl(CB const& buffers, bool eof)
62 : {
63 12 : system::error_code ec;
64 12 : std::size_t total = 0;
65 12 : auto const end = capy::end(buffers);
66 23 : for(auto it = capy::begin(buffers); it != end; ++it)
67 : {
68 12 : capy::const_buffer buf(*it);
69 24 : auto n = parser_.write(
70 12 : static_cast<char const*>(buf.data()),
71 : buf.size(),
72 : ec);
73 12 : total += n;
74 12 : if(ec.failed())
75 1 : return {ec, total};
76 : }
77 11 : if(eof)
78 : {
79 6 : parser_.finish(ec);
80 6 : if(ec.failed())
81 0 : return {ec, total};
82 : }
83 11 : return capy::ready(total);
84 : }
85 :
86 : public:
87 : /** Default constructor.
88 :
89 : Constructs a sink with a default-initialized stream parser.
90 : */
91 8 : json_sink() = default;
92 :
93 : /** Constructor with parse options.
94 :
95 : @param opt Options controlling JSON parsing behavior.
96 : */
97 : explicit
98 1 : json_sink(json::parse_options const& opt)
99 1 : : parser_(json::storage_ptr(), opt)
100 : {
101 1 : }
102 :
103 : /** Constructor with storage and parse options.
104 :
105 : @param sp The storage to use for parsed values.
106 : @param opt Options controlling JSON parsing behavior.
107 : */
108 0 : json_sink(
109 : json::storage_ptr sp,
110 : json::parse_options const& opt = {})
111 0 : : parser_(std::move(sp), opt)
112 : {
113 0 : }
114 :
115 : /** Write some data to the JSON parser.
116 :
117 : Writes bytes from the buffer sequence to the stream parser.
118 :
119 : @param buffers Buffer sequence containing JSON data.
120 :
121 : @return An awaitable yielding `(error_code,std::size_t)`.
122 : On success, returns the total bytes written.
123 : */
124 : template<capy::ConstBufferSequence CB>
125 : capy::immediate<capy::io_result<std::size_t>>
126 : write_some(CB const& buffers)
127 : {
128 : return write_impl(buffers, false);
129 : }
130 :
131 : /** Write data to the JSON parser.
132 :
133 : Writes all bytes from the buffer sequence to the stream parser.
134 :
135 : @param buffers Buffer sequence containing JSON data.
136 :
137 : @return An awaitable yielding `(error_code,std::size_t)`.
138 : On success, returns the total bytes written.
139 : */
140 : template<capy::ConstBufferSequence CB>
141 : capy::immediate<capy::io_result<std::size_t>>
142 5 : write(CB const& buffers)
143 : {
144 5 : return write_impl(buffers, false);
145 : }
146 :
147 : /** Write data with optional end-of-stream.
148 :
149 : Writes all bytes from the buffer sequence to the stream parser.
150 : If @p eof is true, also finishes parsing.
151 :
152 : @param buffers Buffer sequence containing JSON data.
153 : @param eof If true, signals end of JSON data after writing.
154 :
155 : @return An awaitable yielding `(error_code,std::size_t)`.
156 : On success, returns the total bytes written.
157 : */
158 : template<capy::ConstBufferSequence CB>
159 : capy::immediate<capy::io_result<std::size_t>>
160 7 : write(CB const& buffers, bool eof)
161 : {
162 7 : return write_impl(buffers, eof);
163 : }
164 :
165 : /** Write final data and signal end of JSON data.
166 :
167 : Writes all bytes from the buffer sequence to the stream
168 : parser, then finishes parsing.
169 :
170 : @param buffers Buffer sequence containing JSON data.
171 :
172 : @return An awaitable yielding `(error_code,std::size_t)`.
173 : On success, returns the total bytes written.
174 : */
175 : template<capy::ConstBufferSequence CB>
176 : capy::immediate<capy::io_result<std::size_t>>
177 : write_eof(CB const& buffers)
178 : {
179 : return write_impl(buffers, true);
180 : }
181 :
182 : /** Signal end of JSON data.
183 :
184 : Finishes parsing and validates the JSON is complete.
185 :
186 : @return An awaitable yielding `(error_code)`.
187 : */
188 : capy::immediate<capy::io_result<>>
189 2 : write_eof()
190 : {
191 2 : system::error_code ec;
192 2 : parser_.finish(ec);
193 2 : if(ec.failed())
194 1 : return {ec};
195 1 : return {};
196 : }
197 :
198 : /** Check if parsing is complete.
199 :
200 : @return `true` if a complete JSON value has been parsed.
201 : */
202 : bool
203 11 : done() const noexcept
204 : {
205 11 : return parser_.done();
206 : }
207 :
208 : /** Release the parsed JSON value.
209 :
210 : Returns the parsed value and resets the parser for reuse.
211 :
212 : @par Preconditions
213 : `this->done() == true`
214 :
215 : @return The parsed JSON value.
216 : */
217 : json::value
218 7 : release()
219 : {
220 7 : return parser_.release();
221 : }
222 :
223 : /** Reset the parser for a new JSON value.
224 :
225 : Clears all state and prepares to parse a new value.
226 : */
227 : void
228 1 : reset()
229 : {
230 1 : parser_.reset();
231 1 : }
232 : };
233 :
234 : } // namespace http
235 : } // namespace boost
236 :
237 : #endif
|