From 7882ccf866d967d05ba767422e7f0c376431e9df Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 27 Sep 2020 13:55:59 -0700 Subject: [PATCH] parser docs, test, and tidy --- include/boost/json/detail/basic_parser.hpp | 7 +- include/boost/json/impl/parse.ipp | 8 +- include/boost/json/impl/parser.ipp | 27 +- include/boost/json/impl/stream_parser.ipp | 4 +- include/boost/json/parser.hpp | 605 +++++++++------------ include/boost/json/pilfer.hpp | 2 +- test/parser.cpp | 352 ++++++++++++ 7 files changed, 630 insertions(+), 375 deletions(-) diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp index 7fdbd613..cd8d780f 100644 --- a/include/boost/json/detail/basic_parser.hpp +++ b/include/boost/json/detail/basic_parser.hpp @@ -39,7 +39,7 @@ BOOST_JSON_NS_BEGIN To use, first declare a variable of type `basic_parser` where `T` meets the handler requirements specified below. Then call - @ref write one or more times with the input, + @ref write_some one or more times with the input, setting `more = false` on the final buffer. The parsing events are realized through member function calls on the handler, which exists @@ -563,8 +563,9 @@ public: /** Return the last error. - This returns the last error code which was - generated in the most recent call to @ref write. + This returns the last error code which + was generated in the most recent call + to @ref write_some. @par Complexity Constant. diff --git a/include/boost/json/impl/parse.ipp b/include/boost/json/impl/parse.ipp index bf97555e..2192e864 100644 --- a/include/boost/json/impl/parse.ipp +++ b/include/boost/json/impl/parse.ipp @@ -24,11 +24,9 @@ parse( storage_ptr sp, const parse_options& opt) { - unsigned char temp[BOOST_JSON_STACK_BUFFER_SIZE]; - parser p( - storage_ptr(), - opt, - temp, sizeof(temp)); + unsigned char temp[ + BOOST_JSON_STACK_BUFFER_SIZE]; + parser p(storage_ptr(), opt, temp); p.reset(std::move(sp)); p.write(s, ec); if(ec) diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index 5f8d309d..cc0ff1f2 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -64,14 +64,7 @@ write_some( { auto const n = p_.write_some( false, data, size, ec); - if(! ec) - { - if(! p_.done()) - { - ec = error::incomplete; - p_.fail(ec); - } - } + BOOST_ASSERT(ec || p_.done()); return n; } @@ -82,8 +75,8 @@ write_some( std::size_t size) { error_code ec; - auto const n = p_.write_some( - true, data, size, ec); + auto const n = write_some( + data, size, ec); if(ec) detail::throw_system_error(ec, BOOST_CURRENT_LOCATION); @@ -99,18 +92,10 @@ write( { auto const n = write_some( data, size, ec); - if(! ec) + if(! ec && n < size) { - if(! p_.done()) - { - ec = error::incomplete; - p_.fail(ec); - } - else if(n < size) - { - ec = error::extra_data; - p_.fail(ec); - } + ec = error::extra_data; + p_.fail(ec); } return n; } diff --git a/include/boost/json/impl/stream_parser.ipp b/include/boost/json/impl/stream_parser.ipp index d736dc95..9c4c0a3f 100644 --- a/include/boost/json/impl/stream_parser.ipp +++ b/include/boost/json/impl/stream_parser.ipp @@ -73,8 +73,8 @@ write_some( std::size_t size) { error_code ec; - auto const n = p_.write_some( - true, data, size, ec); + auto const n = write_some( + data, size, ec); if(ec) detail::throw_system_error(ec, BOOST_CURRENT_LOCATION); diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index 07c20087..23cba28c 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -22,35 +22,42 @@ BOOST_JSON_NS_BEGIN //---------------------------------------------------------- -/** A DOM parser for serialized JSON. +/** A DOM parser for JSON contained in a single buffer. - This class is used to incrementally parse JSON - from character buffers into a @ref value container. + This class is used to parse a JSON contained in a + single character buffer, into a @ref value container. @par Usage - Parsing for a new JSON may begin after the parser is - constructed, or after calling @ref reset, optionally - passing the storage pointer to be used by the - @ref value container into which the parsed results - are stored. After the parse is started, call - @ref write_some or @ref write to provide buffers - of characters of the JSON. When there are no more - buffers, call @ref finish. The parse is complete - when the function @ref done returns `true`, or - when a non-successful error code is returned. - To retrieve the result on success, call @ref release. + To use the parser first construct it, then optionally + call @ref reset to specify a @ref storage_ptr to use + for the resulting @ref value. Then call @ref write + to parse a character buffer containing a complete + JSON. If the parse is successful, call @ref release + to take ownership of the value: + @code + parser p; // construct a parser + size_t n = p.write( "[1,2,3]" ); // parse a complete JSON + assert( n == 7 ); // all characters consumed + value jv = p.release(); // take ownership of the value + @endcode - @par Incremental Parsing + @par Extra Data - The parser allows the input to be presented in - multiple character buffers. This is useful when - not all of the serialized JSON is present at once - and it is desirable to process the data as it becomes - available, such as when reading from a network socket - or other device. The incremental interface may also - be used to bound the amount of work performed in each - parsing cycle. + When the character buffer provided as input contains + additional data that is not part of the complete + JSON, an error is returned. The @ref write_some + function is an alternative which allows the parse + to finish early, without consuming all the characters + in the buffer. This allows parsing of a buffer + containing multiple individual JSONs or containing + different protocol data: + @code + parser p; // construct a parser + size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON + assert( n == 8 ); // only some characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Temporary Storage @@ -62,10 +69,10 @@ BOOST_JSON_NS_BEGIN is exhausted, the next allocation uses the @ref memory_resource passed to the constructor; if no such argument is specified, the default memory - resource is used instead. Temporary storage is - freed only when the parser is destroyed, improving - performance when the parser is reused to parse - multiple JSONs. + resource is used. Temporary storage is freed only + when the parser is destroyed; The performance of + parsing multiple JSONs may be improved by reusing + the same parser instance. \n It is important to note that the @ref memory_resource supplied upon construction is used for temporary @@ -85,7 +92,7 @@ BOOST_JSON_NS_BEGIN The @ref parse_options structure optionally provided upon construction is used to customize - some parameters of the parser, including which + some parameters of the parser, including which non-standard JSON extensions should be allowed. A default-constructed parse options allows only standard JSON. @@ -99,7 +106,8 @@ BOOST_JSON_NS_BEGIN @see @ref parse, - @ref parse_options. + @ref parse_options, + @ref stream_parser. */ class parser { @@ -129,15 +137,15 @@ public: /** Constructor. - This constructs a new parser which first uses the - caller-owned storage pointed to by `temp_buffer` + This constructs a new parser which first uses + the caller-owned storage pointed to by `buffer` for temporary storage, falling back to the memory resource `sp` if needed. The parser will use the specified parsing options. \n - The parsed value will use the default memory resource - for storage. To use a different resource, call - @ref reset after construction. + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. @par Complexity Constant. @@ -145,8 +153,8 @@ public: @par Exception Safety No-throw guarantee. - @param sp The memory resource to use for temporary storage - when `buffer` is exhausted. + @param sp The memory resource to use for + temporary storage after `buffer` is exhausted. @param opt The parsing options to use. @@ -171,9 +179,9 @@ public: memory resource for temporary storage, and accepts only strict JSON. \n - The parsed value will use the default memory resource - for storage. To use a different resource, call - @ref reset after construction. + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. @par Complexity Constant. @@ -188,13 +196,14 @@ public: /** Constructor. - This constructs a new parser which uses the specified - memory resource for temporary storage, and is - configured to use the specified parsing options. + This constructs a new parser which uses the + specified memory resource for temporary storage, + and is configured to use the specified parsing + options. \n - The parsed value will use the default memory resource - for storage. To use a different resource, call - @ref reset after construction. + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. @par Complexity Constant. @@ -213,13 +222,13 @@ public: /** Constructor. - This constructs a new parser which uses the specified - memory resource for temporary storage, and accepts - only strict JSON. + This constructs a new parser which uses the + specified memory resource for temporary storage, + and accepts only strict JSON. \n - The parsed value will use the default memory resource - for storage. To use a different resource, call - @ref reset after construction. + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. @par Complexity Constant. @@ -242,9 +251,9 @@ public: falling back to the memory resource `sp` if needed. The parser will use the specified parsing options. \n - The parsed value will use the default memory resource - for storage. To use a different resource, call - @ref reset after construction. + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. @par Complexity Constant. @@ -252,15 +261,15 @@ public: @par Exception Safety No-throw guarantee. - @param sp The memory resource to use for temporary storage - when `buffer` is exhausted. + @param sp The memory resource to use for + temporary storage after `buffer` is exhausted. @param opt The parsing options to use. - @param buffer A buffer for the parser to use for temporary - storage. Ownership is not transferred, the caller is - responsible for ensuring the lifetime of `buffer` extends - until the parser is destroyed. + @param buffer A buffer for the parser to use for + temporary storage. Ownership is not transferred, + the caller is responsible for ensuring the lifetime + of `buffer` extends until the parser is destroyed. */ template parser( @@ -273,31 +282,73 @@ public: } #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS) - /** Constructor (delegating) + /** Constructor. - @par Effects - @code - parser( std::move(sp), opt, - reinterpret_cast( temp_buffer ), N ) - @endcode + This constructs a new parser which first uses + the caller-owned storage pointed to by `buffer` + for temporary storage, falling back to the memory + resource `sp` if needed. The parser will use the + specified parsing options. + \n + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. + + @par Complexity + Constant. + + @par Exception Safety + No-throw guarantee. + + @param sp The memory resource to use for + temporary storage after `buffer` is exhausted. + + @param opt The parsing options to use. + + @param buffer A pointer to valid memory of at least + `size` bytes for the parser to use for temporary storage. + Ownership is not transferred, the caller is responsible + for ensuring the lifetime of the memory pointed to by + `buffer` extends until the parser is destroyed. + + @param size The number of valid bytes in `buffer`. */ parser( storage_ptr sp, parse_options const& opt, - std::byte* temp_buffer, - std::size_t temp_size) noexcept - : parser(sp, opt, - reinterpret_cast( - temp_buffer), temp_size) + std::byte* buffer, + std::size_t size) noexcept + : parser(sp, opt, reinterpret_cast< + unsigned char*>(buffer), size) { } - /** Constructor (delegating) + /** Constructor. - @par Effects - @code - parser( std::move(sp), opt, &buffer[0], N ) - @endcode + This constructs a new parser which first uses the + caller-owned storage `buffer` for temporary storage, + falling back to the memory resource `sp` if needed. + The parser will use the specified parsing options. + \n + The parsed value will use the default memory + resource for storage. To use a different resource, + call @ref reset after construction. + + @par Complexity + Constant. + + @par Exception Safety + No-throw guarantee. + + @param sp The memory resource to use for + temporary storage after `buffer` is exhausted. + + @param opt The parsing options to use. + + @param buffer A buffer for the parser to use for + temporary storage. Ownership is not transferred, + the caller is responsible for ensuring the lifetime + of `buffer` extends until the parser is destroyed. */ template parser( @@ -345,30 +396,11 @@ public: #endif #endif - /** Returns the current depth of the JSON being parsed. - - The parsing depth is the total current nesting - level of arrays and objects. - - @par Complexity - - Constant. - - @par Exception Safety - - No-throw guarantee. - */ - std::size_t - depth() const noexcept - { - return p_.depth(); - } - - /** Start parsing JSON incrementally. + /** Reset the parser for a new JSON. This function is used to reset the parser to - prepare it for parsing a new JSON. Any previous - partial results are destroyed. + prepare it for parsing a new complete JSON. + Any previous partial results are destroyed. @par Complexity Constant or linear in the size of any previous @@ -385,35 +417,24 @@ public: void reset(storage_ptr sp = {}) noexcept; - /** Parse some of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. Additional + characters past the end of the complete JSON + are ignored. The function returns the actual + number of characters parsed, which may be less + than the size of the input. This allows parsing + of a buffer containing multiple individual JSONs + or containing different protocol data: - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, the function will - return the number of characters actually used - which may be less than the size of the input. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON + assert( n == 8 ); // only some characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -421,8 +442,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -442,35 +463,24 @@ public: std::size_t size, error_code& ec); - /** Parse some of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. Additional + characters past the end of the complete JSON + are ignored. The function returns the actual + number of characters parsed, which may be less + than the size of the input. This allows parsing + of a buffer containing multiple individual JSONs + or containing different protocol data: - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, the function will - return the number of characters actually used - which may be less than the size of the input. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON + assert( n == 8 ); // only some characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -478,8 +488,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -498,35 +508,24 @@ public: char const* data, std::size_t size); - /** Parse some of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. Additional + characters past the end of the complete JSON + are ignored. The function returns the actual + number of characters parsed, which may be less + than the size of the input. This allows parsing + of a buffer containing multiple individual JSONs + or containing different protocol data: - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, the function will - return the number of characters actually used - which may be less than the size of the input. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON + assert( n == 8 ); // only some characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -534,8 +533,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -553,35 +552,24 @@ public: s.data(), s.size(), ec); } - /** Parse some of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. Additional + characters past the end of the complete JSON + are ignored. The function returns the actual + number of characters parsed, which may be less + than the size of the input. This allows parsing + of a buffer containing multiple individual JSONs + or containing different protocol data: - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, the function will - return the number of characters actually used - which may be less than the size of the input. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON + assert( n == 8 ); // only some characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -589,8 +577,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -607,35 +595,21 @@ public: s.data(), s.size()); } - /** Parse all of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. The entire + buffer must be consumed; if there are additional + characters past the end of the complete JSON, + the parse fails and an error is returned. - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, but not all - characters in the buffer were consumed, an - error occurs. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write( "[1,2,3]" ); // parse a complete JSON + assert( n == 7 ); // all characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -643,8 +617,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -664,35 +638,21 @@ public: std::size_t size, error_code& ec); - /** Parse all of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. The entire + buffer must be consumed; if there are additional + characters past the end of the complete JSON, + the parse fails and an error is returned. - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, but not all - characters in the buffer were consumed, an - error occurs. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write( "[1,2,3]" ); // parse a complete JSON + assert( n == 7 ); // all characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -700,8 +660,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -720,35 +680,21 @@ public: char const* data, std::size_t size); - /** Parse all of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. The entire + buffer must be consumed; if there are additional + characters past the end of the complete JSON, + the parse fails and an error is returned. - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, but not all - characters in the buffer were consumed, an - error occurs. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write( "[1,2,3]" ); // parse a complete JSON + assert( n == 7 ); // all characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -756,8 +702,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -775,35 +721,21 @@ public: s.data(), s.size(), ec); } - /** Parse all of an input string as JSON, incrementally. + /** Parse a buffer containing a complete JSON. - This function parses the JSON in the specified - buffer. The parse proceeds from the current - state, which is at the beginning of a new JSON - or in the middle of the current JSON if any - characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + This function parses a complete JSON contained + in the specified character buffer. The entire + buffer must be consumed; if there are additional + characters past the end of the complete JSON, + the parse fails and an error is returned. - @li All of the characters in the buffer have - been parsed, or - - @li A complete JSON is parsed, including any - optional trailing whitespace in the buffer, or - - @li A parsing error occurs. - - If a complete JSON is parsed, but not all - characters in the buffer were consumed, an - error occurs. - \n - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - is be indicated by calling @ref finish. + @par Example + @code + parser p; // construct a parser + size_t n = p.write( "[1,2,3]" ); // parse a complete JSON + assert( n == 7 ); // all characters consumed + value jv = p.release(); // take ownership of the value + @endcode @par Complexity Linear in `size`. @@ -811,8 +743,8 @@ public: @par Exception Safety Basic guarantee. Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. + Upon error or exception, subsequent calls will + fail until @ref reset is called to parse a new JSON. @return The number of characters consumed from the buffer. @@ -837,22 +769,9 @@ public: after calling this function in order to parse another JSON. - @par Effects - @code - if( ! this->done() ) - this->finish(); - @endcode - @note - @par Complexity Constant. - @par Exception Safety - Basic guarantee. - Calls to `memory_resource::allocate` may throw. - Upon error, the only valid operations are - @ref reset and destruction. - @return The parsed value. Ownership of this value is transferred to the caller. diff --git a/include/boost/json/pilfer.hpp b/include/boost/json/pilfer.hpp index d88111c9..f4d0f62e 100644 --- a/include/boost/json/pilfer.hpp +++ b/include/boost/json/pilfer.hpp @@ -154,7 +154,7 @@ struct is_pilfer_constructible }; @endcode - Pilfer construction is performed using @ref pilfer: + Pilfer construction is performed using @ref pilfer : @code { diff --git a/test/parser.cpp b/test/parser.cpp index 25c86031..883878aa 100644 --- a/test/parser.cpp +++ b/test/parser.cpp @@ -17,9 +17,361 @@ BOOST_JSON_NS_BEGIN class parser_test { public: + void + testCtors() + { + // parser(parser const&) + BOOST_STATIC_ASSERT( + ! std::is_copy_constructible::value); + + // operator=(parser const&) + BOOST_STATIC_ASSERT( + ! std::is_copy_assignable::value); + + // ~parser() + { + // implied + } + + // parser(storage_ptr, parse_options, unsigned char*, size_t) + { + unsigned char buf[256]; + parser p( + storage_ptr(), + parse_options(), + &buf[0], + sizeof(buf)); + } + + // parser() + { + parser p; + } + + // parser(storage_ptr, parse_options) + { + parser p(storage_ptr{}, parse_options()); + } + + // parser(storage_ptr) + { + parser p(storage_ptr{}); + } + + // parser(storage_ptr, parse_options, unsigned char[]) + { + unsigned char buf[256]; + parser p( + storage_ptr(), + parse_options(), + buf); + } + + #if defined(__cpp_lib_byte) + // parser(storage_ptr, parse_options, std::byte*, size_t) + { + std::byte buf[256]; + parser p( + storage_ptr(), + parse_options(), + &buf[0], + sizeof(buf)); + } + + // parser(storage_ptr, parse_options, std::byte[]) + { + std::byte buf[256]; + parser p( + storage_ptr(), + parse_options(), + buf); + } + #endif + + // parser(storage_ptr, parse_options, unsigned char[], size_t) + { + unsigned char buf[256]; + parser p( + storage_ptr(), + parse_options(), + buf, + sizeof(buf)); + } + + #if defined(__cpp_lib_byte) + // parser(storage_ptr, parse_options, std::byte[], size_t) + { + std::byte buf[256]; + parser p( + storage_ptr(), + parse_options(), + buf, + sizeof(buf)); + } + #endif + } + + void + testMembers() + { + // reset + { + parser p; + p.reset(); + } + + // write_some(char const*, size_t, error_code&) + { + // valid json + { + parser p; + error_code ec; + auto const n = + p.write_some("null", 4, ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 4); + } + + // valid json with trailing space + { + parser p; + error_code ec; + auto const n = + p.write_some("null ", 5, ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 5); + } + + // valid json with invalid trailing char + { + parser p; + error_code ec; + auto const n = + p.write_some("null*", 5, ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 4); + } + + // partial json + { + parser p; + error_code ec; + p.write_some("nul", 3, ec); + BOOST_TEST(ec); + } + } + + // write_some(string_view, error_code&) + { + // valid json + { + parser p; + error_code ec; + auto const n = + p.write_some("null", ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 4); + } + + // partial json + { + parser p; + error_code ec; + p.write_some("nul", ec); + BOOST_TEST(ec); + } + } + + // write_some(char const*, size_t) + { + // valid json with trailing space + { + parser p; + auto const n = + p.write_some("null ", 5); + BOOST_TEST(n == 5); + } + + // partial json + { + parser p; + BOOST_TEST_THROWS( + p.write_some("nul", 3), + system_error); + } + } + + // write_some(string_view) + { + // valid json with trailing space + { + parser p; + auto const n = + p.write_some("null "); + BOOST_TEST(n == 5); + } + + // partial json + { + parser p; + BOOST_TEST_THROWS( + p.write_some("nul"), + system_error); + } + } + + //-------------------------------------------------- + + // write(char const*, size_t, error_code&) + { + // valid json + { + parser p; + error_code ec; + auto const n = + p.write("null", 4, ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 4); + } + + // valid json with trailing space + { + parser p; + error_code ec; + auto const n = + p.write("null ", 5, ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 5); + } + + // valid json with invalid trailing char + { + parser p; + error_code ec; + p.write("null*", 5, ec); + BOOST_TEST(ec); + } + + // partial json + { + parser p; + error_code ec; + p.write("nul", 3, ec); + BOOST_TEST(ec); + } + } + + // write(string_view, error_code&) + { + // valid json + { + parser p; + error_code ec; + auto const n = + p.write("null", ec); + BOOST_TEST(! ec); + BOOST_TEST(n == 4); + } + + // partial json + { + parser p; + error_code ec; + p.write("nul", ec); + BOOST_TEST(ec); + } + } + + // write(char const*, size_t) + { + // valid json with trailing space + { + parser p; + auto const n = + p.write("null ", 5); + BOOST_TEST(n == 5); + } + + // valid json with invalid trailing char + { + parser p; + BOOST_TEST_THROWS( + p.write("null*", 5), + system_error); + } + + // partial json + { + parser p; + BOOST_TEST_THROWS( + p.write("nul", 3), + system_error); + } + } + + // write(string_view) + { + // valid json with trailing space + { + parser p; + auto const n = + p.write("null "); + BOOST_TEST(n == 5); + } + + // valid json with invalid trailing char + { + parser p; + BOOST_TEST_THROWS( + p.write("null*"), + system_error); + } + + // partial json + { + parser p; + BOOST_TEST_THROWS( + p.write("nul"), + system_error); + } + } + + // release + { + // valid json + { + parser p; + p.write("[1,2,3]"); + BOOST_TEST(p.release() == + value({1,2,3})); + } + + // release with no write + { + parser p; + BOOST_TEST_THROWS( + p.release(), + system_error); + } + + // release after error + { + parser p; + error_code ec; + p.write("nul", ec); + BOOST_TEST(ec); + BOOST_TEST_THROWS( + p.release(), + system_error); + } + } + } + void run() { + testCtors(); + testMembers(); } };