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

View File

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

View File

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

View File

@ -50,6 +50,24 @@ namespace json {
to cheaply re-use this memory when parsing
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
The parser is strict: no extensions are supported.
@ -82,6 +100,7 @@ class basic_parser
error_code ec_;
detail::stack st_;
std::size_t depth_ = 0;
std::size_t max_depth_ = 32;
unsigned u1_;
unsigned u2_;
bool more_; // false for final buffer
@ -161,6 +180,30 @@ public:
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.
*/
inline

View File

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

View File

@ -65,7 +65,7 @@ namespace json {
to cheaply re-use this memory when parsing
subsequent JSONs, improving performance.
*/
class parser
class parser : public basic_parser
{
friend class basic_parser;
enum class state : char;
@ -76,10 +76,8 @@ class parser
state st;
};
basic_parser p_;
storage_ptr sp_;
detail::raw_stack rs_;
std::size_t max_depth_ = 32;
std::uint32_t key_size_ = 0;
std::uint32_t str_size_ = 0;
level lev_;
@ -140,62 +138,6 @@ public:
void
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.
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
class null_parser
class null_parser : public basic_parser
{
friend class boost::json::basic_parser;
boost::json::basic_parser p_;
public:
null_parser() {}
@ -130,25 +129,14 @@ validate( string_view s )
bool on_bool( bool, 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
write(
char const* data,
std::size_t size,
error_code& ec)
{
auto const n = p_.write_some(
auto const n =
basic_parser::write_some(
*this, false, data, size, ec);
if(! ec && n < size)
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;
std::size_t n_ = std::size_t(-1);
basic_parser p_;
bool
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
write_some(
bool more,
@ -301,7 +288,7 @@ public:
std::size_t size,
error_code& ec)
{
return p_.write_some(
return basic_parser::write_some(
*this, more, data, size, ec);
}
@ -312,8 +299,9 @@ public:
std::size_t size,
error_code& ec)
{
auto const n = p_.write_some(
*this, more, data, size, ec);
auto const n =
basic_parser::write_some(
*this, more, data, size, ec);
if(! ec && n < size)
ec = error::extra_data;
return n;
@ -333,10 +321,9 @@ struct test_exception
};
// Exercises every exception path
class throw_parser
class throw_parser : public basic_parser
{
friend class basic_parser;
basic_parser p_;
std::size_t n_ = std::size_t(-1);
bool
@ -471,18 +458,6 @@ public:
{
}
bool
is_done() const noexcept
{
return p_.is_done();
}
void
reset()
{
p_.reset();
}
std::size_t
write(
bool more,
@ -490,8 +465,9 @@ public:
std::size_t size,
error_code& ec)
{
auto const n = p_.write_some(
*this, more, data, size, ec);
auto const n =
basic_parser::write_some(
*this, more, data, size, ec);
if(! ec && n < size)
ec = error::extra_data;
return n;