mirror of
https://github.com/boostorg/json.git
synced 2025-05-11 21:53:58 +00:00
basic_parser is a base
This commit is contained in:
parent
845d7af5e3
commit
2545475bdd
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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) +
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user