Refactor parser interface

This commit is contained in:
Vinnie Falco 2019-11-15 12:11:22 -08:00
parent 8f1348ed40
commit 19ebd6c943
23 changed files with 704 additions and 322 deletions

View File

@ -256,6 +256,8 @@ public:
error_code ec;
vec_parser p;
p.write(s.data(), s.size(), ec);
if(! ec)
p.finish(ec);
}
}

View File

@ -55,6 +55,7 @@ PREDEFINED = \
BOOST_JSON_DECL \
BOOST_JSON_PUBLIC \
BOOST_JSON_FORCEINLINE \
"BOOST_JSON_NODISCARD=[[nodiscard]]" \
"BOOST_JSON_INLINE_VARIABLE(v, t)=constexpr t v;"
EXPAND_AS_DEFINED =

View File

@ -18,6 +18,45 @@
namespace json = boost::json;
class file
{
FILE* f_ = nullptr;
public:
~file()
{
if(f_)
std::fclose(f_);
}
file() = default;
void
close()
{
if(f_)
{
std::fclose(f_);
f_ = nullptr;
}
}
void
open(
char const* path,
char const* mode,
json::error_code& ec)
{
close();
f_ = std::fopen( path, mode );
if( ! f_ )
{
ec.assign( errno, json::generic_category() );
return;
}
}
};
json::value
parse_file( char const* filename )
{
@ -54,7 +93,7 @@ parse_file( char const* filename )
while( ! std::feof(f) );
// Tell the parser there is no more serialized JSON.
p.write_eof(ec);
p.finish(ec);
if( ec )
throw json::system_error(ec);

View File

@ -573,7 +573,7 @@ public:
@param pos A zero-based index.
@throws std::out_of_range `pos >= size()`
@throw std::out_of_range `pos >= size()`
*/
inline
reference
@ -592,7 +592,7 @@ public:
@param pos A zero-based index.
@throws std::out_of_range `pos >= size()`
@throw std::out_of_range `pos >= size()`
*/
inline
const_reference

View File

@ -21,21 +21,30 @@
namespace boost {
namespace json {
/** A SAX parser for serialized JSON.
/** An incremental SAX parser for serialized JSON.
This implements a SAX-style parser. The serialized
JSON is presented to the parser by calling to
@ref write_some, @ref write, and @ref write_eof.
@ref write_some, @ref write, and @ref finish.
The parsing events are realized through calls of
protected virtual functions, whose implementations
are in the derived class.
<br>
The parser may dynamically allocate intermediate
storage as needed to accommodate the nesting level
of the JSON being parsed. This storage is freed
when the parser is destroyed, allowing the parser
to cheaply re-use this memory when parsing
subsequent JSONs, improving performance.
@note
The parser is strict: no extensions are supported.
Only compliant JSON is recognized.
@see parser
@see parse, parser
*/
class basic_parser
{
@ -64,7 +73,7 @@ public:
/** Destructor.
All internal memory is freed.
All dynamically allocated internal memory is freed.
*/
virtual
~basic_parser()
@ -132,8 +141,36 @@ public:
/** Parse JSON incrementally.
This function presents the next buffer of serialized
JSON as input into the parser.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed, or
@li Some of the characters in the buffer have been
parsed and the JSON is complete, or
@li A parsing error occurs.
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
can be indicated by calling @ref finish().
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@ -144,7 +181,7 @@ public:
@param ec Set to the error, if any occurred.
@return The number of characters successfully
parsed.
parsed, which may be smaller than `size`.
*/
BOOST_JSON_DECL
std::size_t
@ -153,19 +190,85 @@ public:
std::size_t size,
error_code& ec);
/** Parse JSON incrementally.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed, or
@li Some of the characters in the buffer have been
parsed and the JSON is complete, or
@li A parsing error occurs.
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
can be indicated by calling @ref finish().
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@throw system_error Thrown on failure.
@return The number of characters successfully
parsed, which may be smaller than `size`.
*/
BOOST_JSON_DECL
std::size_t
write_some(
char const* data,
std::size_t size);
/** Parse the remaining JSON immediately.
/** Parse JSON incrementally.
This function is used to submit the last buffer,
or the only buffer, of serialized JSON as input into
the parser. An error occurs if all of the characters
could not be successfully parsed, even if a complete
JSON object is encountered.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed, or
@li A parsing error occurs.
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
can be indicated by calling @ref finish().
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@ -182,28 +285,190 @@ public:
std::size_t size,
error_code& ec);
/** Parse JSON incrementally.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed, or
@li A parsing error occurs.
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
can be indicated by calling @ref finish().
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@throw system_error Thrown on failure.
*/
BOOST_JSON_DECL
void
write(
char const* data,
std::size_t size);
/** Indicate that no more serialized JSON remains.
/** Parse JSON incrementally.
This function informs the parser that there is
are no more characters left in the serialized
JSON being parsed. If no error occurs, the parse
is complete and @ref is_done will return `true`.
Otherwise, the error is set to the problem
encountered.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed and form a complete JSON, or
@li A parsing error occurs.
The caller uses this function to inform the
parser that there is no more serialized JSON
available. If the entire buffer is not consumed
or if a complete JSON is not available after
consuming the entire buffer, the error is
set to indicate failure.
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@param ec Set to the error, if any occurred.
*/
BOOST_JSON_DECL
void
write_eof(error_code& ec);
finish(
char const* data,
std::size_t size,
error_code& ec);
/** Parse JSON incrementally.
This function parses the JSON in the specified
buffer, emitting SAX parsing events by calling
the derived class. 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.
<br>
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer have been
parsed and form a complete JSON, or
@li A parsing error occurs.
The caller uses this function to inform the
parser that there is no more serialized JSON
available. If the entire buffer is not consumed
or if a complete JSON is not available after
consuming the entire buffer, the error is
set to indicate failure.
@par Complexity
Linear in `size`.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@throw system_error Thrown on failure.
*/
BOOST_JSON_DECL
void
write_eof();
finish(
char const* data,
std::size_t size);
/** Parse JSON incrementally.
This function finishes parsing the current JSON,
emitting SAX parsing events by calling the derived
class. The parse is finalized according to the
current state, which includes all previously
parsed data since the last reset.
<br>
The caller uses this function to inform the
parser that there is no more serialized JSON
available. If a complete JSON is not available
the error is set to indicate failure.
@par Complexity
Constant.
@param ec Set to the error, if any occurred.
*/
BOOST_JSON_DECL
void
finish(error_code& ec);
/** Parse JSON incrementally.
This function finishes parsing the current JSON,
emitting SAX parsing events by calling the derived
class. The parse is finalized according to the
current state, which includes all previously
parsed data since the last reset.
<br>
The caller uses this function to inform the
parser that there is no more serialized JSON
available. If a complete JSON is not available
the error is set to indicate failure.
@par Complexity
Constant.
@throw system_error Thrown on failure.
*/
BOOST_JSON_DECL
void
finish();
protected:
using saved_state = char;
@ -244,7 +509,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -260,7 +525,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -274,7 +539,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -289,7 +554,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -303,7 +568,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -318,7 +583,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -339,7 +604,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -360,7 +625,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -382,7 +647,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -403,7 +668,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -420,7 +685,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -437,7 +702,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -454,7 +719,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -471,7 +736,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void
@ -483,7 +748,7 @@ protected:
@param ec The error, if any, which will be
returned by the current invocation of
@ref write_some, @ref write, or @ref write_eof.
@ref write_some, @ref write, or @ref finish.
*/
virtual
void

View File

@ -54,6 +54,19 @@
# endif
#endif
// VFALCO Copied from <boost/config.hpp>
// This is a derivative work.
#ifdef __has_cpp_attribute
// clang-6 accepts [[nodiscard]] with -std=c++14, but warns about it -pedantic
# if __has_cpp_attribute(nodiscard) && !(defined(__clang__) && (__cplusplus < 201703L))
# define BOOST_JSON_NODISCARD [[nodiscard]]
# else
# define BOOST_JSON_NODISCARD
# endif
#else
# define BOOST_JSON_NODISCARD
#endif
#ifndef BOOST_JSON_FORCEINLINE
# ifdef _MSC_VER
# define BOOST_JSON_FORCEINLINE __forceinline

View File

@ -98,7 +98,7 @@ public:
BOOST_JSON_DECL
void
write_eof(
finish(
error_code& ec) noexcept;
};

View File

@ -260,7 +260,7 @@ loop:
st_ = state::exp1;
goto loop;
}
write_eof(ec);
finish(ec);
goto finish;
}
while(p < p1);
@ -346,7 +346,7 @@ loop:
if(static_cast<unsigned char>(
*p - '0') > 9)
{
write_eof(ec);
finish(ec);
goto finish;
}
++p;
@ -406,7 +406,7 @@ loop:
if(*p != 'e' && *p != 'E')
{
n_.u = m;
write_eof(ec);
finish(ec);
goto finish;
}
++p;
@ -445,7 +445,7 @@ loop:
if(*p != 'e' && *p != 'E')
{
n_.d = m;
write_eof(ec);
finish(ec);
goto finish;
}
++p;
@ -522,7 +522,7 @@ loop:
continue;
}
exp_ = e;
write_eof(ec);
finish(ec);
goto finish;
}
while(p < p1);
@ -532,7 +532,7 @@ loop:
}
case state::end:
ec = error::illegal_extra_chars;
ec = error::extra_data;
break;
}
finish:
@ -557,13 +557,13 @@ write(
if(ec)
return n;
}
write_eof(ec);
finish(ec);
return n;
}
void
number_parser::
write_eof(
finish(
error_code& ec) noexcept
{
switch(st_)

View File

@ -26,6 +26,9 @@ enum class error
/// extra data
extra_data,
/// incomplete data
incomplete,
/// mantissa overflow
mantissa_overflow,
@ -47,9 +50,6 @@ enum class error
/// illegal extra digits in number
illegal_extra_digits,
/// illegal extra characters
illegal_extra_chars,
/// illegal leading surrogate
illegal_leading_surrogate,

View File

@ -169,13 +169,13 @@ loop:
goto loop_val;
case state::end:
ec = error::illegal_extra_chars;
ec = error::extra_data;
goto yield;
case state::maybe_end:
if(! skip_white())
goto yield;
write_eof(ec);
finish(ec);
goto yield;
//------------------------------------------------------
@ -799,7 +799,7 @@ loop_num:
// is_done inside write_some better
if(p < p1)
{
iep_.write_eof(ec);
iep_.finish(ec);
if(ec)
goto yield;
BOOST_JSON_ASSERT(iep_.is_done());
@ -884,8 +884,7 @@ write(
if(! ec)
{
if(n < size)
write_some(
data + n, size - n, ec);
ec = error::extra_data;
}
}
@ -906,7 +905,34 @@ write(
void
basic_parser::
write_eof(error_code& ec)
finish(
char const* data,
std::size_t size,
error_code& ec)
{
write(data, size, ec);
if(! ec)
finish(ec);
}
void
basic_parser::
finish(
char const* data,
std::size_t size)
{
error_code ec;
finish(data, size, ec);
if(ec)
BOOST_JSON_THROW(
system_error(ec));
}
//----------------------------------------------------------
void
basic_parser::
finish(error_code& ec)
{
for(;;)
{
@ -931,7 +957,7 @@ write_eof(error_code& ec)
case state::num:
{
iep_.write_eof(ec);
iep_.finish(ec);
if(ec)
return;
auto const num = iep_.get();
@ -976,7 +1002,7 @@ write_eof(error_code& ec)
case state::sur2:
case state::sur3:
case state::sur1:
ec = error::syntax;
ec = error::incomplete;
return;
}
}
@ -984,10 +1010,10 @@ write_eof(error_code& ec)
void
basic_parser::
write_eof()
finish()
{
error_code ec;
write_eof(ec);
finish(ec);
if(ec)
BOOST_JSON_THROW(
system_error(ec));

View File

@ -34,6 +34,7 @@ make_error_code(error e)
default:
case error::syntax: return "syntax error";
case error::extra_data: return "extra data";
case error::incomplete: return "incomplete";
case error::mantissa_overflow: return "mantissa overflow";
case error::exponent_overflow: return "exponent overflow";
case error::too_deep: return "too deep";
@ -41,7 +42,6 @@ make_error_code(error e)
case error::illegal_control_char: return "illegal control character";
case error::illegal_escape_char: return "illegal character in escape sequence";
case error::illegal_extra_digits: return "illegal extra digits in number";
case error::illegal_extra_chars: return "illegal extra characters";
case error::illegal_leading_surrogate: return "illegal leading surrogate";
case error::illegal_trailing_surrogate: return "illegal trailing surrogate";
case error::need_start: return "parser needs start";
@ -91,7 +91,6 @@ make_error_code(error e)
case error::illegal_control_char:
case error::illegal_escape_char:
case error::illegal_extra_digits:
case error::illegal_extra_chars:
case error::illegal_leading_surrogate:
case error::illegal_trailing_surrogate:

View File

@ -59,7 +59,7 @@ enum class parser::state : char
arr, // empty array value
obj, // empty object value
key, // complete key
end // compelte top value
end // complete top value
};
void
@ -182,62 +182,27 @@ clear() noexcept
value
parser::
release() noexcept
release()
{
if(is_done())
{
BOOST_JSON_ASSERT(lev_.st == state::end);
auto ua = pop_array();
BOOST_JSON_ASSERT(rs_.empty());
union U
{
value v;
U(){}
~U(){}
};
U u;
ua.relocate(&u.v);
basic_parser::reset();
lev_.st = state::need_start;
sp_ = {};
return pilfer(u.v);
}
// return null
value jv(std::move(sp_));
clear();
return jv;
}
value
parser::
parse(
char const* data,
std::size_t size,
error_code& ec,
storage_ptr sp)
{
start(std::move(sp));
write(data, size, ec);
if(! ec)
write_eof(ec);
return release();
}
value
parser::
parse(
char const* data,
std::size_t size,
storage_ptr sp)
{
error_code ec;
auto jv = parse(
data, size, ec,
std::move(sp));
if(ec)
if(! is_done())
BOOST_JSON_THROW(
system_error(ec));
return release();
std::logic_error(
"no value"));
BOOST_JSON_ASSERT(lev_.st == state::end);
auto ua = pop_array();
BOOST_JSON_ASSERT(rs_.empty());
union U
{
value v;
U(){}
~U(){}
};
U u;
ua.relocate(&u.v);
basic_parser::reset();
lev_.st = state::need_start;
sp_ = {};
return pilfer(u.v);
}
//----------------------------------------------------------
@ -291,10 +256,19 @@ emplace(Args&&... args)
key, std::forward<Args>(args)...);
rs_.add(sizeof(u.v));
}
else if(lev_.st == state::arr)
{
// prevent splits from exceptions
rs_.prepare(sizeof(value));
BOOST_JSON_ASSERT((rs_.top() %
alignof(value)) == 0);
::new(rs_.behind(sizeof(value))) value(
std::forward<Args>(args)...);
rs_.add(sizeof(value));
}
else
{
BOOST_JSON_ASSERT(
lev_.st == state::arr ||
lev_.st == state::top);
// prevent splits from exceptions
rs_.prepare(sizeof(value));
@ -303,6 +277,7 @@ emplace(Args&&... args)
::new(rs_.behind(sizeof(value))) value(
std::forward<Args>(args)...);
rs_.add(sizeof(value));
lev_.st = state::end; // VFALCO Maybe pre_end
}
++lev_.count;
}
@ -384,7 +359,6 @@ parser::
on_document_end(error_code&)
{
BOOST_JSON_ASSERT(lev_.count == 1);
lev_.st = state::end; // VFALCO RECONSIDER
}
void
@ -574,12 +548,17 @@ on_null(error_code&)
value
parse(
string_view s,
storage_ptr sp,
error_code& ec)
error_code& ec,
storage_ptr sp)
{
parser p;
p.start(std::move(sp));
p.write(s.data(), s.size(), ec);
p.finish(
s.data(),
s.size(),
ec);
if(ec)
return nullptr;
return p.release();
}
@ -589,7 +568,8 @@ parse(
storage_ptr sp)
{
error_code ec;
auto jv = parse(s, std::move(sp), ec);
auto jv = parse(
s, ec, std::move(sp));
if(ec)
BOOST_JSON_THROW(
system_error(ec));

View File

@ -33,8 +33,7 @@ struct value::undo
undo(value* self_) noexcept
: self(self_)
{
std::memcpy(&saved, self,
sizeof(*self));
relocate(&saved, *self_);
}
void
@ -47,9 +46,7 @@ struct value::undo
~undo()
{
if(self)
std::memcpy(
self, &saved,
sizeof(*self));
relocate(self, saved);
}
};
@ -164,6 +161,19 @@ operator=(T&& t)
return *this;
}
void
value::
relocate(
value* dest,
value const& src) noexcept
{
std::memcpy(
reinterpret_cast<
void*>(dest),
&src,
sizeof(src));
}
//----------------------------------------------------------
template<class... Args>

