/src/boost/boost/json/stream_parser.hpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (c) 2019 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/boostorg/json |
8 | | // |
9 | | |
10 | | #ifndef BOOST_JSON_STREAM_PARSER_HPP |
11 | | #define BOOST_JSON_STREAM_PARSER_HPP |
12 | | |
13 | | #include <boost/json/detail/config.hpp> |
14 | | #include <boost/json/basic_parser.hpp> |
15 | | #include <boost/json/parse_options.hpp> |
16 | | #include <boost/json/storage_ptr.hpp> |
17 | | #include <boost/json/value.hpp> |
18 | | #include <boost/json/detail/handler.hpp> |
19 | | #include <type_traits> |
20 | | #include <cstddef> |
21 | | |
22 | | namespace boost { |
23 | | namespace json { |
24 | | |
25 | | //---------------------------------------------------------- |
26 | | |
27 | | /** A DOM parser for JSON text contained in multiple buffers. |
28 | | |
29 | | This class is used to parse a JSON text contained in a series of one or |
30 | | more character buffers, into a @ref value container. It implements a |
31 | | [_streaming algorithm_](https://en.wikipedia.org/wiki/Streaming_algorithm), |
32 | | allowing these parsing strategies: |
33 | | |
34 | | @li parse a JSON file a piece at a time; |
35 | | @li parse incoming JSON text as it arrives, one buffer at a time; |
36 | | @li parse with bounded resource consumption per cycle. |
37 | | |
38 | | @par Usage |
39 | | To use the parser first construct it, then optionally call @ref reset to |
40 | | specify a @ref storage_ptr to use for the resulting @ref value. Then call |
41 | | @ref write one or more times to parse a single, complete JSON text. Call |
42 | | @ref done to determine if the parse has completed. To indicate there are no |
43 | | more buffers, call @ref finish. If the parse is successful, call @ref |
44 | | release to take ownership of the value: |
45 | | |
46 | | @code |
47 | | stream_parser p; // construct a parser |
48 | | p.write( "[1,2" ); // parse some of a JSON text |
49 | | p.write( ",3,4]" ); // parse the rest of the JSON text |
50 | | assert( p.done() ); // we have a complete JSON text |
51 | | value jv = p.release(); // take ownership of the value |
52 | | @endcode |
53 | | |
54 | | @par Extra Data |
55 | | When the character buffer provided as input contains additional data that |
56 | | is not part of the complete JSON text, an error is returned. The @ref |
57 | | write_some function is an alternative which allows the parse to finish |
58 | | early, without consuming all the characters in the buffer. This allows |
59 | | parsing of a buffer containing multiple individual JSON texts or containing |
60 | | different protocol data: |
61 | | |
62 | | @code |
63 | | stream_parser p; // construct a parser |
64 | | std::size_t n; // number of characters used |
65 | | n = p.write_some( "[1,2" ); // parse some of a JSON text |
66 | | assert( n == 4 ); // all characters consumed |
67 | | n = p.write_some( ",3,4] null" ); // parse the remainder of the JSON text |
68 | | assert( n == 6 ); // only some characters consumed |
69 | | assert( p.done() ); // we have a complete JSON text |
70 | | value jv = p.release(); // take ownership of the value |
71 | | @endcode |
72 | | |
73 | | @par Temporary Storage |
74 | | The parser may dynamically allocate temporary storage as needed to |
75 | | accommodate the nesting level of the JSON text being parsed. Temporary |
76 | | storage is first obtained from an optional, caller-owned buffer specified |
77 | | upon construction. When that is exhausted, the next allocation uses the |
78 | | @ref boost::container::pmr::memory_resource passed to the constructor; if |
79 | | no such argument is specified, the default memory resource is used. |
80 | | Temporary storage is freed only when the parser is destroyed; The |
81 | | performance of parsing multiple JSON texts may be improved by reusing the |
82 | | same parser instance. |
83 | | |
84 | | It is important to note that the @ref |
85 | | boost::container::pmr::memory_resource supplied upon construction is used |
86 | | for temporary storage only, and not for allocating the elements which make |
87 | | up the parsed value. That other memory resource is optionally supplied in |
88 | | each call to @ref reset. |
89 | | |
90 | | @par Duplicate Keys |
91 | | If there are object elements with duplicate keys; that is, if multiple |
92 | | elements in an object have keys that compare equal, only the last |
93 | | equivalent element will be inserted. |
94 | | |
95 | | @par Non-Standard JSON |
96 | | The @ref parse_options structure optionally provided upon construction is |
97 | | used to customize some parameters of the parser, including which |
98 | | non-standard JSON extensions should be allowed. A default-constructed parse |
99 | | options allows only standard JSON. |
100 | | |
101 | | @par Thread Safety |
102 | | Distinct instances may be accessed concurrently. Non-const member functions |
103 | | of a shared instance may not be called concurrently with any other member |
104 | | functions of that instance. |
105 | | |
106 | | @see @ref parse, @ref parser, @ref parse_options. |
107 | | */ |
108 | | class stream_parser |
109 | | { |
110 | | basic_parser<detail::handler> p_; |
111 | | |
112 | | public: |
113 | | /** Destructor. |
114 | | |
115 | | All dynamically allocated memory, including |
116 | | any incomplete parsing results, is freed. |
117 | | |
118 | | @par Complexity |
119 | | Linear in the size of partial results |
120 | | |
121 | | @par Exception Safety |
122 | | No-throw guarantee. |
123 | | */ |
124 | 15.8k | ~stream_parser() = default; |
125 | | |
126 | | /** Constructors. |
127 | | |
128 | | Construct a new parser. |
129 | | |
130 | | The parser will only support standard JSON if overloads **(1)** |
131 | | or **(2)** are used. Otherwise the parser will support extensions |
132 | | specified by the parameter `opt`. |
133 | | |
134 | | The parsed value will use the \<\<default_memory_resource,default |
135 | | memory resource\>\> for storage. To use a different resource, call @ref |
136 | | reset after construction. |
137 | | |
138 | | The main difference between the overloads is in what the constructed |
139 | | parser will use for temporary storage: |
140 | | |
141 | | @li **(1)** the constructed parser uses the default memory resource for |
142 | | temporary storage. |
143 | | |
144 | | @li **(2)**, **(3)** the constructed parser uses the memory resource of |
145 | | `sp` for temporary storage. |
146 | | |
147 | | @li **(4)**, **(6)** the constructed parser first uses the caller-owned |
148 | | storage `[buffer, buffer + size)` for temporary storage, falling back |
149 | | to the memory resource of `sp` if needed. |
150 | | |
151 | | @li **(5)**, **(7)** the constructed parser first uses the caller-owned |
152 | | storage `[buffer, buffer + N)` for temporary storage, falling back to |
153 | | the memory resource of `sp` if needed. |
154 | | |
155 | | @note Ownership of `buffer` is not transferred. The caller is |
156 | | responsible for ensuring the lifetime of the storage pointed to by |
157 | | `buffer` extends until the parser is destroyed. |
158 | | |
159 | | Overload **(8)** is the copy constructor. The type is neither copyable |
160 | | nor movable, so the overload is deleted. |
161 | | |
162 | | @par Complexity |
163 | | Constant. |
164 | | |
165 | | @par Exception Safety |
166 | | No-throw guarantee. |
167 | | |
168 | | @{ |
169 | | */ |
170 | | stream_parser() noexcept |
171 | | : stream_parser({}, {}) |
172 | 0 | { |
173 | 0 | } |
174 | | |
175 | | |
176 | | /** Overload |
177 | | |
178 | | @param sp The memory resource to use for temporary storage. |
179 | | */ |
180 | | explicit |
181 | | stream_parser(storage_ptr sp) noexcept |
182 | | : stream_parser(std::move(sp), {}) |
183 | 0 | { |
184 | 0 | } |
185 | | |
186 | | /** Overload |
187 | | |
188 | | @param opt The parsing options to use. |
189 | | @param sp |
190 | | */ |
191 | | BOOST_JSON_DECL |
192 | | stream_parser( |
193 | | storage_ptr sp, |
194 | | parse_options const& opt) noexcept; |
195 | | |
196 | | /** Overload |
197 | | @param buffer A pointer to valid storage. |
198 | | @param size The number of valid bytes in `buffer`. |
199 | | @param sp |
200 | | @param opt |
201 | | */ |
202 | | BOOST_JSON_DECL |
203 | | stream_parser( |
204 | | storage_ptr sp, |
205 | | parse_options const& opt, |
206 | | unsigned char* buffer, |
207 | | std::size_t size) noexcept; |
208 | | |
209 | | /** Overload |
210 | | |
211 | | @tparam N The number of valid bytes in `buffer`. |
212 | | @param sp |
213 | | @param opt |
214 | | @param buffer |
215 | | */ |
216 | | template<std::size_t N> |
217 | | stream_parser( |
218 | | storage_ptr sp, |
219 | | parse_options const& opt, |
220 | | unsigned char(&buffer)[N]) noexcept |
221 | 0 | : stream_parser(std::move(sp), |
222 | 0 | opt, &buffer[0], N) |
223 | 0 | { |
224 | 0 | } |
225 | | |
226 | | #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS) |
227 | | /** Overload |
228 | | |
229 | | @param sp |
230 | | @param opt |
231 | | @param buffer |
232 | | @param size |
233 | | */ |
234 | | stream_parser( |
235 | | storage_ptr sp, |
236 | | parse_options const& opt, |
237 | | std::byte* buffer, |
238 | | std::size_t size) noexcept |
239 | | : stream_parser(sp, opt, reinterpret_cast< |
240 | | unsigned char*>(buffer), size) |
241 | 0 | { |
242 | 0 | } |
243 | | |
244 | | /** Overload |
245 | | |
246 | | @tparam N |
247 | | @param sp |
248 | | @param opt |
249 | | @param buffer |
250 | | */ |
251 | | template<std::size_t N> |
252 | | stream_parser( |
253 | | storage_ptr sp, |
254 | | parse_options const& opt, |
255 | | std::byte(&buffer)[N]) noexcept |
256 | | : stream_parser(std::move(sp), |
257 | | opt, &buffer[0], N) |
258 | | { |
259 | | } |
260 | | #endif |
261 | | |
262 | | #ifndef BOOST_JSON_DOCS |
263 | | // Safety net for accidental buffer overflows |
264 | | template<std::size_t N> |
265 | | stream_parser( |
266 | | storage_ptr sp, |
267 | | parse_options const& opt, |
268 | | unsigned char(&buffer)[N], |
269 | | std::size_t n) noexcept |
270 | | : stream_parser(std::move(sp), |
271 | | opt, &buffer[0], n) |
272 | | { |
273 | | // If this goes off, check your parameters |
274 | | // closely, chances are you passed an array |
275 | | // thinking it was a pointer. |
276 | | BOOST_ASSERT(n <= N); |
277 | | } |
278 | | |
279 | | #ifdef __cpp_lib_byte |
280 | | // Safety net for accidental buffer overflows |
281 | | template<std::size_t N> |
282 | | stream_parser( |
283 | | storage_ptr sp, |
284 | | parse_options const& opt, |
285 | | std::byte(&buffer)[N], std::size_t n) noexcept |
286 | | : stream_parser(std::move(sp), |
287 | | opt, &buffer[0], n) |
288 | | { |
289 | | // If this goes off, check your parameters |
290 | | // closely, chances are you passed an array |
291 | | // thinking it was a pointer. |
292 | | BOOST_ASSERT(n <= N); |
293 | | } |
294 | | #endif |
295 | | #endif |
296 | | |
297 | | /// Overload |
298 | | stream_parser( |
299 | | stream_parser const&) = delete; |
300 | | /// @} |
301 | | |
302 | | /** Assignment operator. |
303 | | |
304 | | This type is neither copyable nor movable, so copy assignment operator |
305 | | is deleted. |
306 | | */ |
307 | | stream_parser& operator=( |
308 | | stream_parser const&) = delete; |
309 | | |
310 | | /** Reset the parser for a new JSON text. |
311 | | |
312 | | This function is used to reset the parser to prepare it for parsing |
313 | | a new complete JSON text. Any previous partial results are destroyed. |
314 | | The new value will use the memory resource of `sp`. |
315 | | |
316 | | @par Complexity |
317 | | Constant or linear in the size of any previous partial parsing results. |
318 | | |
319 | | @par Exception Safety |
320 | | No-throw guarantee. |
321 | | |
322 | | @param sp A pointer to the @ref boost::container::pmr::memory_resource. |
323 | | */ |
324 | | BOOST_JSON_DECL |
325 | | void |
326 | | reset(storage_ptr sp = {}) noexcept; |
327 | | |
328 | | /** Check if a complete JSON text has been parsed. |
329 | | |
330 | | This function returns `true` when all of these conditions are met: |
331 | | |
332 | | @li A complete serialized JSON text has been presented to the parser, |
333 | | and |
334 | | |
335 | | @li No error has occurred since the parser was constructed, or since |
336 | | the last call to @ref reset, |
337 | | |
338 | | @par Complexity |
339 | | Constant. |
340 | | |
341 | | @par Exception Safety |
342 | | No-throw guarantee. |
343 | | */ |
344 | | bool |
345 | | done() const noexcept |
346 | 0 | { |
347 | 0 | return p_.done(); |
348 | 0 | } |
349 | | |
350 | | /** Parse a buffer containing all or part of a complete JSON text. |
351 | | |
352 | | This function parses JSON text contained in the specified character |
353 | | buffer. If parsing completes, any additional characters past the end of |
354 | | the complete JSON text are ignored. The function returns the actual |
355 | | number of characters parsed, which may be less than the size of the |
356 | | input. This allows parsing of a buffer containing multiple individual |
357 | | JSON texts or containing different protocol data. |
358 | | |
359 | | Overloads **(1)**, **(2)**, **(4)**, and **(5)** report errors by |
360 | | setting `ec`. Overloads **(3)** and **(6)** report errors by throwing |
361 | | exceptions. Upon error or exception, subsequent calls will fail until |
362 | | @ref reset is called to parse a new JSON text. |
363 | | |
364 | | @note To indicate there are no more character buffers, such as when |
365 | | @ref done returns `false` after writing, call @ref finish. |
366 | | |
367 | | @par Example |
368 | | @code |
369 | | stream_parser p; // construct a parser |
370 | | std::size_t n; // number of characters used |
371 | | n = p.write_some( "[1,2" ); // parse the first part of the JSON text |
372 | | assert( n == 4 ); // all characters consumed |
373 | | n = p.write_some( "3,4] null" ); // parse the rest of the JSON text |
374 | | assert( n == 5 ); // only some characters consumed |
375 | | value jv = p.release(); // take ownership of the value |
376 | | @endcode |
377 | | |
378 | | @par Complexity |
379 | | @li **(1)**--**(3)** linear in `size`. |
380 | | @li **(4)**--**(6)** linear in `s.size()`. |
381 | | |
382 | | @par Exception Safety |
383 | | Basic guarantee. Calls to `memory_resource::allocate` may throw. |
384 | | |
385 | | @return The number of characters consumed from the buffer. |
386 | | |
387 | | @param data A pointer to a buffer of `size` characters to parse. |
388 | | @param size The number of characters pointed to by `data`. |
389 | | @param ec Set to the error, if any occurred. |
390 | | |
391 | | @{ |
392 | | */ |
393 | | BOOST_JSON_DECL |
394 | | std::size_t |
395 | | write_some( |
396 | | char const* data, |
397 | | std::size_t size, |
398 | | system::error_code& ec); |
399 | | |
400 | | BOOST_JSON_DECL |
401 | | std::size_t |
402 | | write_some( |
403 | | char const* data, |
404 | | std::size_t size, |
405 | | std::error_code& ec); |
406 | | |
407 | | /** Overload |
408 | | |
409 | | @param data |
410 | | @param size |
411 | | |
412 | | @throw boost::system::system_error Thrown on error. |
413 | | */ |
414 | | BOOST_JSON_DECL |
415 | | std::size_t |
416 | | write_some( |
417 | | char const* data, |
418 | | std::size_t size); |
419 | | |
420 | | /** Overload |
421 | | @param s The character string to parse. |
422 | | @param ec |
423 | | */ |
424 | | std::size_t |
425 | | write_some( |
426 | | string_view s, |
427 | | system::error_code& ec) |
428 | 0 | { |
429 | 0 | return write_some( |
430 | 0 | s.data(), s.size(), ec); |
431 | 0 | } |
432 | | |
433 | | /** Overload |
434 | | @param s |
435 | | @param ec |
436 | | */ |
437 | | std::size_t |
438 | | write_some( |
439 | | string_view s, |
440 | | std::error_code& ec) |
441 | 0 | { |
442 | 0 | return write_some( |
443 | 0 | s.data(), s.size(), ec); |
444 | 0 | } |
445 | | |
446 | | /** Overload |
447 | | @param s |
448 | | */ |
449 | | std::size_t |
450 | | write_some( |
451 | | string_view s) |
452 | 0 | { |
453 | 0 | return write_some( |
454 | 0 | s.data(), s.size()); |
455 | 0 | } |
456 | | /// @} |
457 | | |
458 | | /** Parse a buffer containing all or part of a complete JSON text. |
459 | | |
460 | | This function parses all or part of a JSON text contained in the |
461 | | specified character buffer. The entire buffer must be consumed; if |
462 | | there are additional characters past the end of the complete JSON text, |
463 | | the parse fails and an error is returned. |
464 | | |
465 | | Overloads **(1)**, **(2)**, **(4)**, and **(5)** report errors by |
466 | | setting `ec`. Overloads **(3)** and **(6)** report errors by throwing |
467 | | exceptions. Upon error or exception, subsequent calls will fail until |
468 | | @ref reset is called to parse a new JSON text. |
469 | | |
470 | | @note To indicate there are no more character buffers, such as when |
471 | | @ref done returns `false` after writing, call @ref finish. |
472 | | |
473 | | @par Example |
474 | | @code |
475 | | stream_parser p; // construct a parser |
476 | | std::size_t n; // number of characters used |
477 | | n = p.write( "[1,2" ); // parse some of the JSON text |
478 | | assert( n == 4 ); // all characters consumed |
479 | | n = p.write( "3,4]" ); // parse the rest of the JSON text |
480 | | assert( n == 4 ); // all characters consumed |
481 | | value jv = p.release(); // take ownership of the value |
482 | | @endcode |
483 | | |
484 | | @par Complexity |
485 | | @li **(1)**--**(3)** linear in `size`. |
486 | | @li **(4)**--**(6)** linear in `s.size()`. |
487 | | |
488 | | @par Exception Safety |
489 | | Basic guarantee. Calls to `memory_resource::allocate` may throw. |
490 | | @return The number of characters consumed from the buffer. |
491 | | |
492 | | @param data A pointer to a buffer of `size` characters to parse. |
493 | | |
494 | | @param size The number of characters pointed to by `data`. |
495 | | |
496 | | @param ec Set to the error, if any occurred. |
497 | | |
498 | | @{ |
499 | | */ |
500 | | BOOST_JSON_DECL |
501 | | std::size_t |
502 | | write( |
503 | | char const* data, |
504 | | std::size_t size, |
505 | | system::error_code& ec); |
506 | | |
507 | | BOOST_JSON_DECL |
508 | | std::size_t |
509 | | write( |
510 | | char const* data, |
511 | | std::size_t size, |
512 | | std::error_code& ec); |
513 | | |
514 | | /** Overload |
515 | | |
516 | | @param data |
517 | | @param size |
518 | | |
519 | | @throw boost::system::system_error Thrown on error. |
520 | | */ |
521 | | BOOST_JSON_DECL |
522 | | std::size_t |
523 | | write( |
524 | | char const* data, |
525 | | std::size_t size); |
526 | | |
527 | | /** Overload |
528 | | |
529 | | @param s The character string to parse. |
530 | | @param ec |
531 | | */ |
532 | | std::size_t |
533 | | write( |
534 | | string_view s, |
535 | | system::error_code& ec) |
536 | | { |
537 | | return write( |
538 | | s.data(), s.size(), ec); |
539 | | } |
540 | | |
541 | | /** Overload |
542 | | |
543 | | @param s |
544 | | @param ec |
545 | | */ |
546 | | std::size_t |
547 | | write( |
548 | | string_view s, |
549 | | std::error_code& ec) |
550 | 0 | { |
551 | 0 | return write( |
552 | 0 | s.data(), s.size(), ec); |
553 | 0 | } |
554 | | |
555 | | /** Overload |
556 | | @param s |
557 | | */ |
558 | | std::size_t |
559 | | write( |
560 | | string_view s) |
561 | 0 | { |
562 | 0 | return write( |
563 | 0 | s.data(), s.size()); |
564 | 0 | } |
565 | | /// @} |
566 | | |
567 | | /** Indicate the end of JSON input. |
568 | | |
569 | | This function is used to indicate that there are no more character |
570 | | buffers in the current JSON text being parsed. If the resulting JSON |
571 | | text is incomplete, **(1)** and **(2)** assign the relevant |
572 | | `error_code` to `ec`, while **(3)** throws an exception. |
573 | | |
574 | | Upon error or exception, subsequent calls will fail until @ref reset is |
575 | | called to parse a new JSON text. |
576 | | |
577 | | @par Example |
578 | | In the code below, @ref finish is called to |
579 | | indicate there are no more digits in the |
580 | | resulting number: |
581 | | @code |
582 | | stream_parser p; // construct a parser |
583 | | p.write( "3." ); // write the first part of the number |
584 | | p.write( "14" ); // write the second part of the number |
585 | | assert( ! p.done() ); // there could be more digits |
586 | | p.finish(); // indicate the end of the JSON input |
587 | | assert( p.done() ); // now we are finished |
588 | | value jv = p.release(); // take ownership of the value |
589 | | @endcode |
590 | | |
591 | | @par Complexity |
592 | | Constant. |
593 | | |
594 | | @par Exception Safety |
595 | | Basic guarantee. Calls to `memory_resource::allocate` may throw. |
596 | | |
597 | | @param ec Set to the error, if any occurred. |
598 | | |
599 | | @{ |
600 | | */ |
601 | | BOOST_JSON_DECL |
602 | | void |
603 | | finish(system::error_code& ec); |
604 | | |
605 | | BOOST_JSON_DECL |
606 | | void |
607 | | finish(std::error_code& ec); |
608 | | |
609 | | /** Overload |
610 | | |
611 | | @throw boost::system::system_error Parsing error. |
612 | | */ |
613 | | BOOST_JSON_DECL |
614 | | void |
615 | | finish(); |
616 | | /// @} |
617 | | |
618 | | /** Return the parsed JSON as a @ref value. |
619 | | |
620 | | This returns the parsed value, or throws an exception if the parsing is |
621 | | incomplete or failed. If `! this->done()`, calls @ref finish() first. |
622 | | It is necessary to call @ref reset after calling this function in order |
623 | | to parse another JSON text. |
624 | | |
625 | | @par Complexity |
626 | | Constant. |
627 | | |
628 | | @return The parsed value. Ownership of this value is transferred to the |
629 | | caller. |
630 | | |
631 | | @throw boost::system::system_error A complete JSON text hasn't been |
632 | | parsed, or parsing failed. |
633 | | */ |
634 | | BOOST_JSON_DECL |
635 | | value |
636 | | release(); |
637 | | }; |
638 | | |
639 | | } // namespace json |
640 | | } // namespace boost |
641 | | |
642 | | #endif |