From 0c232111c6b75765354bd3c656167d2aefbc7022 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 14 Oct 2019 12:06:27 -0700 Subject: [PATCH] basic_parser work --- .travis.yml | 1 + include/boost/json/assign_string.hpp | 4 +- include/boost/json/assign_vector.hpp | 4 +- include/boost/json/basic_parser.hpp | 2 + include/boost/json/detail/basic_parser.hpp | 50 ++ include/boost/json/detail/buffer.hpp | 116 ++++ include/boost/json/detail/stack.hpp | 4 +- include/boost/json/error.hpp | 95 ++- include/boost/json/impl/basic_parser.ipp | 668 +++++++++++++-------- include/boost/json/impl/error.ipp | 133 +++- include/boost/json/impl/iterator.ipp | 8 +- include/boost/json/impl/parser.ipp | 20 +- include/boost/json/impl/value.hpp | 12 +- test/CMakeLists.txt | 1 + test/_detail_stack.cpp | 4 +- test/basic_parser.cpp | 125 +++- test/error.cpp | 38 +- test/make-pvs.py | 42 +- test/parse-vectors.hpp | Bin 475103 -> 124075 bytes 19 files changed, 969 insertions(+), 358 deletions(-) create mode 100644 include/boost/json/detail/buffer.hpp diff --git a/.travis.yml b/.travis.yml index c4ed79c0..60c8faf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -118,6 +118,7 @@ jobs: env: - COMMENT=codecov.io - B2_CXXSTD=11 + - B2_CXXFLAGS="cxxflags=-fno-inline" - B2_TOOLSET=gcc-8 - B2_DEFINES="define=BOOST_DISABLE_ASSERTS define=BOOST_NO_STRESS_TEST=1" addons: *gcc-8 diff --git a/include/boost/json/assign_string.hpp b/include/boost/json/assign_string.hpp index 021da7c5..d3058fca 100644 --- a/include/boost/json/assign_string.hpp +++ b/include/boost/json/assign_string.hpp @@ -29,8 +29,8 @@ from_json( value const& v) { if(! v.is_string()) - throw system_error( - error::expected_string); + BOOST_THROW_EXCEPTION( + system_error(error::not_string)); auto& s= v.as_string(); t.assign(s.data(), s.size()); } diff --git a/include/boost/json/assign_vector.hpp b/include/boost/json/assign_vector.hpp index 788a8095..8d0ad4f7 100644 --- a/include/boost/json/assign_vector.hpp +++ b/include/boost/json/assign_vector.hpp @@ -31,8 +31,8 @@ from_json( value const& v) { if(! v.is_array()) - throw system_error( - error::expected_array); + BOOST_THROW_EXCEPTION( + system_error(error::not_array)); auto& arr = v.as_array(); t.resize(0); t.resize(arr.size()); diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp index bd6fce15..92adae0e 100644 --- a/include/boost/json/basic_parser.hpp +++ b/include/boost/json/basic_parser.hpp @@ -40,6 +40,8 @@ class basic_parser state, stack_capacity> stack_; number::mantissa_type n_mant_; number::exponent_type n_exp_; + long u0_; + unsigned short u_; bool n_neg_; bool n_exp_neg_; bool is_key_; diff --git a/include/boost/json/detail/basic_parser.hpp b/include/boost/json/detail/basic_parser.hpp index 537f5568..af9f01e5 100644 --- a/include/boost/json/detail/basic_parser.hpp +++ b/include/boost/json/detail/basic_parser.hpp @@ -38,6 +38,56 @@ struct parser_base { return static_cast(c) < 32; } + + static + short + hex_digit(char c) noexcept + { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return -1; + } + + static + int + utf8_encode( + char* dest, + unsigned long cp) + { + if(cp < 0x80) + { + dest[0] = static_cast(cp); + return 1; + } + + if(cp < 0x800) + { + dest[0] = static_cast( (cp >> 6) | 0xc0); + dest[1] = static_cast( (cp & 0x3f) | 0x80); + return 2; + } + + if(cp < 0x10000) + { + dest[0] = static_cast( (cp >> 12) | 0xe0); + dest[1] = static_cast(((cp >> 6) & 0x3f) | 0x80); + dest[2] = static_cast( (cp & 0x3f) | 0x80); + return 3; + } + + { + dest[0] = static_cast( (cp >> 18) | 0xf0); + dest[1] = static_cast(((cp >> 12) & 0x3f) | 0x80); + dest[2] = static_cast(((cp >> 6) & 0x3f) | 0x80); + dest[3] = static_cast( (cp & 0x3f) | 0x80); + return 4; + } + } + }; } // detail diff --git a/include/boost/json/detail/buffer.hpp b/include/boost/json/detail/buffer.hpp new file mode 100644 index 00000000..89f029e2 --- /dev/null +++ b/include/boost/json/detail/buffer.hpp @@ -0,0 +1,116 @@ +// +// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/vinniefalco/json +// + +#ifndef BOOST_JSON_DETAIL_BUFFER_HPP +#define BOOST_JSON_DETAIL_BUFFER_HPP + +#include +#include + +namespace boost { +namespace json { +namespace detail { + +// A simple string-like temporary static buffer +template +class buffer +{ + char buf_[N]; + unsigned int size_ = 0; + +public: + using size_type = unsigned int; + + buffer() = default; + + string_view + get() const noexcept + { + return {buf_, size_}; + } + + operator string_view() const noexcept + { + return get(); + } + + char const* + data() const noexcept + { + return buf_; + } + + size_type + size() const noexcept + { + return size_; + } + + size_type + max_size() const noexcept + { + return N; + } + + void + clear() noexcept + { + size_ = 0; + } + + void + push_back(char ch) noexcept + { + buf_[size_++] = ch; + } + + // append valid 32-bit code point as utf8 + void + append_utf8( + unsigned long cp) noexcept + { + auto dest = buf_ + size_; + if(cp < 0x80) + { + dest[0] = static_cast(cp); + size_ += 1; + return; + } + + if(cp < 0x800) + { + dest[0] = static_cast( (cp >> 6) | 0xc0); + dest[1] = static_cast( (cp & 0x3f) | 0x80); + size_ += 2; + return; + } + + if(cp < 0x10000) + { + dest[0] = static_cast( (cp >> 12) | 0xe0); + dest[1] = static_cast(((cp >> 6) & 0x3f) | 0x80); + dest[2] = static_cast( (cp & 0x3f) | 0x80); + size_ += 3; + } + + { + dest[0] = static_cast( (cp >> 18) | 0xf0); + dest[1] = static_cast(((cp >> 12) & 0x3f) | 0x80); + dest[2] = static_cast(((cp >> 6) & 0x3f) | 0x80); + dest[3] = static_cast( (cp & 0x3f) | 0x80); + size_ += 4; + } + } +}; + +} // detail +} // json +} // boost + +#endif diff --git a/include/boost/json/detail/stack.hpp b/include/boost/json/detail/stack.hpp index 9825c5ad..c809466a 100644 --- a/include/boost/json/detail/stack.hpp +++ b/include/boost/json/detail/stack.hpp @@ -147,7 +147,7 @@ public: } void - push_front(T const& t) + push(T const& t) { if(n_ < N) ::new(&base()[n_]) T(t); @@ -170,7 +170,7 @@ public: } void - pop_front() + pop() { BOOST_ASSERT(n_ > 0); if(! v_.empty()) diff --git a/include/boost/json/error.hpp b/include/boost/json/error.hpp index 8f2544af..4d97549f 100644 --- a/include/boost/json/error.hpp +++ b/include/boost/json/error.hpp @@ -32,44 +32,101 @@ using error_condition = boost::system::error_condition; /// Error codes returned by JSON operations enum class error { - /// The serialized JSON object contains a syntax error + /// syntax error syntax = 1, - /// Unexpected extra data encountered while parsing + /// extra data extra_data, - /// A mantissa overflowed while parsing + /// mantissa overflow mantissa_overflow, - /// The parser encountered an exponent that overflowed + /// exponent too large exponent_overflow, - /// The parser's maximum depth limit was reached + /// too deep too_deep, - /// Expected a value of kind object - expected_object, + /// illegal character for value + illegal_char, - /// Expected a value of kind array - expected_array, + /// illegal control character + illegal_control_char, + + /// illegal character in escape sequence + illegal_escape_char, - /// Expected a value of kind string - expected_string, + /// illegal extra digits in number + illegal_extra_digits, - /// Expect a value of kind number - expected_number, + /// illegal extra characters + illegal_extra_chars, - /// Expected a value of kind boolean - expected_bool, + /// illegal leading surrogate + illegal_leading_surrogate, - /// Expected a value of kind boolean + /// illegal trailing surrogate + illegal_trailing_surrogate, + + /// expected comma + expected_comma, + + /// expected colon + expected_colon, + + /// expected quotes + expected_quotes, + + /// expected hex digit + expected_hex_digit, + + /// expected utf16 escape + expected_utf16_escape, + + /// expected mantissa + expected_mantissa, + + /// expected fractional part of mantissa + expected_fraction, + + /// expected exponent here + expected_exponent, + + /// expected 'true' + expected_true, + + /// expected 'false' + expected_false, + + /// expected 'null' expected_null, - /// An integer assignment would overflow + /// not an object + not_object, + + /// not an array + not_array, + + /// not a string + not_string, + + /// not a number + not_number, + + /// not a boolean + not_bool, + + /// not a null + not_null, + + /// integer overflow integer_overflow, - /// The key was not found in the object - key_not_found + /// key not found + key_not_found, + + /// test failure + test_failure }; /// Error conditions corresponding to JSON errors diff --git a/include/boost/json/impl/basic_parser.ipp b/include/boost/json/impl/basic_parser.ipp index 86025726..77dedf73 100644 --- a/include/boost/json/impl/basic_parser.ipp +++ b/include/boost/json/impl/basic_parser.ipp @@ -12,6 +12,7 @@ #include #include +#include #include namespace boost { @@ -57,18 +58,21 @@ namespace json { enum class basic_parser::state : char { json, - element, ws, value, - object1, object2, object3, object4, colon, - array1, array2, array3, array4, + object1, object2, colon, + array1, array2, string1, string2, string3, string4, - true1, true2, true3, true4, - false1, false2, false3, false4, false5, - null1, null2, null3, null4, + true1, true2, true3, + false1, false2, false3, false4, + null1, null2, null3, - number, number_mant1, number_mant2, + u_esc1, u_esc2, u_esc3, u_esc4, + u_pair1, u_pair2, + u_surr, + + number_mant1, number_mant2, number_fract1, number_fract2, number_fract3, number_exp, number_exp_sign, number_exp_digits1, number_exp_digits2, number_end, @@ -94,11 +98,14 @@ basic_parser:: reset() { stack_.clear(); - stack_.push_front(state::end); - stack_.push_front(state::json); + stack_.push(state::end); + stack_.push(state::json); + is_key_ = false; + n_neg_ = false; + u0_ = -1; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------- // Append the digit to the // value, which must be unsigned. @@ -148,55 +155,7 @@ append_digit( return true; } -//------------------------------------------------------------------------------ - -void -basic_parser:: -write_eof(error_code& ec) -{ - // write a null, this is invalid no matter - // what state we are in, to get a descriptive - // error. - // - // VFALCO we might want to return error::partial_data - - auto const fail = - [this, &ec] - { - char c = 0; - write_some(&c, 1, ec); - BOOST_ASSERT(ec); - }; - - while(stack_.front() != state::end) - { - // pop all states that - // allow "" (empty string) - switch(stack_.front()) - { - case state::number_mant2: - case state::number_fract1: - case state::number_fract3: - case state::number_exp: - case state::number_exp_digits2: - stack_.front() = state::number_end; - write_some(nullptr, 0, ec); - if(ec) - return; - break; - - case state::ws: - stack_.pop_front(); - break; - - default: - return fail(); - } - } - ec = {}; -} - -//------------------------------------------------------------------------------ +//---------------------------------------------------------- std::size_t basic_parser:: @@ -209,21 +168,19 @@ write_some( auto n = size; auto const p0 = p; auto const p1 = p0 + n; - std::string temp; // VFALCO string bad! - temp.reserve(4096); + detail::buffer<2048> temp; ec = {}; BOOST_ASSERT(stack_.front() != state::end); auto const maybe_flush = [&] { - if(temp.size() != temp.max_size()) + // need 4 chars for largest utf8 code point + if(temp.size() < temp.max_size() - 4) return; if(is_key_) - this->on_key_data( - {temp.data(), temp.size()}, ec); + this->on_key_data(temp, ec); else - this->on_string_data( - {temp.data(), temp.size()}, ec); + this->on_string_data(temp, ec); temp.clear(); }; loop: @@ -233,23 +190,19 @@ loop: this->on_document_begin(ec); if(ec) goto finish; - stack_.front() = state::element; - temp.clear(); - is_key_ = false; - goto loop; - - case state::element: - stack_.front() = state::ws; - stack_.push_front(state::value); - stack_.push_front(state::ws); - goto loop; + stack_.pop(); + stack_.push(state::ws); + stack_.push(state::value); + stack_.push(state::ws); + BOOST_FALLTHROUGH; case state::ws: +loop_ws: while(p < p1) { if(! is_ws(*p)) { - stack_.pop_front(); + stack_.pop(); goto loop; } ++p; @@ -258,51 +211,66 @@ loop: case state::value: { - if(p >= p1) - break; + BOOST_ASSERT(p < p1); switch(*p) { // object case '{': + ++p; stack_.front() = state::object1; - goto loop; + stack_.push(state::ws); + this->on_object_begin(ec); + if(ec) + goto finish; + goto loop_ws; // array case '[': ++p; stack_.front() = state::array1; + stack_.push(state::ws); this->on_array_begin(ec); - goto loop; + if(ec) + goto finish; + goto loop_ws; // string case '"': stack_.front() = state::string1; - goto loop; + goto loop_string; // number + case '-': + ++p; + n_neg_ = true; + BOOST_FALLTHROUGH; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - case '-': - stack_.front() = state::number; - goto loop; + n_mant_ = 0; + n_exp_ = 0; + stack_.front() = state::number_mant1; + goto loop_number; // true case 't': if(p + 4 <= p1) { if( - p[1] != 'r' || - p[2] != 'u' || - p[3] != 'e') + p[1] == 'r' && + p[2] == 'u' && + p[3] == 'e') { - ec = error::syntax; - goto finish; + p = p + 4; + this->on_bool(true, ec); + if(ec) + goto finish; + stack_.pop(); + goto loop; } - p = p + 4; - stack_.front() = state::true4; - goto loop; + ec = error::expected_true; + goto finish; } ++p; stack_.front() = state::true1; @@ -313,17 +281,20 @@ loop: if(p + 5 <= p1) { if( - p[1] != 'a' || - p[2] != 'l' || - p[3] != 's' || - p[4] != 'e') + p[1] == 'a' && + p[2] == 'l' && + p[3] == 's' && + p[4] == 'e') { - ec = error::syntax; - goto finish; + p = p + 5; + this->on_bool(false, ec); + if(ec) + goto finish; + stack_.pop(); + goto loop; } - p = p + 5; - stack_.front() = state::false5; - goto loop; + ec = error::expected_false; + goto finish; } ++p; stack_.front() = state::false1; @@ -334,113 +305,118 @@ loop: if(p + 4 <= p1) { if( - p[1] != 'u' || - p[2] != 'l' || - p[3] != 'l') + p[1] == 'u' && + p[2] == 'l' && + p[3] == 'l') { - ec = error::syntax; - goto finish; + p = p + 4; + this->on_null(ec); + if(ec) + goto finish; + stack_.pop(); + goto loop; } - p = p + 4; - stack_.front() = state::null4; - goto loop; + ec = error::expected_null; + goto finish; } ++p; stack_.front() = state::null1; goto loop; default: - ec = error::syntax; + ec = error::illegal_char; goto finish; } break; } - //-------------------------------------------------------------------------- + //------------------------------------------------------ // // object // - // beginning of object - case state::object1: - BOOST_ASSERT(*p == '{'); - ++p; - this->on_object_begin(ec); - if(ec) - goto finish; - stack_.front() = state::object2; - stack_.push_front(state::ws); - goto loop; - // first key or end of object - case state::object2: - if(p >= p1) - break; + case state::object1: + BOOST_ASSERT(p < p1); if(*p == '}') { ++p; - stack_.front() = state::object4; + this->on_object_end(ec); + if(ec) + goto finish; + stack_.pop(); goto loop; } - stack_.front() = state::object3; - stack_.push_front(state::element); - stack_.push_front(state::colon); - stack_.push_front(state::ws); - stack_.push_front(state::string1); + stack_.pop(); + stack_.push(state::object2); + stack_.push(state::ws); + stack_.push(state::value); + stack_.push(state::ws); + stack_.push(state::colon); + stack_.push(state::ws); + stack_.push(state::string1); is_key_ = true; goto loop; - case state::object3: - if(p >= p1) - break; + case state::object2: + BOOST_ASSERT(p < p1); if(*p == '}') { ++p; - stack_.front() = state::object4; + this->on_object_end(ec); + if(ec) + goto finish; + stack_.pop(); goto loop; } if(*p != ',') { - ec = error::syntax; + ec = error::expected_comma; goto finish; } ++p; - stack_.front() = state::object3; - stack_.push_front(state::element); - stack_.push_front(state::colon); - stack_.push_front(state::ws); - stack_.push_front(state::string1); - stack_.push_front(state::ws); + stack_.push(state::ws); + stack_.push(state::value); + stack_.push(state::ws); + stack_.push(state::colon); + stack_.push(state::ws); + stack_.push(state::string1); + stack_.push(state::ws); is_key_ = true; - goto loop; - - case state::object4: - this->on_object_end(ec); - if(ec) - goto finish; - stack_.pop_front(); - goto loop; + goto loop_ws; case state::colon: - if(p >= p1) - break; + BOOST_ASSERT(p < p1); if(*p != ':') { - ec = error::syntax; + ec = error::expected_colon; goto finish; } ++p; - stack_.pop_front(); + stack_.pop(); goto loop; - //-------------------------------------------------------------------------- + //------------------------------------------------------ // // array // case state::array1: - stack_.front() = state::array2; - stack_.push_front(state::ws); + BOOST_ASSERT(p < p1); + if(*p == ']') + { + ++p; + this->on_array_end(ec); + if(ec) + goto finish; + stack_.pop(); + goto loop; + } + stack_.pop(); + stack_.push(state::array2); + stack_.push(state::ws); + stack_.push(state::value); + stack_.push(state::ws); goto loop; case state::array2: @@ -449,63 +425,49 @@ loop: if(*p == ']') { ++p; - stack_.front() = state::array4; - goto loop; - } - stack_.front() = state::array3; - stack_.push_front(state::element); - goto loop; - - case state::array3: - if(p >= p1) - break; - if(*p == ']') - { - ++p; - stack_.front() = state::array4; + this->on_array_end(ec); + if(ec) + goto finish; + stack_.pop(); goto loop; } if(*p != ',') { - ec = error::syntax; + ec = error::expected_comma; goto finish; } ++p; - stack_.front() = state::array3; - stack_.push_front(state::element); - stack_.push_front(state::ws); + stack_.push(state::ws); + stack_.push(state::value); + stack_.push(state::ws); goto loop; - case state::array4: - this->on_array_end(ec); - if(ec) - goto finish; - stack_.pop_front(); - goto loop; - - //-------------------------------------------------------------------------- + //------------------------------------------------------ // // string // // double quote opening string case state::string1: +loop_string: if(p >= p1) break; if(*p != '\"') { - ec = error::syntax; + ec = error::expected_quotes; goto finish; } ++p; - stack_.front() = state::string2; + stack_.pop(); + stack_.push(state::string2); BOOST_FALLTHROUGH; - //goto loop; // characters // No copies here case state::string2: { + if(p >= p1) + break; auto const start = p; while(p < p1) { @@ -523,33 +485,37 @@ loop: if(ec) goto finish; is_key_ = false; - stack_.pop_front(); + stack_.pop(); goto loop; } if(*p == '\\') { - if(is_key_) - this->on_key_data({start, - static_cast( - p - start)}, ec); - else - this->on_string_data({start, - static_cast( - p - start)}, ec); - ++p; + if(p > start) + { + if(is_key_) + this->on_key_data({start, + static_cast( + p - start)}, ec); + else + this->on_string_data({start, + static_cast( + p - start)}, ec); + } if(ec) goto finish; + ++p; stack_.front() = state::string4; goto loop; } if(is_control(*p)) { - ec = error::syntax; + ec = error::illegal_control_char; goto finish; } // TODO UTF-8? ++p; } + BOOST_ASSERT(p != start); if(is_key_) this->on_key_data({start, static_cast( @@ -572,17 +538,15 @@ loop: if(*p == '\"') { if(is_key_) - this->on_key_end({temp.data(), - temp.size()}, ec); + this->on_key_end(temp, ec); else - this->on_string_end({temp.data(), - temp.size()}, ec); + this->on_string_end(temp, ec); ++p; if(ec) goto finish; temp.clear(); is_key_ = false; - stack_.pop_front(); + stack_.pop(); goto loop; } if(*p == '\\') @@ -593,10 +557,9 @@ loop: } if(is_control(*p)) { - ec = error::syntax; + ec = error::illegal_control_char; goto finish; } - // TODO UTF-8 maybe_flush(); temp.push_back(*p++); } @@ -650,18 +613,176 @@ loop: break; case 'u': - BOOST_ASSERT(false); - break; + ++p; + stack_.front() = state::string3; + stack_.push(state::u_esc1); + goto loop; default: - ec = error::syntax; + ec = error::illegal_escape_char; goto finish; } ++p; stack_.front() = state::string3; goto loop; - //-------------------------------------------------------------------------- + // utf16 escape, got "\u" already + case state::u_esc1: + { + if(p >= p1) + break; + auto d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ = d << 12; + if(p + 3 <= p1) + { + // fast path + d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d << 8; + d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d << 4; + d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d; + stack_.front() = state::u_surr; + goto loop; + } + stack_.front() = state::u_esc2; + goto loop; + } + + case state::u_esc2: + { + if(p >= p1) + break; + auto d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d << 8; + stack_.front() = state::u_esc3; + goto loop; + } + + case state::u_esc3: + { + if(p >= p1) + break; + auto d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d << 4; + stack_.front() = state::u_esc4; + goto loop; + } + + case state::u_esc4: + { + if(p >= p1) + break; + auto d = hex_digit(*p++); + if(d == -1) + { + ec = error::expected_hex_digit; + goto finish; + } + u_ += d; + stack_.front() = state::u_surr; + goto loop; + } + + // handles 1 or 2 surrogates + case state::u_surr: + { + // one code unit + if(u0_ == -1) + { + if( u_ >= 0xd800) + { + if(u_ <= 0xdbff) + { + // need 2nd surrogate + u0_ = u_; + stack_.front() = state::u_pair1; + goto loop; + } + else if(u_ <= 0xdfff) + { + ec = error::illegal_leading_surrogate; + goto finish; + } + } + + maybe_flush(); + temp.append_utf8(u_); + stack_.pop(); + goto loop; + } + // both code units + if( u_ < 0xdc00 || + u_ > 0xdfff) + { + ec = error::illegal_trailing_surrogate; + goto finish; + } + unsigned long cp = + ((u0_ - 0xd800) << 10) + + (u_ - 0xdc00); + temp.append_utf8(cp); + u0_ = -1; + stack_.pop(); + goto loop; + } + + // second utf16 surrogate + case state::u_pair1: + if(p >= p1) + break; + if(*p != '\\') + { + ec = error::expected_utf16_escape; + goto finish; + } + ++p; + stack_.front() = state::u_pair2; + goto loop; + + case state::u_pair2: + if(p >= p1) + break; + if(*p != 'u') + { + ec = error::expected_utf16_escape; + goto finish; + } + ++p; + stack_.front() = state::u_esc1; + goto loop; + + //------------------------------------------------------ // // true @@ -672,7 +793,7 @@ loop: break; if(*p != 'r') { - ec = error::syntax; + ec = error::expected_true; goto finish; } ++p; @@ -684,7 +805,7 @@ loop: break; if(*p != 'u') { - ec = error::syntax; + ec = error::expected_true; goto finish; } ++p; @@ -696,18 +817,14 @@ loop: break; if(*p != 'e') { - ec = error::syntax; + ec = error::expected_true; goto finish; } ++p; - stack_.front() = state::true4; - BOOST_FALLTHROUGH; - - case state::true4: this->on_bool(true, ec); if(ec) goto finish; - stack_.pop_front(); + stack_.pop(); goto loop; // @@ -719,7 +836,7 @@ loop: break; if(*p != 'a') { - ec = error::syntax; + ec = error::expected_false; goto finish; } ++p; @@ -731,7 +848,7 @@ loop: break; if(*p != 'l') { - ec = error::syntax; + ec = error::expected_false; goto finish; } ++p; @@ -743,7 +860,7 @@ loop: break; if(*p != 's') { - ec = error::syntax; + ec = error::expected_false; goto finish; } ++p; @@ -755,18 +872,14 @@ loop: break; if(*p != 'e') { - ec = error::syntax; + ec = error::expected_false; goto finish; } ++p; - stack_.front() = state::false5; - BOOST_FALLTHROUGH; - - case state::false5: this->on_bool(false, ec); if(ec) goto finish; - stack_.pop_front(); + stack_.pop(); goto loop; // @@ -778,7 +891,7 @@ loop: break; if(*p != 'u') { - ec = error::syntax; + ec = error::expected_null; goto finish; } ++p; @@ -790,7 +903,7 @@ loop: break; if(*p != 'l') { - ec = error::syntax; + ec = error::expected_null; goto finish; } ++p; @@ -802,47 +915,28 @@ loop: break; if(*p != 'l') { - ec = error::syntax; + ec = error::expected_null; goto finish; } ++p; - stack_.front() = state::null4; - BOOST_FALLTHROUGH; - - case state::null4: this->on_null(ec); if(ec) goto finish; - stack_.pop_front(); + stack_.pop(); goto loop; // // number // - case state::number: - BOOST_ASSERT(p < p1); - n_mant_ = 0; - n_exp_ = 0; - if(*p == '-') - { - ++p; - n_neg_ = true; - } - else - { - n_neg_ = false; - } - stack_.front() = state::number_mant1; - goto loop; - case state::number_mant1: +loop_number: if(p >= p1) break; if(! is_digit(*p)) { // expected mantissa digit - ec = error::syntax; + ec = error::expected_mantissa; goto finish; } if(*p != '0') @@ -882,7 +976,7 @@ loop: if(is_digit(*p)) { // unexpected digit after zero - ec = error::syntax; + ec = error::illegal_extra_digits; goto finish; } stack_.front() = state::number_exp; @@ -894,7 +988,7 @@ loop: if(! is_digit(*p)) { // expected mantissa fraction digit - ec = error::syntax; + ec = error::expected_fraction; goto finish; } stack_.front() = state::number_fract3; @@ -955,7 +1049,7 @@ loop: if(! is_digit(*p)) { // expected exponent digit - ec = error::syntax; + ec = error::expected_exponent; goto finish; } stack_.front() = state::number_exp_digits2; @@ -979,11 +1073,12 @@ loop: break; case state::number_end: + n_neg_ = false; this->on_number(number( n_mant_, n_exp_, n_neg_), ec); if(ec) goto finish; - stack_.pop_front(); + stack_.pop(); goto loop; // @@ -991,14 +1086,12 @@ loop: // case state::end: - /* if(p < p1) { // unexpected extra characters - ec = error::syntax; + ec = error::illegal_extra_chars; goto finish; } - */ break; } @@ -1006,7 +1099,7 @@ finish: return p - p0; } -//------------------------------------------------------------------------------ +//---------------------------------------------------------- // Called to parse the rest of the document, this // can be optimized by assuming no more data is coming. @@ -1020,10 +1113,67 @@ write( auto bytes_used = write_some(data, size, ec); if(! ec) + { write_eof(ec); + if(! ec) + { + if( bytes_used < size || + ec == error::illegal_char) + ec = error::illegal_extra_chars; + } + } return bytes_used; } +//---------------------------------------------------------- + +void +basic_parser:: +write_eof(error_code& ec) +{ + // write a null, this is invalid no matter + // what state we are in, to get a descriptive + // error. + // + // VFALCO we might want to return error::partial_data + + auto const fail = + [this, &ec] + { + char c = 0; + write_some(&c, 1, ec); + BOOST_ASSERT(ec); + }; + + while(stack_.front() != state::end) + { + // pop all states that + // allow "" (empty string) + switch(stack_.front()) + { + case state::number_mant2: + case state::number_fract1: + case state::number_fract3: + case state::number_exp: + case state::number_exp_digits2: + stack_.front() = state::number_end; + write_some(nullptr, 0, ec); + if(ec) + return; + break; + + case state::ws: + stack_.pop(); + break; + + default: + return fail(); + } + } + ec = {}; +} + + } // json } // boost diff --git a/include/boost/json/impl/error.ipp b/include/boost/json/impl/error.ipp index 8c68efb8..002cabd7 100644 --- a/include/boost/json/impl/error.ipp +++ b/include/boost/json/impl/error.ipp @@ -33,47 +33,110 @@ make_error_code(error e) { default: case error::syntax: return - "The serialized JSON object contains a syntax error"; + "syntax error"; case error::extra_data: return - "Unexpected extra data encountered while parsing"; + "extra data"; case error::mantissa_overflow: return - "A mantissa overflowed while parsing"; + "mantissa overflow"; case error::exponent_overflow: return - "An exponent overflowed while parsing"; + "exponent overflow"; case error::too_deep: return - "The parser reached the maximum allowed depth"; + "too deep"; // - case error::integer_overflow: return - "An integer assignment overflowed"; + case error::illegal_char: return + "illegal character for value"; - case error::expected_object: return - "Expected a value of kind object"; + case error::illegal_control_char: return + "illegal control character"; - case error::expected_array: return - "Expected a value of kind array"; + case error::illegal_escape_char: return + "illegal character in escape sequence"; - case error::expected_string: return - "Expected a value of kind string"; + case error::illegal_extra_digits: return + "illegal extra digits in number"; - case error::expected_number: return - "Expected a value of kind number"; + case error::illegal_extra_chars: return + "illegal extra characters"; - case error::expected_bool: return - "Expected a value of kind bool"; + case error::illegal_leading_surrogate: return + "illegal leading surrogate"; + + case error::illegal_trailing_surrogate: return + "illegal trailing surrogate"; + + // + + case error::expected_comma: return + "expected comma"; + + case error::expected_colon: return + "expected colon"; + + case error::expected_quotes: return + "expected quotes"; + + case error::expected_hex_digit: return + "expected hex digit"; + + case error::expected_utf16_escape: return + "expected utf16 escape"; + + case error::expected_mantissa: return + "expected mantissa"; + + case error::expected_fraction: return + "expected mantissa fraction"; + + case error::expected_exponent: return + "expected exponent"; + + case error::expected_true: return + "expected 'true'"; + + case error::expected_false: return + "expected 'false'"; case error::expected_null: return - "Expected a value of kind null"; + "expected 'null'"; + + // + + case error::not_object: return + "not an object"; + + case error::not_array: return + "not an array"; + + case error::not_string: return + "not a string"; + + case error::not_number: return + "not a number"; + + case error::not_bool: return + "not a boolean"; + + case error::not_null: return + "not a null"; + + case error::integer_overflow: return + "integer overflowed"; // case error::key_not_found: return - "The key was not found in the object"; + "key not found"; + + // + + case error::test_failure: return + "test failure"; } } @@ -91,15 +154,35 @@ make_error_code(error e) case error::mantissa_overflow: case error::exponent_overflow: case error::too_deep: + + case error::illegal_char: + case error::illegal_control_char: + case error::illegal_escape_char: + case error::illegal_extra_digits: + case error::illegal_extra_chars: + case error::illegal_leading_surrogate: + case error::illegal_trailing_surrogate: + + case error::expected_comma: + case error::expected_colon: + case error::expected_quotes: + case error::expected_hex_digit: + case error::expected_utf16_escape: + case error::expected_mantissa: + case error::expected_fraction: + case error::expected_exponent: + case error::expected_true: + case error::expected_false: + case error::expected_null: return condition::parse_error; + case error::not_object: + case error::not_array: + case error::not_string: + case error::not_number: + case error::not_bool: + case error::not_null: case error::integer_overflow: - case error::expected_object: - case error::expected_array: - case error::expected_string: - case error::expected_number: - case error::expected_bool: - case error::expected_null: return condition::assign_error; } } diff --git a/include/boost/json/impl/iterator.ipp b/include/boost/json/impl/iterator.ipp index fb2e46e0..03876c9a 100644 --- a/include/boost/json/impl/iterator.ipp +++ b/include/boost/json/impl/iterator.ipp @@ -161,13 +161,13 @@ operator++() noexcept { if(n.v->is_structured()) { - stack_.pop_front(); + stack_.pop(); stack_.emplace_front( *n.v, true); } else { - stack_.pop_front(); + stack_.pop(); } } else if(n.v->is_object()) @@ -175,7 +175,7 @@ operator++() noexcept if(n.obj_it == n.v->as_object().end()) { - stack_.pop_front(); + stack_.pop(); } else { @@ -192,7 +192,7 @@ operator++() noexcept if(n.arr_it == n.v->as_array().end()) { - stack_.pop_front(); + stack_.pop(); } else { diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index 0df26fab..7ba6e647 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -30,7 +30,7 @@ assign(T&& t) { BOOST_ASSERT(jv.is_null()); jv = std::forward(t); - stack_.pop_front(); + stack_.pop(); } else if(stack_.front()->is_array()) { @@ -94,7 +94,7 @@ parser:: on_document_begin(error_code&) { stack_.clear(); - stack_.push_front(&jv_); + stack_.push(&jv_); s_.clear(); obj_ = false; } @@ -119,7 +119,7 @@ on_object_begin(error_code& ec) { jv.as_array().emplace_back( kind::object); - stack_.push_front( + stack_.push( &jv.as_array().back()); } else @@ -136,7 +136,7 @@ on_object_end(error_code&) { BOOST_ASSERT( stack_.front()->is_object()); - stack_.pop_front(); + stack_.pop(); if(! stack_.empty()) { auto const& jv = stack_.front(); @@ -167,7 +167,7 @@ on_array_begin(error_code& ec) BOOST_ASSERT(s_.empty()); jv.as_array().emplace_back( kind::array); - stack_.push_front( + stack_.push( &jv.as_array().back()); } else @@ -184,7 +184,7 @@ on_array_end(error_code&) { BOOST_ASSERT( stack_.front()->is_array()); - stack_.pop_front(); + stack_.pop(); if(! stack_.empty()) { auto const& jv = stack_.front(); @@ -220,7 +220,7 @@ on_key_end( // overwrite duplicate keys if(! result.second) result.first->second.emplace_null(); - stack_.push_front(&result.first->second); + stack_.push(&result.first->second); s_.clear(); } @@ -243,7 +243,7 @@ on_string_data( { BOOST_ASSERT(s_.empty()); jv.as_array().emplace_back(kind::string); - stack_.push_front( + stack_.push( &jv.as_array().back()); stack_.front()->as_string().append( s.data(), s.size()); @@ -270,7 +270,7 @@ on_string_end( { on_string_data(s, ec); BOOST_ASSERT(stack_.front()->is_string()); - stack_.pop_front(); + stack_.pop(); if(! stack_.empty()) { auto const& jv = stack_.front(); @@ -290,7 +290,7 @@ on_number(number n, error_code&) { BOOST_ASSERT(jv.is_null()); jv.emplace_number() = std::move(n); - stack_.pop_front(); + stack_.pop(); } else if(stack_.front()->is_array()) { diff --git a/include/boost/json/impl/value.hpp b/include/boost/json/impl/value.hpp index 54be9a9c..f2438d68 100644 --- a/include/boost/json/impl/value.hpp +++ b/include/boost/json/impl/value.hpp @@ -578,22 +578,22 @@ from_json(T& t, value const& v) auto const rhs = v.get_int64(); if( rhs > (std::numeric_limits::max)() || rhs < (std::numeric_limits::min)()) - throw system_error( - error::integer_overflow); + BOOST_THROW_EXCEPTION(system_error( + error::integer_overflow)); t = static_cast(rhs); } else if(v.is_uint64()) { auto const rhs = v.get_uint64(); if(rhs > (std::numeric_limits::max)()) - throw system_error( - error::integer_overflow); + BOOST_THROW_EXCEPTION(system_error( + error::integer_overflow)); t = static_cast(rhs); } else { - throw system_error( - error::expected_number); + BOOST_THROW_EXCEPTION( + system_error(error::not_number)); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e6cc1c0a..9c3d1c95 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable (json-tests ${PROJECT_SOURCE_DIR}/src/src.cpp Jamfile test_storage.hpp + parse-vectors.hpp main.cpp _detail_stack.cpp array.cpp diff --git a/test/_detail_stack.cpp b/test/_detail_stack.cpp index b328efc2..1f10a4f0 100644 --- a/test/_detail_stack.cpp +++ b/test/_detail_stack.cpp @@ -33,10 +33,10 @@ public: s.emplace_front("2"); BEAST_EXPECT(s.size() == 2); BEAST_EXPECT(s[0] == "2"); - s.pop_front(); + s.pop(); BEAST_EXPECT(s.size() == 1); BEAST_EXPECT(s[0] == "1"); - s.pop_front(); + s.pop(); BEAST_EXPECT(s.empty()); } diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp index cf3cb671..236b27ca 100644 --- a/test/basic_parser.cpp +++ b/test/basic_parser.cpp @@ -20,89 +20,165 @@ namespace json { class basic_parser_test : public beast::unit_test::suite { public: - struct test_parser - : basic_parser + class test_parser + : public basic_parser { - test_parser() = default; + std::size_t n_ = std::size_t(-1); + + void + maybe_fail(error_code& ec) + { + if(n_ && --n_ > 0) + return; + ec = error::test_failure; + } void on_document_begin( - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_object_begin( - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_object_end( - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_array_begin( - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_array_end( - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_key_data( string_view, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_key_end( string_view, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_string_data( string_view, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_string_end( string_view, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_number( number, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void on_bool( bool, - error_code&) override + error_code& ec) override { + maybe_fail(ec); } void - on_null(error_code&) override + on_null(error_code& ec) override + { + maybe_fail(ec); + } + + public: + test_parser() = default; + + test_parser( + std::size_t n) + : n_(n) { } }; + void + parse_grind( + string_view input, + error_code ex) + { + if(input.size() > 100) + return; + for(std::size_t n = 0; + n < input.size() - 1; ++n) + { + error_code ec; + test_parser p; + p.write_some(input.data(), n, ec); + if(! ec) + p.write_some(input.data() + n, + input.size() - n, ec); + if(! ec) + p.write_eof(ec); + if(ec) + { + BEAST_EXPECTS( + ec == ex, std::string(input) + + " : " + ec.message()); + return; + } + } + + std::size_t n = 1; + for(; n < 10000; ++n) + { + error_code ec; + test_parser p{n}; + p.write( + input.data(), + input.size(), + ec); + if(ec != error::test_failure) + { + BEAST_EXPECTS( + ec == ex, std::string(input) + + " : " + ec.message()); + break; + } + } + BEAST_EXPECT(n < 10000); + } + void good(string_view s) { @@ -286,6 +362,17 @@ public: v.text.data(), v.text.size(), ec); + if(v.result == 'i') + { + auto const s = ec ? + "reject" : "accept"; + ++info; + log << + "'" << v.result << "' " << + v.name << " " << s << "\n"; + parse_grind(v.text, ec); + continue; + } char result; result = ec ? 'n' : 'y'; if(result != v.result) @@ -302,6 +389,10 @@ public: else log << "\n"; } + else + { + parse_grind(v.text, ec); + } } if(fail > 0) log << fail << "/" << tot << @@ -315,13 +406,13 @@ public: log << "sizeof(basic_parser) == " << sizeof(basic_parser) << "\n"; + testParseVectors(); + testObject(); testArray(); testString(); testNumber(); testMonostates(); - - testParseVectors(); } }; diff --git a/test/error.cpp b/test/error.cpp index ce7b4f39..71ebeedb 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -49,16 +49,38 @@ public: check(condition::parse_error, error::mantissa_overflow); check(condition::parse_error, error::exponent_overflow); check(condition::parse_error, error::too_deep); - - check(condition::assign_error, error::integer_overflow); - check(condition::assign_error, error::expected_object); - check(condition::assign_error, error::expected_array); - check(condition::assign_error, error::expected_string); - check(condition::assign_error, error::expected_number); - check(condition::assign_error, error::expected_bool); - check(condition::assign_error, error::expected_null); + check(condition::parse_error, error::illegal_char); + check(condition::parse_error, error::illegal_control_char); + check(condition::parse_error, error::illegal_escape_char); + check(condition::parse_error, error::illegal_extra_digits); + check(condition::parse_error, error::illegal_extra_chars); + check(condition::parse_error, error::illegal_leading_surrogate); + check(condition::parse_error, error::illegal_trailing_surrogate); + + check(condition::parse_error, error::expected_comma); + check(condition::parse_error, error::expected_colon); + check(condition::parse_error, error::expected_quotes); + check(condition::parse_error, error::expected_hex_digit); + check(condition::parse_error, error::expected_utf16_escape); + check(condition::parse_error, error::expected_mantissa); + check(condition::parse_error, error::expected_fraction); + check(condition::parse_error, error::expected_exponent); + check(condition::parse_error, error::expected_true); + check(condition::parse_error, error::expected_false); + check(condition::parse_error, error::expected_null); + + check(condition::assign_error, error::not_object); + check(condition::assign_error, error::not_array); + check(condition::assign_error, error::not_string); + check(condition::assign_error, error::not_number); + check(condition::assign_error, error::not_bool); + check(condition::assign_error, error::not_null); + check(condition::assign_error, error::integer_overflow); + check(error::key_not_found); + + check(error::test_failure); } }; diff --git a/test/make-pvs.py b/test/make-pvs.py index 9b9c79f8..e4f6967a 100644 --- a/test/make-pvs.py +++ b/test/make-pvs.py @@ -2,14 +2,44 @@ import os +def chex(c): + d1 = ord(c)/16; + d2 = ord(c)%16; + d = "0123456789ABCDEF"; + s = "\\x" + d[d1:d1+1] + d[d2:d2+1]; + return s; + +def escape(c): + if c == ' ' or c == '\t': + return c; + elif c == '\"': + return "\\\""; + elif c == '\\': + return "\\\\"; + n = ord(c); + if n >= 32 and n <= 127: + return c; + return chex(c); + +def tocpp(s): + v0 = "" + v = "\""; + for c in s: + v = v + escape(c); + if len(v) > 80: + if len(v0) > 50000: + return v0 + v + "\""; + v0 += v + "\"\n \""; + v = ""; + return v0 + v + "\""; + def do_files(directory): for root, directories, files in os.walk(directory): for filename in files: filepath = os.path.join(root, filename) with open(filepath, 'r') as file: data = file.read(); - data = data.replace('\"', "\\\""); - print(" { '" + filename[0:1] + "', \"" + filename[2:-5] + "\", R\"json(" + data + ")json\" },"); + print(" { '" + filename[0:1] + "', \"" + filename[2:-5] + "\", lit(" + tocpp(data) + ") },"); print(""" // @@ -58,6 +88,14 @@ struct parse_vectors inline parse_vectors() noexcept; private: + template + static + ::boost::string_view + lit(char const (&s)[N]) + { + return {s, N - 1}; + } + iterator first_; iterator last_; }; diff --git a/test/parse-vectors.hpp b/test/parse-vectors.hpp index 2ab68e709ca4150f4005106c8baa4f100e86a8a3..f070d56dfe2f331d1e234c15601133aebfb755df 100644 GIT binary patch literal 124075 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 literal 475103 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