View File

@ -122,8 +122,7 @@ value::
value::
value(pilfered<value> p) noexcept
{
std::memcpy(this,
&p.get(), sizeof(*this));
relocate(this, p.get());
::new(&p.get().nul_) null_k;
}
@ -186,8 +185,7 @@ value(
value::
value(value&& other) noexcept
{
std::memcpy(
this, &other, sizeof(*this));
relocate(this, other);
::new(&other.nul_) null_k(sp_);
}
@ -394,12 +392,9 @@ swap(value& other)
~U(){}
};
U u;
std::memcpy(&u.tmp,
this, sizeof(*this));
std::memcpy(this,
&other, sizeof(other));
std::memcpy(&other,
&u.tmp, sizeof(u.tmp));
relocate(&u.tmp, *this);
relocate(this, other);
relocate(&other, u.tmp);
}
//----------------------------------------------------------

View File

@ -901,14 +901,14 @@ public:
@param p The value to insert.
@returns A pair where `first` is an iterator
to the existing or inserted element, and `second`
is `true` if the insertion took place or `false` if
the assignment took place.
@throw std::length_error key is too long.
@throw std::length_error @ref size() >= max_size().
@return A pair where `first` is an iterator
to the existing or inserted element, and `second`
is `true` if the insertion took place or `false` if
the assignment took place.
*/
template<class P
#ifndef GENERATING_DOCUMENTATION
@ -1016,13 +1016,13 @@ public:
Strong guarantee.
Calls to @ref storage::allocate may throw.
@throw std::length_error if key is too long
@param key The key used for lookup and insertion
@param m The value to insert or assign
@returns A `std::pair` where `first` is an iterator
@throw std::length_error if key is too long
@return A `std::pair` where `first` is an iterator
to the existing or inserted element, and `second`
is `true` if the insertion took place or `false` if
the assignment took place.
@ -1056,15 +1056,15 @@ public:
Strong guarantee.
Calls to @ref storage::allocate may throw.
@throw std::length_error if key is too long
@param key The key used for lookup and insertion
@param arg The argument used to construct the value.
This will be passed as `std::forward<Arg>(arg)` to
the @ref value constructor.
@returns A `std::pair` where `first` is an iterator
@throw std::length_error if key is too long
@return A `std::pair` where `first` is an iterator
to the existing or inserted element, and `second`
is `true` if the insertion took place or `false` if
the assignment took place.
@ -1094,7 +1094,7 @@ public:
@param pos An iterator pointing to the element to be
removed.
@returns The number of elements removed, which can
@return The number of elements removed, which can
be either 0 or 1.
*/
BOOST_JSON_DECL
@ -1116,7 +1116,7 @@ public:
No-throw guarantee.
@returns The number of elements removed, which can
@return The number of elements removed, which can
be either 0 or 1.
*/
BOOST_JSON_DECL
@ -1168,9 +1168,9 @@ public:
Constant on average, worst case linear in @ref size().
@throw std::out_of_range if no such element exists.
@param key The key of the element to find
@throw std::out_of_range if no such element exists.
*/
BOOST_JSON_DECL
value&
@ -1185,9 +1185,9 @@ public:
Constant on average, worst case linear in @ref size().
@throw std::out_of_range if no such element exists.
@param key The key of the element to find
@throw std::out_of_range if no such element exists.
*/
BOOST_JSON_DECL
value const&

