basic_parser is a base

This commit is contained in:
Vinnie Falco 2020-04-04 06:47:13 -07:00
parent 845d7af5e3
commit 2545475bdd
8 changed files with 91 additions and 152 deletions

View File

@ -359,10 +359,9 @@ public:
class boost_vec_impl : public any_impl class boost_vec_impl : public any_impl
{ {
struct vec_parser struct vec_parser : basic_parser
{ {
friend class basic_parser; friend class basic_parser;
basic_parser p_;
std::vector<double> vec_; std::vector<double> vec_;
double d_ = 0; double d_ = 0;
@ -394,7 +393,8 @@ class boost_vec_impl : public any_impl
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
this->basic_parser::write_some(
*this, false, data, size, ec); *this, false, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;
@ -438,10 +438,9 @@ public:
class boost_null_impl : public any_impl class boost_null_impl : public any_impl
{ {
struct null_parser struct null_parser : basic_parser
{ {
friend class basic_parser; friend class basic_parser;
basic_parser p_;
null_parser() {} null_parser() {}
~null_parser() {} ~null_parser() {}
@ -460,18 +459,18 @@ class boost_null_impl : public any_impl
bool on_double(double, error_code&) { return true; } bool on_double(double, error_code&) { return true; }
bool on_bool(bool, error_code&) { return true; } bool on_bool(bool, error_code&) { return true; }
bool on_null(error_code&) { return true; } bool on_null(error_code&) { return true; }
void reset()
{ using basic_parser::reset;
p_.reset();
}
std::size_t std::size_t
write( write(
char const* data, char const* data,
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
*this, false, data, size, ec); basic_parser::write_some(
*this, false, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;
return n; return n;

View File

@ -32,10 +32,9 @@ validate( string_view s )
{ {
// The null parser discards all the data // The null parser discards all the data
class null_parser class null_parser : public basic_parser
{ {
friend class boost::json::basic_parser; friend class boost::json::basic_parser;
boost::json::basic_parser p_;
public: public:
null_parser() {} null_parser() {}
@ -56,25 +55,14 @@ validate( string_view s )
bool on_bool( bool, error_code& ) { return true; } bool on_bool( bool, error_code& ) { return true; }
bool on_null( error_code& ) { return true; } bool on_null( error_code& ) { return true; }
bool
is_done() const noexcept
{
return p_.is_done();
}
void
reset()
{
p_.reset();
}
std::size_t std::size_t
write( write(
char const* data, char const* data,
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
basic_parser::write_some(
*this, false, data, size, ec); *this, false, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;

View File

@ -1333,6 +1333,12 @@ parse_object(
{ {
BOOST_ASSERT(*cs == '{'); BOOST_ASSERT(*cs == '{');
++depth_; ++depth_;
if(BOOST_JSON_UNLIKELY(
depth_ > max_depth_))
{
ec_ = error::too_deep;
return result::fail;
}
if(BOOST_JSON_UNLIKELY( if(BOOST_JSON_UNLIKELY(
! h.on_object_begin(ec_))) ! h.on_object_begin(ec_)))
return result::fail; return result::fail;
@ -1471,6 +1477,12 @@ parse_array(
{ {
BOOST_ASSERT(*cs == '['); BOOST_ASSERT(*cs == '[');
++depth_; ++depth_;
if(BOOST_JSON_UNLIKELY(
depth_ > max_depth_))
{
ec_ = error::too_deep;
return result::fail;
}
if(BOOST_JSON_UNLIKELY( if(BOOST_JSON_UNLIKELY(
! h.on_array_begin(ec_))) ! h.on_array_begin(ec_)))
return result::fail; return result::fail;

View File

@ -50,6 +50,24 @@ namespace json {
to cheaply re-use this memory when parsing to cheaply re-use this memory when parsing
subsequent JSONs, improving performance. subsequent JSONs, improving performance.
@par Usage
Users who wish to parse JSON into the DOM container
@ref value will not use this class directly; instead
they will create an instance of @ref parser and
use that instead. Alternatively, they may call the
function @ref parse. This class is designed for
users who wish to perform custom actions instead of
building a @ref value. For example, to produce a
DOM from an external library.
<br>
To use this class it is necessary to create a derived
class which calls @ref reset at the beginning of
parsing a new JSON, and then calls @ref write_some one
or more times with the serialized JSON.
@note @note
The parser is strict: no extensions are supported. The parser is strict: no extensions are supported.
@ -82,6 +100,7 @@ class basic_parser
error_code ec_; error_code ec_;
detail::stack st_; detail::stack st_;
std::size_t depth_ = 0; std::size_t depth_ = 0;
std::size_t max_depth_ = 32;
unsigned u1_; unsigned u1_;
unsigned u2_; unsigned u2_;
bool more_; // false for final buffer bool more_; // false for final buffer
@ -161,6 +180,30 @@ public:
return depth_; return depth_;
} }
/** Returns the maximum allowed depth of input JSON.
The maximum allowed depth may be configured.
*/
std::size_t
max_depth() const noexcept
{
return max_depth_;
}
/** Set the maximum allowed depth of input JSON.
When the maximum depth is exceeded, parser
operations will return @ref error::too_deep.
@param levels The maximum depth.
*/
void
max_depth(unsigned long levels) noexcept
{
max_depth_ = levels;
}
protected:
/** Reset the state, to parse a new document. /** Reset the state, to parse a new document.
*/ */
inline inline

View File

@ -182,7 +182,7 @@ clear() noexcept
{ {
destroy(); destroy();
rs_.clear(); rs_.clear();
p_.reset(); basic_parser::reset();
lev_.count = 0; lev_.count = 0;
key_size_ = 0; key_size_ = 0;
str_size_ = 0; str_size_ = 0;
@ -197,7 +197,7 @@ write_some(
std::size_t const size, std::size_t const size,
error_code& ec) error_code& ec)
{ {
return p_.write_some( return basic_parser::write_some(
*this, true, data, size, ec); *this, true, data, size, ec);
} }
@ -223,9 +223,9 @@ write(
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
*this, true, basic_parser::write_some(
data, size, ec); *this, true, data, size, ec);
if(! ec) if(! ec)
{ {
if(n < size) if(n < size)
@ -253,8 +253,9 @@ finish(
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
*this, false, data, size, ec); basic_parser::write_some(
*this, false, data, size, ec);
if(! ec) if(! ec)
{ {
if(n < size) if(n < size)
@ -302,7 +303,7 @@ release()
std::logic_error( std::logic_error(
"no value")); "no value"));
BOOST_ASSERT(lev_.count == 1); BOOST_ASSERT(lev_.count == 1);
BOOST_ASSERT(p_.depth() == 0); BOOST_ASSERT(depth() == 0);
auto ua = pop_array(); auto ua = pop_array();
BOOST_ASSERT(rs_.empty()); BOOST_ASSERT(rs_.empty());
union U union U
@ -313,7 +314,7 @@ release()
}; };
U u; U u;
ua.relocate(&u.v); ua.relocate(&u.v);
p_.reset(); basic_parser::reset();
lev_.st = state::need_start; lev_.st = state::need_start;
sp_ = {}; sp_ = {};
return pilfer(u.v); return pilfer(u.v);
@ -484,11 +485,6 @@ bool
parser:: parser::
on_object_begin(error_code& ec) on_object_begin(error_code& ec)
{ {
if(p_.depth() >= max_depth_)
{
ec = error::too_deep;
return false;
}
// prevent splits from exceptions // prevent splits from exceptions
rs_.prepare( rs_.prepare(
sizeof(level) + sizeof(level) +
@ -528,11 +524,6 @@ bool
parser:: parser::
on_array_begin(error_code& ec) on_array_begin(error_code& ec)
{ {
if(p_.depth() >= max_depth_)
{
ec = error::too_deep;
return false;
}
// prevent splits from exceptions // prevent splits from exceptions
rs_.prepare( rs_.prepare(
sizeof(level) + sizeof(level) +

View File

@ -65,7 +65,7 @@ namespace json {
to cheaply re-use this memory when parsing to cheaply re-use this memory when parsing
subsequent JSONs, improving performance. subsequent JSONs, improving performance.
*/ */
class parser class parser : public basic_parser
{ {
friend class basic_parser; friend class basic_parser;
enum class state : char; enum class state : char;
@ -76,10 +76,8 @@ class parser
state st; state st;
}; };
basic_parser p_;
storage_ptr sp_; storage_ptr sp_;
detail::raw_stack rs_; detail::raw_stack rs_;
std::size_t max_depth_ = 32;
std::uint32_t key_size_ = 0; std::uint32_t key_size_ = 0;
std::uint32_t str_size_ = 0; std::uint32_t str_size_ = 0;
level lev_; level lev_;
@ -140,62 +138,6 @@ public:
void void
start(storage_ptr sp = {}) noexcept; start(storage_ptr sp = {}) noexcept;
/** Returns the current depth of the JSON being parsed.
The parsing depth is the total current nesting
level of arrays and objects.
*/
std::size_t
depth() const noexcept
{
return p_.depth();
}
/** Returns the maximum allowed depth of input JSON.
The maximum allowed depth may be configured.
*/
std::size_t
max_depth() const noexcept
{
return max_depth_;
}
/** Set the maximum allowed depth of input JSON.
When the maximum depth is exceeded, parser
operations will return @ref error::too_deep.
@param levels The maximum depth.
*/
void
max_depth(unsigned long levels) noexcept
{
max_depth_ = levels;
}
/** Return true if a complete JSON has been parsed.
This function returns `true` when all of these
conditions are met:
@li A complete serialized JSON has been
presented to the parser, and
@li No error has occurred since the parser
was constructed, or since the last call
to @ref reset,
@par Complexity
Constant.
*/
bool
is_done() const noexcept
{
return p_.is_done();
}
/** Parse JSON incrementally. /** Parse JSON incrementally.
This function parses the JSON in the specified This function parses the JSON in the specified

View File

@ -106,10 +106,9 @@ validate( string_view s )
{ {
// The null parser discards all the data // The null parser discards all the data
class null_parser class null_parser : public basic_parser
{ {
friend class boost::json::basic_parser; friend class boost::json::basic_parser;
boost::json::basic_parser p_;
public: public:
null_parser() {} null_parser() {}
@ -130,25 +129,14 @@ validate( string_view s )
bool on_bool( bool, error_code& ) { return true; } bool on_bool( bool, error_code& ) { return true; }
bool on_null( error_code& ) { return true; } bool on_null( error_code& ) { return true; }
bool
is_done() const noexcept
{
return p_.is_done();
}
void
reset()
{
p_.reset();
}
std::size_t std::size_t
write( write(
char const* data, char const* data,
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
basic_parser::write_some(
*this, false, data, size, ec); *this, false, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;

View File

@ -142,12 +142,11 @@ struct unique_storage
//---------------------------------------------------------- //----------------------------------------------------------
class fail_parser class fail_parser : public basic_parser
{ {
friend class basic_parser; friend class basic_parser;
std::size_t n_ = std::size_t(-1); std::size_t n_ = std::size_t(-1);
basic_parser p_;
bool bool
maybe_fail(error_code& ec) maybe_fail(error_code& ec)
@ -282,18 +281,6 @@ public:
{ {
} }
bool
is_done() const noexcept
{
return p_.is_done();
}
void
reset()
{
p_.reset();
}
std::size_t std::size_t
write_some( write_some(
bool more, bool more,
@ -301,7 +288,7 @@ public:
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
return p_.write_some( return basic_parser::write_some(
*this, more, data, size, ec); *this, more, data, size, ec);
} }
@ -312,8 +299,9 @@ public:
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
*this, more, data, size, ec); basic_parser::write_some(
*this, more, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;
return n; return n;
@ -333,10 +321,9 @@ struct test_exception
}; };
// Exercises every exception path // Exercises every exception path
class throw_parser class throw_parser : public basic_parser
{ {
friend class basic_parser; friend class basic_parser;
basic_parser p_;
std::size_t n_ = std::size_t(-1); std::size_t n_ = std::size_t(-1);
bool bool
@ -471,18 +458,6 @@ public:
{ {
} }
bool
is_done() const noexcept
{
return p_.is_done();
}
void
reset()
{
p_.reset();
}
std::size_t std::size_t
write( write(
bool more, bool more,
@ -490,8 +465,9 @@ public:
std::size_t size, std::size_t size,
error_code& ec) error_code& ec)
{ {
auto const n = p_.write_some( auto const n =
*this, more, data, size, ec); basic_parser::write_some(
*this, more, data, size, ec);
if(! ec && n < size) if(! ec && n < size)
ec = error::extra_data; ec = error::extra_data;
return n; return n;