diff --git a/bench/bench.cpp b/bench/bench.cpp index f8050eed..d1e3a827 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -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 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; diff --git a/example/validate.cpp b/example/validate.cpp index e25f2e04..65e12f3f 100644 --- a/example/validate.cpp +++ b/example/validate.cpp @@ -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; diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp index ff1a7c85..1ee98430 100644 --- a/include/boost/json/basic_parser.hpp +++ b/include/boost/json/basic_parser.hpp @@ -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; diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp index 9e26b720..f2e0295c 100644 --- a/include/boost/json/detail/basic_parser.hpp +++ b/include/boost/json/detail/basic_parser.hpp @@ -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. + +
+ + 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 diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index dce4974c..c83650f5 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -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) + diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index a0b3180c..9b7a8b9e 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -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 diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp index ed8e628b..bb8d2fe6 100644 --- a/test/basic_parser.cpp +++ b/test/basic_parser.cpp @@ -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; diff --git a/test/test.hpp b/test/test.hpp index 448e8b3a..5d1159b9 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -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;