View File

@ -40,7 +40,7 @@ namespace json {
pointer to be used by the @ref value container into
which the parsed results are stored. After the
parse is started, the functions @ref write_some,
@ref write, and @ref write_eof may be called to
@ref write, and @ref finish may be called to
provide successive buffers of characters of the
JSON. The caller can check that the parse is
complete by calling @ref is_done, or that a
@ -57,6 +57,14 @@ namespace json {
to bound the amount of work performed in each
parsing cycle.
<br>
The parser may dynamically allocate intermediate
storage as needed to accommodate the nesting level
of the JSON being parsed. This storage is freed
when the parser is destroyed, allowing the parser
to cheaply re-use this memory when parsing
subsequent JSONs, improving performance.
*/
class parser final
: public basic_parser
@ -83,6 +91,9 @@ class parser final
public:
/** Destructor.
All dynamically allocated memory, including
any partial parsing results, is freed.
*/
BOOST_JSON_DECL
virtual
@ -96,11 +107,11 @@ public:
BOOST_JSON_DECL
parser();
/** Prepare the parser for new serialized JSON.
/** Start parsing JSON incrementally.
This function must be called once, before
any data is presented, to start parsing a
new JSON.
This function must be called once manually before
parsing a new JSON incrementally; that is, when
using @ref write_some, @ref write, or @ref finish.
@param sp The storage to use for all values.
*/
@ -108,16 +119,17 @@ public:
void
start(storage_ptr sp = {}) noexcept;
/** Discard all intermadiate or final parsing results.
/** Discard all parsed JSON results.
This function destroys all intermediate parsing
results, while preserving dynamically allocated
internal memory which is reused between parses.
internal memory which may be reused on a
subsequent parse.
@note
It is necessary to call @ref start to parse new
JSON after calling this function.
After this call, it is necessary to call
@ref start to parse a new JSON incrementally.
*/
BOOST_JSON_DECL
void
@ -125,34 +137,19 @@ public:
/** Return the parsed JSON as a @ref value.
If the parse failed, the returned value
will be null.
If @ref is_done() returns `true`, then the
parsed value is returned. Otherwise an
exception is thrown.
@par Preconditions
@throw std::logic_error `! is_done()`
`is_done() == true`.
@returns The parsed value. Ownership of this
@return The parsed value. Ownership of this
value is transferred to the caller.
*/
BOOST_JSON_NODISCARD
BOOST_JSON_DECL
value
release() noexcept;
BOOST_JSON_DECL
value
parse(
char const* data,
std::size_t size,
error_code& ec,
storage_ptr sp = {});
BOOST_JSON_DECL
value
parse(
char const* data,
std::size_t size,
storage_ptr sp = {});
release();
private:
template<class T>
@ -271,8 +268,12 @@ private:
/** Parse a string of JSON.
The string is parsed as JSON into a @ref value,
using the specified storage.
This function parses an entire single string in
one step to produce a complete JSON object, returned
as a @ref value. If the buffer does not contain a
complete serialized JSON, an error occurs. In this
case the returned value will be null, using the
default storage.
@par Complexity
@ -283,28 +284,33 @@ private:
Strong guarantee.
Calls to @ref storage::allocate may throw.
@param s The string containing the JSON to parse.
@param s The string to parse.
@param sp A pointer to the @ref storage
to use. The container will acquire shared
ownership of the storage object.
@param ec Set to the error, if any occurred.
@param ec Set to the error if any occurred.
@param sp The storage that the new value and all of
its elements will use. If this parameter is omitted,
the default storage is used.
@return A value representing the parsed JSON,
or a null if any error occurred.
*/
BOOST_JSON_NODISCARD
BOOST_JSON_DECL
value
parse(
string_view s,
storage_ptr sp,
error_code& ec);
error_code& ec,
storage_ptr sp = {});
/** Parse a string of JSON.
The string is parsed as JSON into a @ref value
using the specified storage.
This function parses an entire single string in
one step to produce a complete JSON object, returned
as a @ref value. If the buffer does not contain a
complete serialized JSON, an error occurs. In this
case the returned value will be null, using the
default storage.
@par Complexity
@ -315,80 +321,23 @@ parse(
Strong guarantee.
Calls to @ref storage::allocate may throw.
@param s The string containing the JSON to parse.
@param s The string to parse.
@param sp A pointer to the @ref storage
to use. The container will acquire shared
ownership of the storage object.
@param sp The storage that the new value and all of
its elements will use. If this parameter is omitted,
the default storage is used.
@return A value representing the parsed JSON.
@throw system_error any errors.
*/
BOOST_JSON_DECL
value
parse(
string_view s,
storage_ptr sp);
/** Parse a string of JSON.
The string is parsed as JSON into a @ref value,
using the default storage.
@par Complexity
Linear in `s.size()`.
@par Exception Safety
Strong guarantee.
Calls to @ref storage::allocate may throw.
@param s The string containing the JSON to parse.
@param ec Set to the error if any occurred.
@throw system_error Thrown on failure.
@return A value representing the parsed JSON,
or a null if any error occurred.
*/
inline
BOOST_JSON_NODISCARD
BOOST_JSON_DECL
value
parse(
string_view s,
error_code& ec)
{
return parse(s,
storage_ptr{}, ec);
}
/** Parse a string of JSON.
The string is parsed as JSON into a @ref value
using the default storage.
@par Complexity
Linear in `s.size()`.
@par Exception Safety
Strong guarantee.
Calls to @ref storage::allocate may throw.
@param s The string containing the JSON to parse.
@return A value representing the parsed JSON.
@throw system_error any errors.
*/
inline
value
parse(string_view s)
{
return parse(
s, storage_ptr{});
}
storage_ptr sp = {});
} // json
} // boost

