diff --git a/bench/bench.cpp b/bench/bench.cpp index 46179cb9..0d6fe576 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -387,6 +387,11 @@ class boost_null_impl : public any_impl { struct handler { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + bool on_document_begin(error_code&) { return true; } bool on_document_end(error_code&) { return true; } bool on_object_begin(error_code&) { return true; } diff --git a/example/validate.cpp b/example/validate.cpp index 31ba0c23..da71125c 100644 --- a/example/validate.cpp +++ b/example/validate.cpp @@ -26,60 +26,64 @@ using namespace boost::json; //[example_validate +// The null parser discards all the data +class null_parser +{ + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, error_code& ) { return true; } + bool on_string( string_view, std::size_t, error_code& ) { return true; } + bool on_number_part( string_view, error_code& ) { return true; } + bool on_int64( std::int64_t, string_view, error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } + bool on_double( double, string_view, error_code& ) { return true; } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part(string_view, error_code&) { return true; } + bool on_comment(string_view, error_code&) { return true; } + }; + + basic_parser p_; + +public: + null_parser() + : p_(parse_options()) + { + } + + ~null_parser() + { + } + + std::size_t + write( + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write( false, data, size, ec ); + if(! ec && n < size) + ec = error::extra_data; + return n; + } +}; + bool validate( string_view s ) { - // The null parser discards all the data - - class null_parser - { - struct handler - { - bool on_document_begin( error_code& ) { return true; } - bool on_document_end( error_code& ) { return true; } - bool on_object_begin( error_code& ) { return true; } - bool on_object_end( std::size_t, error_code& ) { return true; } - bool on_array_begin( error_code& ) { return true; } - bool on_array_end( std::size_t, error_code& ) { return true; } - bool on_key_part( string_view, std::size_t, error_code& ) { return true; } - bool on_key( string_view, std::size_t, error_code& ) { return true; } - bool on_string_part( string_view, std::size_t, error_code& ) { return true; } - bool on_string( string_view, std::size_t, error_code& ) { return true; } - bool on_number_part( string_view, error_code& ) { return true; } - bool on_int64( std::int64_t, string_view, error_code& ) { return true; } - bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } - bool on_double( double, string_view, error_code& ) { return true; } - bool on_bool( bool, error_code& ) { return true; } - bool on_null( error_code& ) { return true; } - bool on_comment_part(string_view, error_code&) { return true; } - bool on_comment(string_view, error_code&) { return true; } - }; - - basic_parser p_; - - public: - null_parser() - : p_(parse_options()) - { - } - - ~null_parser() - { - } - - std::size_t - write( - char const* data, - std::size_t size, - error_code& ec) - { - auto const n = p_.write( false, data, size, ec ); - if(! ec && n < size) - ec = error::extra_data; - return n; - } - }; - // Parse with the null parser and return false on error null_parser p; error_code ec; diff --git a/fuzzing/fuzz_basic_parser.cpp b/fuzzing/fuzz_basic_parser.cpp index 34075cc3..2a36e3a0 100644 --- a/fuzzing/fuzz_basic_parser.cpp +++ b/fuzzing/fuzz_basic_parser.cpp @@ -17,58 +17,63 @@ using namespace boost::json; +class null_parser +{ + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, error_code& ) { return true; } + bool on_string( string_view, std::size_t, error_code& ) { return true; } + bool on_number_part( string_view, error_code& ) { return true; } + bool on_int64( std::int64_t, string_view, error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } + bool on_double( double, string_view, error_code& ) { return true; } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part(string_view, error_code&) { return true; } + bool on_comment(string_view, error_code&) { return true; } + }; + + basic_parser p_; + +public: + null_parser() + : p_(parse_options()) + { + } + + ~null_parser() + { + } + + std::size_t + write( + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write( false, data, size, ec ); + if(! ec && n < size) + ec = error::extra_data; + return n; + } +}; + bool validate( string_view s ) { - class null_parser - { - struct handler - { - bool on_document_begin( error_code& ) { return true; } - bool on_document_end( error_code& ) { return true; } - bool on_object_begin( error_code& ) { return true; } - bool on_object_end( std::size_t, error_code& ) { return true; } - bool on_array_begin( error_code& ) { return true; } - bool on_array_end( std::size_t, error_code& ) { return true; } - bool on_key_part( string_view, std::size_t, error_code& ) { return true; } - bool on_key( string_view, std::size_t, error_code& ) { return true; } - bool on_string_part( string_view, std::size_t, error_code& ) { return true; } - bool on_string( string_view, std::size_t, error_code& ) { return true; } - bool on_number_part( string_view, error_code& ) { return true; } - bool on_int64( std::int64_t, string_view, error_code& ) { return true; } - bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } - bool on_double( double, string_view, error_code& ) { return true; } - bool on_bool( bool, error_code& ) { return true; } - bool on_null( error_code& ) { return true; } - bool on_comment_part(string_view, error_code&) { return true; } - bool on_comment(string_view, error_code&) { return true; } - }; - - basic_parser p_; - - public: - null_parser() - : p_(parse_options()) - { - } - - ~null_parser() - { - } - - std::size_t - write( - char const* data, - std::size_t size, - error_code& ec) - { - auto const n = p_.write( false, data, size, ec ); - if(! ec && n < size) - ec = error::extra_data; - return n; - } - }; - // Parse with the null parser and return false on error null_parser p; error_code ec; diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp index 4b9a4185..91c37128 100644 --- a/include/boost/json/array.hpp +++ b/include/boost/json/array.hpp @@ -1022,7 +1022,7 @@ public: std::size_t max_size() noexcept { - return BOOST_JSON_MAX_STRUCTURED_SIZE; + return array_impl::max_size(); } /** Return the number of elements that can be held in currently allocated memory. diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp index bf24b07e..c0af0171 100644 --- a/include/boost/json/basic_parser.hpp +++ b/include/boost/json/basic_parser.hpp @@ -965,10 +965,22 @@ parse_unescaped(const char* p) cs = detail::count_valid( cs.begin(), cs.end()); std::size_t size = cs.used(start); - if(BOOST_JSON_UNLIKELY(size > - BOOST_JSON_MAX_STRING_SIZE - total)) - return fail(cs.begin(), IsKey ? - error::key_too_large : error::string_too_large); + if(IsKey) + { + BOOST_ASSERT(total <= Handler::max_key_size); + if(BOOST_JSON_UNLIKELY(size > + Handler::max_key_size - total)) + return fail(cs.begin(), + error::key_too_large); + } + else + { + BOOST_ASSERT(total <= Handler::max_string_size); + if(BOOST_JSON_UNLIKELY(size > + Handler::max_string_size - total)) + return fail(cs.begin(), + error::string_too_large); + } total += size; if(BOOST_JSON_UNLIKELY(! cs)) { @@ -1049,6 +1061,8 @@ parse_escaped( &Handler::on_key_part : &Handler::on_string_part; constexpr auto ev_too_large = IsKey ? error::key_too_large : error::string_too_large; + constexpr auto max_size = IsKey ? + Handler::max_key_size : Handler::max_string_size; detail::clipped_const_stream cs(p, end_); detail::buffer temp; int digit; @@ -1089,8 +1103,9 @@ do_str3: { if(BOOST_JSON_LIKELY(! temp.empty())) { - if(BOOST_JSON_UNLIKELY(temp.size() > - BOOST_JSON_MAX_STRING_SIZE - total)) + BOOST_ASSERT(total <= max_size); + if(BOOST_JSON_UNLIKELY( + temp.size() > max_size - total)) return fail(cs.begin(), ev_too_large); total += temp.size(); if(BOOST_JSON_UNLIKELY( @@ -1238,8 +1253,9 @@ do_str3: // flush if(BOOST_JSON_LIKELY(! temp.empty())) { - if(BOOST_JSON_UNLIKELY(temp.size() > - BOOST_JSON_MAX_STRING_SIZE - total)) + BOOST_ASSERT(total <= max_size); + if(BOOST_JSON_UNLIKELY( + temp.size() > max_size - total)) return fail(cs.begin(), ev_too_large); total += temp.size(); if(BOOST_JSON_UNLIKELY( @@ -1367,8 +1383,9 @@ do_str2: // flush if(BOOST_JSON_LIKELY(! temp.empty())) { - if(BOOST_JSON_UNLIKELY(temp.size() > - BOOST_JSON_MAX_STRING_SIZE - total)) + BOOST_ASSERT(total <= max_size); + if(BOOST_JSON_UNLIKELY( + temp.size() > max_size - total)) return fail(cs.begin(), ev_too_large); total += temp.size(); if(BOOST_JSON_UNLIKELY( @@ -1383,8 +1400,9 @@ do_str2: c = *cs; if(BOOST_JSON_LIKELY(c == '\x22')) // '"' { - if(BOOST_JSON_UNLIKELY(temp.size() > - BOOST_JSON_MAX_STRING_SIZE - total)) + BOOST_ASSERT(total <= max_size); + if(BOOST_JSON_UNLIKELY( + temp.size() > max_size - total)) return fail(cs.begin(), ev_too_large); total += temp.size(); if(BOOST_JSON_UNLIKELY( @@ -1400,8 +1418,9 @@ do_str2: { if(BOOST_JSON_LIKELY(! temp.empty())) { - if(BOOST_JSON_UNLIKELY(temp.size() > - BOOST_JSON_MAX_STRING_SIZE - total)) + BOOST_ASSERT(total <= max_size); + if(BOOST_JSON_UNLIKELY( + temp.size() > max_size - total)) return fail(cs.begin(), ev_too_large); total += temp.size(); if(BOOST_JSON_UNLIKELY( @@ -1512,7 +1531,7 @@ do_obj2: } loop: if(BOOST_JSON_UNLIKELY(++size > - BOOST_JSON_MAX_STRUCTURED_SIZE)) + Handler::max_object_size)) return fail(cs.begin(), error::object_too_large); do_obj3: cs = parse_string - BOOST_JSON_MAX_STRUCTURED_SIZE)) + Handler::max_array_size)) return fail(cs.begin(), error::array_too_large); do_arr3: // array is not empty, value required diff --git a/include/boost/json/detail/array_impl.hpp b/include/boost/json/detail/array_impl.hpp index 5debba6a..aca2ce72 100644 --- a/include/boost/json/detail/array_impl.hpp +++ b/include/boost/json/detail/array_impl.hpp @@ -54,10 +54,7 @@ public: static constexpr std::size_t - max_size() noexcept - { - return BOOST_JSON_MAX_STRUCTURED_SIZE; - } + max_size() noexcept; value* data() const noexcept diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp index 09910585..584a758c 100644 --- a/include/boost/json/detail/basic_parser.hpp +++ b/include/boost/json/detail/basic_parser.hpp @@ -83,6 +83,24 @@ BOOST_JSON_NS_BEGIN the error code to a suitable value. This error code will be returned by the write function to the caller. +\n + Handlers must also define the following + static data members: + + @li `max_object_size`, the maximum number + of elements an object can contain, + + @li `max_array_size`, the maximum number + of elements an array can contain, + + @li `max_key_size`, the maximum length + for object keys, and + + @li `max_string_size`, the maximum + length for strings. + + If a value exceeding these limits + is encountered, parsing fails. \n The following declaration meets the parser's handler requirements: @@ -90,6 +108,12 @@ BOOST_JSON_NS_BEGIN @code struct handler { + /// Value size limits + constexpr static std::size_t max_object_size = -1; + constexpr static std::size_t max_array_size = -1; + constexpr static std::size_t max_key_size = -1; + constexpr static std::size_t max_string_size = -1; + /// Called once when the JSON parsing begins. /// /// @return `true` on success. diff --git a/include/boost/json/detail/impl/array_impl.hpp b/include/boost/json/detail/impl/array_impl.hpp index ae5591b0..8716f83f 100644 --- a/include/boost/json/detail/impl/array_impl.hpp +++ b/include/boost/json/detail/impl/array_impl.hpp @@ -13,6 +13,18 @@ BOOST_JSON_NS_BEGIN namespace detail { +constexpr +std::size_t +array_impl:: +max_size() noexcept +{ + // max_size depends on the address model + using min = std::integral_constant; + return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ? + min::value : BOOST_JSON_MAX_STRUCTURED_SIZE; +} + auto array_impl:: index_of(value const* pos) const noexcept -> diff --git a/include/boost/json/detail/impl/array_impl.ipp b/include/boost/json/detail/impl/array_impl.ipp index 9a5232d4..bad83bf9 100644 --- a/include/boost/json/detail/impl/array_impl.ipp +++ b/include/boost/json/detail/impl/array_impl.ipp @@ -21,16 +21,14 @@ array_impl( std::size_t capacity, storage_ptr const& sp) { - auto constexpr soft_limit = - (std::size_t(-1) - sizeof(table)) / - sizeof(value); - if( capacity > soft_limit || - capacity > max_size()) + if(capacity > max_size()) detail::throw_length_error( - "array too large", + "capacity > max_size()", BOOST_CURRENT_LOCATION); if(capacity > 0) { + // make sure to update max_size + // if this is changed tab_ = ::new(sp->allocate( (sizeof(table) + capacity * sizeof(value) + diff --git a/include/boost/json/detail/impl/object_impl.hpp b/include/boost/json/detail/impl/object_impl.hpp index 0c139c67..2edd6150 100644 --- a/include/boost/json/detail/impl/object_impl.hpp +++ b/include/boost/json/detail/impl/object_impl.hpp @@ -16,6 +16,19 @@ BOOST_JSON_NS_BEGIN namespace detail { +constexpr +std::size_t +object_impl:: +max_size() noexcept +{ + // max_size depends on the address model + using min = std::integral_constant; + return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ? + min::value : BOOST_JSON_MAX_STRUCTURED_SIZE; +} + void object_impl:: remove( @@ -41,7 +54,7 @@ bucket_sizes() noexcept -> { // Taken from Boost.Intrusive and Boost.MultiIndex code, // thanks to Ion Gaztanaga and Joaquin M Lopez Munoz. - static constexpr std::size_t list[33] = + static constexpr std::size_t list[34] = { 0, @@ -61,7 +74,9 @@ bucket_sizes() noexcept -> 100663319, 201326611, 402653189, 805306457, 1610612741, - BOOST_JSON_MAX_STRUCTURED_SIZE // 3221225473 + BOOST_JSON_MAX_STRUCTURED_SIZE, + // catch anything that exceeds max_size + std::size_t(-1) }; return list; } diff --git a/include/boost/json/detail/impl/object_impl.ipp b/include/boost/json/detail/impl/object_impl.ipp index cfaaeedf..2557ffac 100644 --- a/include/boost/json/detail/impl/object_impl.ipp +++ b/include/boost/json/detail/impl/object_impl.ipp @@ -37,14 +37,12 @@ object_impl( std::uintptr_t salt, storage_ptr const& sp) { - // max capacity based on address model - auto constexpr soft_limit = - (std::size_t(-1) - sizeof(table)) / ( - sizeof(value_type) + sizeof(index_t)); - if(capacity > soft_limit) + if(capacity > max_size()) throw_length_error( - "capacity > soft_limit", + "capacity > max_size()", BOOST_CURRENT_LOCATION); + // make sure to update max_size + // if this is changed const auto n = sizeof(table) + capacity * diff --git a/include/boost/json/detail/object_impl.hpp b/include/boost/json/detail/object_impl.hpp index 12f704c8..9ea7e8ce 100644 --- a/include/boost/json/detail/object_impl.hpp +++ b/include/boost/json/detail/object_impl.hpp @@ -31,7 +31,7 @@ class object_impl using index_t = std::uint32_t; static index_t const null_index = std::uint32_t(-1); - using bucket_size_array = const std::size_t [33]; + using bucket_size_array = const std::size_t[34]; BOOST_JSON_DECL void @@ -62,6 +62,11 @@ public: do_destroy(sp); } + static + constexpr + std::size_t + max_size() noexcept; + std::size_t size() const noexcept { diff --git a/include/boost/json/detail/string_impl.hpp b/include/boost/json/detail/string_impl.hpp index 0ed397b8..f92cbed3 100644 --- a/include/boost/json/detail/string_impl.hpp +++ b/include/boost/json/detail/string_impl.hpp @@ -97,7 +97,11 @@ public: std::size_t max_size() noexcept { - return BOOST_JSON_MAX_STRING_SIZE; + // max_size depends on the address model + using min = std::integral_constant; + return min::value < BOOST_JSON_MAX_STRING_SIZE ? + min::value : BOOST_JSON_MAX_STRING_SIZE; } BOOST_JSON_DECL diff --git a/include/boost/json/impl/object.hpp b/include/boost/json/impl/object.hpp index 5ad261fb..86c0f6db 100644 --- a/include/boost/json/impl/object.hpp +++ b/include/boost/json/impl/object.hpp @@ -212,10 +212,6 @@ reserve(std::size_t new_capacity) { if(new_capacity <= capacity()) return; - if(new_capacity > max_size()) - detail::throw_length_error( - "new_capacity > max_size()", - BOOST_CURRENT_LOCATION); rehash(new_capacity); } diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp index b6033a21..0456195a 100644 --- a/include/boost/json/impl/object.ipp +++ b/include/boost/json/impl/object.ipp @@ -509,12 +509,12 @@ rehash(std::size_t new_capacity) BOOST_ASSERT(new_capacity > capacity()); std::size_t const* prime = object_impl::bucket_sizes(); - while(new_capacity > *prime) + while(*prime < new_capacity) ++prime; new_capacity = *prime; if(new_capacity > max_size()) detail::throw_length_error( - "object too large", + "new_capacity > max_size()", BOOST_CURRENT_LOCATION); object_impl impl( new_capacity, diff --git a/include/boost/json/object.hpp b/include/boost/json/object.hpp index 9ed32a67..1a6242b6 100644 --- a/include/boost/json/object.hpp +++ b/include/boost/json/object.hpp @@ -860,7 +860,7 @@ public: std::size_t max_size() noexcept { - return BOOST_JSON_MAX_STRUCTURED_SIZE; + return object_impl::max_size(); } /** Return the number of elements that can be held in currently allocated memory diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index abf5186c..4d6a9de2 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -104,6 +104,18 @@ class parser { struct handler { + constexpr static std::size_t + max_object_size = object::max_size(); + + constexpr static std::size_t + max_array_size = array::max_size(); + + constexpr static std::size_t + max_key_size = string::max_size(); + + constexpr static std::size_t + max_string_size = string::max_size(); + value_stack st; template diff --git a/include/boost/json/string.hpp b/include/boost/json/string.hpp index ed45aea8..ff0f22a1 100644 --- a/include/boost/json/string.hpp +++ b/include/boost/json/string.hpp @@ -62,6 +62,8 @@ class string friend struct detail::value_access; #endif + using string_impl = detail::string_impl; + inline string( char** key, @@ -130,7 +132,7 @@ private: char>::value>::type; storage_ptr sp_; // must come first - detail::string_impl impl_; + string_impl impl_; public: /** Destructor. @@ -190,7 +192,7 @@ public: : sp_(std::move(other.get().sp_)) , impl_(other.get().impl_) { - ::new(&other.get().impl_) detail::string_impl(); + ::new(&other.get().impl_) string_impl(); } /** Constructor. @@ -437,7 +439,7 @@ public: : sp_(other.sp_) , impl_(other.impl_) { - ::new(&other.impl_) detail::string_impl(); + ::new(&other.impl_) string_impl(); } /** Constructor. @@ -1433,7 +1435,7 @@ public: std::size_t max_size() noexcept { - return BOOST_JSON_MAX_STRING_SIZE; + return string_impl::max_size(); } /** Return the number of characters that can be held without a reallocation. diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp index 132f7c87..cd48d5c8 100644 --- a/test/basic_parser.cpp +++ b/test/basic_parser.cpp @@ -872,6 +872,72 @@ public: good_one(s); } + class comment_parser + { + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + std::string captured = ""; + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, error_code& ) { return true; } + bool on_string( string_view, std::size_t, error_code& ) { return true; } + bool on_number_part( string_view, error_code&) { return true; } + bool on_int64( std::int64_t, string_view, error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } + bool on_double( double, string_view, error_code& ) { return true; } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part( string_view s, error_code& ) + { + captured.append(s.data(), s.size()); + return true; + } + bool on_comment( string_view s, error_code& ) + { + captured.append(s.data(), s.size()); + return true; + } + }; + + basic_parser p_; + + public: + comment_parser() + : p_(make_options(true, false, false)) + { + } + + std::size_t + write( + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write( + false, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } + + string_view + captured() const noexcept + { + return p_.handler().captured; + } + }; + void testComments() { @@ -899,67 +965,6 @@ public: "/*** aaaa***/" }; - class comment_parser - { - struct handler - { - std::string captured = ""; - bool on_document_begin( error_code& ) { return true; } - bool on_document_end( error_code& ) { return true; } - bool on_object_begin( error_code& ) { return true; } - bool on_object_end( std::size_t, error_code& ) { return true; } - bool on_array_begin( error_code& ) { return true; } - bool on_array_end( std::size_t, error_code& ) { return true; } - bool on_key_part( string_view, std::size_t, error_code& ) { return true; } - bool on_key( string_view, std::size_t, error_code& ) { return true; } - bool on_string_part( string_view, std::size_t, error_code& ) { return true; } - bool on_string( string_view, std::size_t, error_code& ) { return true; } - bool on_number_part( string_view, error_code&) { return true; } - bool on_int64( std::int64_t, string_view, error_code& ) { return true; } - bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } - bool on_double( double, string_view, error_code& ) { return true; } - bool on_bool( bool, error_code& ) { return true; } - bool on_null( error_code& ) { return true; } - bool on_comment_part( string_view s, error_code& ) - { - captured.append(s.data(), s.size()); - return true; - } - bool on_comment( string_view s, error_code& ) - { - captured.append(s.data(), s.size()); - return true; - } - }; - - basic_parser p_; - - public: - comment_parser() - : p_(make_options(true, false, false)) - { - } - - std::size_t - write( - char const* data, - std::size_t size, - error_code& ec) - { - auto const n = p_.write( - false, data, size, ec); - if(! ec && n < size) - ec = error::extra_data; - return n; - } - - string_view - captured() const noexcept - { - return p_.handler().captured; - } - }; - std::string formatted = ""; std::string just_comments = ""; std::size_t guess = std::count( @@ -1064,6 +1069,73 @@ public: good("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{},},},},},},}", enabled); } + class utf8_parser + { + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + std::string captured = ""; + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view sv, std::size_t, error_code& ) + { + captured.append(sv.data(), sv.size()); + return true; + } + bool on_string( string_view sv, std::size_t, error_code& ) + { + captured.append(sv.data(), sv.size()); + return true; + } + bool on_number_part( string_view, error_code&) { return true; } + bool on_int64( std::int64_t, string_view, error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } + bool on_double( double, string_view, error_code& ) { return true; } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part( string_view, error_code& ) { return true; } + bool on_comment( string_view, error_code& ) { return true; } + }; + + basic_parser p_; + + public: + utf8_parser() + : p_(parse_options()) + { + } + + std::size_t + write( + bool more, + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write( + more, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } + + string_view + captured() const noexcept + { + return p_.handler().captured; + } + }; + void testUTF8Validation() { @@ -1228,68 +1300,6 @@ public: bad("\"\\n\xf4\x80\x70\x80----------\""); bad("\"\\n\xf4\x80\xbf\x70-\\n\xf4\x80\xbf\x70\""); - class utf8_parser - { - struct handler - { - std::string captured = ""; - bool on_document_begin( error_code& ) { return true; } - bool on_document_end( error_code& ) { return true; } - bool on_object_begin( error_code& ) { return true; } - bool on_object_end( std::size_t, error_code& ) { return true; } - bool on_array_begin( error_code& ) { return true; } - bool on_array_end( std::size_t, error_code& ) { return true; } - bool on_key_part( string_view, std::size_t, error_code& ) { return true; } - bool on_key( string_view, std::size_t, error_code& ) { return true; } - bool on_string_part( string_view sv, std::size_t, error_code& ) - { - captured.append(sv.data(), sv.size()); - return true; - } - bool on_string( string_view sv, std::size_t, error_code& ) - { - captured.append(sv.data(), sv.size()); - return true; - } - bool on_number_part( string_view, error_code&) { return true; } - bool on_int64( std::int64_t, string_view, error_code& ) { return true; } - bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } - bool on_double( double, string_view, error_code& ) { return true; } - bool on_bool( bool, error_code& ) { return true; } - bool on_null( error_code& ) { return true; } - bool on_comment_part( string_view, error_code& ) { return true; } - bool on_comment( string_view, error_code& ) { return true; } - }; - - basic_parser p_; - - public: - utf8_parser() - : p_(parse_options()) - { - } - - std::size_t - write( - bool more, - char const* data, - std::size_t size, - error_code& ec) - { - auto const n = p_.write( - more, data, size, ec); - if(! ec && n < size) - ec = error::extra_data; - return n; - } - - string_view - captured() const noexcept - { - return p_.handler().captured; - } - }; - const auto check = [this](string_view expected) { @@ -1360,85 +1370,89 @@ public: } } + class literal_parser + { + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + std::string captured = ""; + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, error_code& ) { return true; } + bool on_string( string_view, std::size_t, error_code& ) { return true; } + bool on_number_part( string_view sv, error_code&) + { + captured.append(sv.data(), sv.size()); + return true; + } + bool on_int64( std::int64_t, string_view sv, error_code& ) + { + captured.append(sv.data(), sv.size()); + captured += 's'; + return true; + } + bool on_uint64( std::uint64_t, string_view sv, error_code& ) + { + captured.append(sv.data(), sv.size()); + captured += 'u'; + return true; + } + bool on_double( double, string_view sv, error_code& ) + { + captured.append(sv.data(), sv.size()); + captured += 'd'; + return true; + } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part( string_view, error_code& ) { return true; } + bool on_comment( string_view, error_code& ) { return true; } + }; + + basic_parser p_; + + public: + literal_parser() + : p_(make_options(true, false, false)) + { + } + + std::size_t + write( + bool more, + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write( + more, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } + + string_view + captured() + { + return p_.handler().captured; + } + }; + void testNumberLiteral() { - class literal_parser - { - struct handler - { - std::string captured = ""; - - bool on_document_begin( error_code& ) { return true; } - bool on_document_end( error_code& ) { return true; } - bool on_object_begin( error_code& ) { return true; } - bool on_object_end( std::size_t, error_code& ) { return true; } - bool on_array_begin( error_code& ) { return true; } - bool on_array_end( std::size_t, error_code& ) { return true; } - bool on_key_part( string_view, std::size_t, error_code& ) { return true; } - bool on_key( string_view, std::size_t, error_code& ) { return true; } - bool on_string_part( string_view, std::size_t, error_code& ) { return true; } - bool on_string( string_view, std::size_t, error_code& ) { return true; } - bool on_number_part( string_view sv, error_code&) - { - captured.append(sv.data(), sv.size()); - return true; - } - bool on_int64( std::int64_t, string_view sv, error_code& ) - { - captured.append(sv.data(), sv.size()); - captured += 's'; - return true; - } - bool on_uint64( std::uint64_t, string_view sv, error_code& ) - { - captured.append(sv.data(), sv.size()); - captured += 'u'; - return true; - } - bool on_double( double, string_view sv, error_code& ) - { - captured.append(sv.data(), sv.size()); - captured += 'd'; - return true; - } - bool on_bool( bool, error_code& ) { return true; } - bool on_null( error_code& ) { return true; } - bool on_comment_part( string_view, error_code& ) { return true; } - bool on_comment( string_view, error_code& ) { return true; } - }; - - basic_parser p_; - - public: - literal_parser() - : p_(make_options(true, false, false)) - { - } - - std::size_t - write( - bool more, - char const* data, - std::size_t size, - error_code& ec) - { - auto const n = p_.write( - more, data, size, ec); - if(! ec && n < size) - ec = error::extra_data; - return n; - } - - string_view - captured() - { - return p_.handler().captured; - } - }; - const auto check = - [](string_view expected) + [](string_view expected) { string_view sv = expected; sv.remove_suffix(1); diff --git a/test/limits.cpp b/test/limits.cpp index e117ceae..ad2cc92c 100644 --- a/test/limits.cpp +++ b/test/limits.cpp @@ -286,14 +286,8 @@ public: auto jv = parse(s, ec); BOOST_TEST(! ec); } - } - - void - testBasicParser() - { // overflow in on_key_part { - null_parser p; error_code ec; std::string big; big = "\\b"; @@ -301,13 +295,12 @@ public: string::max_size()*2, '*'); auto const js = "{\"" + big + "\":null}"; - p.write(js.data(), js.size(), ec); + auto jv = parse(js, ec); BOOST_TEST(ec == error::key_too_large); } // overflow in on_key { - null_parser p; error_code ec; std::string big; big = "\\b"; @@ -315,13 +308,12 @@ public: (string::max_size()*3)/2, '*'); auto const js = "{\"" + big + "\":null}"; - p.write(js.data(), js.size(), ec); + auto jv = parse(js, ec); BOOST_TEST(ec == error::key_too_large); } // overflow in on_string_part { - null_parser p; error_code ec; std::string big; big = "\\b"; @@ -329,13 +321,12 @@ public: string::max_size()*2, '*'); auto const js = "\"" + big + "\""; - p.write(js.data(), js.size(), ec); + auto jv = parse(js, ec); BOOST_TEST(ec == error::string_too_large); } // overflow in on_string { - null_parser p; error_code ec; std::string big; big = "\\b"; @@ -343,14 +334,13 @@ public: (string::max_size()*3)/2, '*'); auto const js = "\"" + big + "\""; - p.write(js.data(), js.size(), ec); + auto jv = parse(js, ec); BOOST_TEST(ec == error::string_too_large); } // object overflow { - null_parser p; error_code ec; string_view s = R"({ "00":0,"01":0,"02":0,"03":0,"04":0,"05":0,"06":0,"07":0,"08":0,"09":0, @@ -358,20 +348,19 @@ public: "20":0 })"; - p.write(s.data(), s.size(), ec); + auto jv = parse(s, ec); BOOST_TEST(ec == error::object_too_large); } // array overflow { - null_parser p; error_code ec; string_view s = "[" "0,0,0,0,0,0,0,0,0,0," "0,0,0,0,0,0,0,0,0,0," "0" "]"; - p.write(s.data(), s.size(), ec); + auto jv = parse(s, ec); BOOST_TEST(ec == error::array_too_large); } } @@ -390,7 +379,6 @@ public: testString(); testStack(); testParser(); - testBasicParser(); #else BOOST_TEST_PASS(); diff --git a/test/test.hpp b/test/test.hpp index 5a2c7b64..dfabd171 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -145,6 +145,11 @@ class null_parser { struct handler { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + bool on_document_begin( error_code& ) { return true; } bool on_document_end( error_code& ) { return true; } bool on_object_begin( error_code& ) { return true; } @@ -205,6 +210,11 @@ class fail_parser { struct handler { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + std::size_t n; handler() @@ -445,6 +455,11 @@ class throw_parser { struct handler { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + std::size_t n; handler() diff --git a/test/value.cpp b/test/value.cpp index 530c40f4..42e1a6c0 100644 --- a/test/value.cpp +++ b/test/value.cpp @@ -1640,13 +1640,7 @@ public: check_array(value{false,2}, false, 2); check_array(value{false,2,"3",nullptr}, false, 2, "3", nullptr); check_array(value{2,false,"3"}, 2, false, "3"); - check_array(value{true,2,"3"}, true, 2, "3"); } - - void - testMaxSize() - { - // The implementation requires these equal - BOOST_TEST(object::max_size() == array::max_size()); + check_array(value{true,2,"3"}, true, 2, "3"); } //------------------------------------------------------ @@ -1667,7 +1661,6 @@ public: testKeyValuePair(); testStdConstruction(); testInitList(); - testMaxSize(); } };