View File

@ -168,10 +168,10 @@ public:
Strong guarantee.
Calls to @ref storage::allocate may throw.
@throw std::logic_error if no value is set.
@return The number of characters written
to `dest`.
@throw std::logic_error if no value is set.
*/
BOOST_JSON_DECL
std::size_t

View File

@ -220,7 +220,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
string(
std::size_t count,
@ -262,7 +262,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::out_of_range `pos >= other.size()`.
@throw std::out_of_range `pos >= other.size()`.
*/
string(
string const& other,
@ -299,7 +299,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `strlen(s) > max_size()`.
@throw std::length_error `strlen(s) > max_size()`.
*/
string(
char const* s,
@ -335,7 +335,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
string(
char const* s,
@ -377,7 +377,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `std::distance(first, last) > max_size()`.
@throw std::length_error `std::distance(first, last) > max_size()`.
*/
template<class InputIt
#ifndef GENERATING_DOCUMENTATION
@ -533,7 +533,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `init.size() > max_size()`.
@throw std::length_error `init.size() > max_size()`.
*/
string(
std::initializer_list<char> init,
@ -566,7 +566,7 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::length_error `s.size() > max_size()`.
@throw std::length_error `s.size() > max_size()`.
*/
string(
string_view s,
@ -607,9 +607,9 @@ public:
If this parameter is omitted, the default storage
is used.
@throws std::out_of_range `pos >= s.size()`
@throw std::out_of_range `pos >= s.size()`
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
string(
string_view s,
@ -698,7 +698,7 @@ public:
@param s A pointer to a character string used to
copy from.
@throws std::length_error `strlen(s) > max_size()`.
@throw std::length_error `strlen(s) > max_size()`.
*/
string&
operator=(char const* s)
@ -722,7 +722,7 @@ public:
@param init The initializer list to copy from.
@throws std::length_error `init.size() > max_size()`.
@throw std::length_error `init.size() > max_size()`.
*/
string&
operator=(std::initializer_list<char> init)
@ -747,7 +747,7 @@ public:
@param s The string view to copy from.
@throws std::length_error `s.size() > max_size()`.
@throw std::length_error `s.size() > max_size()`.
*/
string&
operator=(string_view s)
@ -776,7 +776,7 @@ public:
@param ch THe value to initialize characters
of the string with.
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
BOOST_JSON_DECL
string&
@ -830,7 +830,7 @@ public:
@param count The number of characters to copy.
@throws std::out_of_range `pos >= other.size()`.
@throw std::out_of_range `pos >= other.size()`.
*/
string&
assign(
@ -892,7 +892,7 @@ public:
@param s A pointer to a character string used to
copy from.
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
BOOST_JSON_DECL
string&
@ -919,7 +919,7 @@ public:
@param s A pointer to a character string used to
copy from.
@throws std::length_error `strlen(s) > max_size()`.
@throw std::length_error `strlen(s) > max_size()`.
*/
string&
assign(
@ -954,7 +954,7 @@ public:
@param last An input iterator pointing to the end
of the range.
@throws std::length_error `std::distance(first, last) > max_size()`.
@throw std::length_error `std::distance(first, last) > max_size()`.
*/
template<class InputIt
#ifndef GENERATING_DOCUMENTATION
@ -982,7 +982,7 @@ public:
@param init The initializer list to copy from.
@throws std::length_error `init.size() > max_size()`.
@throw std::length_error `init.size() > max_size()`.
*/
string&
assign(std::initializer_list<char> init)
@ -1007,7 +1007,7 @@ public:
@param s The string view to copy from.
@throws std::length_error `s.size() > max_size()`.
@throw std::length_error `s.size() > max_size()`.
*/
string&
assign(string_view s)
@ -1040,9 +1040,9 @@ public:
@param count The number of characters to copy.
@throws std::out_of_range `pos >= s.size()`
@throw std::out_of_range `pos >= s.size()`
@throws std::length_error `count > max_size()`.
@throw std::length_error `count > max_size()`.
*/
string&
assign(
@ -1089,7 +1089,7 @@ public:
@param pos A zero-based index
@throws std::out_of_range `pos >= size()`
@throw std::out_of_range `pos >= size()`
*/
char&
at(std::size_t pos)
@ -1114,7 +1114,7 @@ public:
@param pos A zero-based index
@throws std::out_of_range `pos >= size()`
@throw std::out_of_range `pos >= size()`
*/
char const&
at(std::size_t pos) const
@ -1612,9 +1612,9 @@ public:
Strong guarantee.
Calls to @ref storage::allocate may throw.
@throw std::length_error `new_capacity > max_size()`
@param new_capacity The new capacity of the array.
@throw std::length_error `new_capacity > max_size()`
*/
void
reserve(std::size_t new_capacity)

View File

@ -1407,7 +1407,7 @@ public:
Strong guarantee.
@throws system error Thrown upon failure
@throw system error Thrown upon failure
*/
template<class T>
void
@ -2748,6 +2748,13 @@ public:
//------------------------------------------------------
private:
static
inline
void
relocate(
value* dest,
value const& src) noexcept;
BOOST_JSON_DECL
storage_ptr
destroy() noexcept;

View File

@ -47,6 +47,8 @@ public:
p.write(
s.data() + n,
s.size() - n, ec);
if(! ec)
p.finish(ec);
if(! BEAST_EXPECTS(ec == ex,
ec.message()))
log << "should be " << ex.message() << std::endl;
@ -67,6 +69,8 @@ public:
fail_parser p(j);
p.write(
s.data(), s.size(), ec);
if(! ec)
p.finish(ec);
if(ec == error::test_failure)
continue;
BEAST_EXPECTS(ec == ex,
@ -91,6 +95,8 @@ public:
{
p.write(
s.data(), s.size(), ec);
if(! ec)
p.finish(ec);
BEAST_EXPECTS(ec == ex,
ec.message());
break;
@ -118,6 +124,8 @@ public:
s.data(),
s.size(),
ex);
if(! ex)
p.finish(ex);
if(good)
{
if(! BEAST_EXPECTS(
@ -441,6 +449,8 @@ public:
BEAST_EXPECT(ec);
p.reset();
p.write("{}", 2, ec);
if(! ec)
p.finish(ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_done());
}
@ -480,11 +490,67 @@ public:
{
error_code ec;
fail_parser p;
p.write_eof(ec);
p.finish(ec);
BEAST_EXPECT(ec);
}
}
void
testMembers()
{
// write_some(char const*, size_t, error_code&)
{
error_code ec;
fail_parser p;
p.write_some("0", 1, ec);
BEAST_EXPECTS(! ec, ec.message());
}
// write_some(char const*, size_t)
{
fail_parser p;
BEAST_THROWS(
p.write_some("x", 1),
system_error);
}
// write(char const*, size_t, error_code&)
{
error_code ec;
fail_parser p;
p.write("0x", 2, ec);
BEAST_EXPECTS(
ec == error::extra_data,
ec.message());
}
// write(char const*, size_t)
{
fail_parser p;
BEAST_THROWS(
p.write("0x", 2),
system_error);
}
// finish(char const*, size_t, error_code&)
{
error_code ec;
fail_parser p;
p.finish("{", 1, ec);
BEAST_EXPECTS(
ec == error::incomplete,
ec.message());
}
// finish(char const*, size_t)
{
fail_parser p;
BEAST_THROWS(
p.finish("{", 1),
system_error);
}
}
void
testParseVectors()
{
@ -520,6 +586,7 @@ public:
testBoolean();
testNull();
testParser();
testMembers();
testParseVectors();
}
};

View File

@ -44,7 +44,8 @@ public:
}
}
void run() override
void
run() override
{
check(condition::parse_error, error::syntax);
check(condition::parse_error, error::extra_data);
@ -56,7 +57,6 @@ public:
check(condition::parse_error, error::illegal_control_char);
check(condition::parse_error, error::illegal_escape_char);
check(condition::parse_error, error::illegal_extra_digits);
check(condition::parse_error, error::illegal_extra_chars);
check(condition::parse_error, error::illegal_leading_surrogate);
check(condition::parse_error, error::illegal_trailing_surrogate);

View File

@ -51,7 +51,7 @@ public:
if(! BEAST_EXPECT(n == 0))
break;
f(p.get());
p.write_eof(ec);
p.finish(ec);
BEAST_EXPECTS(! ec, ec.message());
break;
}
@ -311,7 +311,7 @@ public:
BEAST_EXPECT(p.maybe_init('-')); p.reset();
}
// write_eof
// finish
{
error_code ec;
number_parser p;
@ -319,7 +319,7 @@ public:
if(BEAST_EXPECTS(! ec,
ec.message()))
{
p.write_eof(ec);
p.finish(ec);
BEAST_EXPECTS(! ec,
ec.message());
}

View File

@ -11,7 +11,7 @@
#include <boost/json/parser.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/json/parser.hpp>
#include <boost/json/block_storage.hpp>
#include <boost/json/serializer.hpp>
#include <sstream>
@ -36,6 +36,8 @@ public:
s.data(),
s.size(),
ec);
if(! ec)
p.finish(ec);
BEAST_EXPECTS(! ec,
ec.message());
//log << " " << to_string_test(p.get()) << std::endl;
@ -92,6 +94,8 @@ public:
error_code ec;
p.start(ss);
p.write(s.data(), i, ec);
if(! ec)
p.finish(ec);
}
}
@ -226,11 +230,11 @@ R"xx({
error_code ec;
parser p;
p.start();
p.write_some(js.data(), N, ec);
p.write(js.data(), N, ec);
if(BEAST_EXPECTS(! ec,
ec.message()))
{
p.write(js.data() + N,
p.finish(js.data() + N,
js.size() - N, ec);
if(BEAST_EXPECTS(! ec,
ec.message()))
@ -302,7 +306,42 @@ R"xx({
"{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1,"
"\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}";
// parse(value)
// parse(string_view, error_code)
{
{
error_code ec;
auto jv = parse(js, ec);
BEAST_EXPECTS(! ec, ec.message());
check_round_trip(jv, js);
}
{
error_code ec;
auto jv = parse("xxx", ec);
BEAST_EXPECT(ec);
BEAST_EXPECT(jv.is_null());
}
}
// parse(string_view, storage_ptr, error_code)
{
{
error_code ec;
scoped_storage<block_storage> sp;
auto jv = parse(js, ec, sp);
BEAST_EXPECTS(! ec, ec.message());
check_round_trip(jv, js);
}
{
error_code ec;
scoped_storage<block_storage> sp;
auto jv = parse("xxx", ec, sp);
BEAST_EXPECT(ec);
BEAST_EXPECT(jv.is_null());
}
}
// parse(string_view)
{
{
check_round_trip(
@ -317,27 +356,17 @@ R"xx({
}
}
// parse(value, storage_ptr)
// parse(string_view, storage_ptr)
{
check_round_trip(
parse(js, storage_ptr{}),
js);
}
// parse(value, error_code)
{
error_code ec;
auto jv = parse(js, ec);
BEAST_EXPECTS(! ec, ec.message());
check_round_trip(jv, js);
}
// parse(value, storage_ptr, error_code)
{
error_code ec;
auto jv = parse(js, storage_ptr{}, ec);
BEAST_EXPECTS(! ec, ec.message());
check_round_trip(jv, js);
{
scoped_storage<block_storage> sp;
check_round_trip(parse(js, sp), js);
}
{
scoped_storage<block_storage> sp;
BEAST_THROWS(parse("xxx", sp),
system_error);
}
}
}