diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp index 9c8576fd0..dcd6cc77c 100644 --- a/include/boost/json/basic_parser.hpp +++ b/include/boost/json/basic_parser.hpp @@ -283,6 +283,8 @@ class basic_parser val1, val2, val3 }; + using no_state = std::integral_constant< state, state(CHAR_MAX) >; + struct number { uint64_t mant; @@ -292,7 +294,7 @@ class basic_parser bool neg; }; - template< bool StackEmpty_, char First_ > + template< class PrevState, char First_ > struct parse_number_helper; // optimization: must come first @@ -392,42 +394,47 @@ class basic_parser #pragma warning pop #endif - template + template const char* parse_comment(const char* p, - std::integral_constant stack_empty, + PrevState st, /*std::integral_constant*/ bool terminal); - template - const char* parse_document(const char* p, - std::integral_constant stack_empty); + template + const char* parse_document(const char* p, PrevState st); - template - const char* parse_value(const char* p, - std::integral_constant stack_empty, + const char* parse_value( + const char* p, + PrevState st, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8); template - const char* resume_value(const char* p, + const char* resume_value( + const char* p, + state st, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8); - template - const char* parse_object(const char* p, - std::integral_constant stack_empty, + const char* parse_object( + const char* p, + PrevState st, + std::size_t size, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8); - template const char* parse_array(const char* p, - std::integral_constant stack_empty, + PrevState st, + std::size_t size, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8); @@ -436,32 +443,36 @@ class basic_parser const char* parse_literal(const char* p, std::integral_constant literal); - template - const char* parse_string(const char* p, - std::integral_constant stack_empty, + const char* parse_string( + const char* p, + PrevState st, + std::size_t total, std::integral_constant is_key, /*std::integral_constant*/ bool allow_bad_utf8); - template + template const char* parse_number(const char* p, - std::integral_constant stack_empty, + PrevState st, std::integral_constant first, std::integral_constant numbers); - template - const char* parse_unescaped(const char* p, - std::integral_constant stack_empty, + const char* parse_unescaped( + const char* p, + PrevState st, + std::size_t total, std::integral_constant is_key, /*std::integral_constant*/ bool allow_bad_utf8); - template const char* parse_escaped( const char* p, + PrevState st, std::size_t total, - std::integral_constant stack_empty, /*std::integral_constant*/ bool is_key, /*std::integral_constant*/ bool allow_bad_utf8); diff --git a/include/boost/json/basic_parser_impl.hpp b/include/boost/json/basic_parser_impl.hpp index 09968e444..8d5962ff2 100644 --- a/include/boost/json/basic_parser_impl.hpp +++ b/include/boost/json/basic_parser_impl.hpp @@ -200,12 +200,13 @@ enum json_literal //---------------------------------------------------------- template< class Handler > -template< bool StackEmpty_, char First_ > +template< class PrevState, char First_ > struct basic_parser:: parse_number_helper { basic_parser* parser; char const* p; + PrevState st; template< std::size_t N > char const* @@ -213,7 +214,7 @@ parse_number_helper { return parser->parse_number( p, - std::integral_constant(), + st, std::integral_constant(), std::integral_constant< number_precision, static_cast(N)>() ); @@ -425,21 +426,19 @@ suspend( template template< - bool StackEmpty_/*, + class PrevState/* bool Terminal_*/> const char* basic_parser:: parse_comment(const char* p, - std::integral_constant stack_empty, + PrevState st, /*std::integral_constant*/ bool terminal) { detail::const_stream_wrapper cs(p, end_); const char* start = cs.begin(); std::size_t remain; - if(! stack_empty && ! st_.empty()) + if( st != no_state() ) { - state st; - st_.pop(st); switch(st) { default: BOOST_JSON_UNREACHABLE(); @@ -531,29 +530,26 @@ parse_comment(const char* p, } template -template +template const char* basic_parser:: -parse_document(const char* p, - std::integral_constant stack_empty) +parse_document(const char* p, PrevState st) { detail::const_stream_wrapper cs(p, end_); - if(! stack_empty && ! st_.empty()) + if( st != no_state() ) { - state st; - st_.peek(st); switch(st) { - default: goto do_doc2; + default: + goto do_doc2; case state::doc1: - st_.pop(st); - goto do_doc1; + goto do_doc1; case state::doc3: - st_.pop(st); - goto do_doc3; + goto do_doc3; case state::com1: case state::com2: case state::com3: case state::com4: - goto do_doc4; + cs = parse_comment( cs.begin(), st, std::true_type() ); + goto after_doc4; } } do_doc1: @@ -561,42 +557,85 @@ parse_document(const char* p, if(BOOST_JSON_UNLIKELY(! cs)) return maybe_suspend(cs.begin(), state::doc1); do_doc2: - switch(+opt_.allow_comments | - (opt_.allow_trailing_commas << 1) | - (opt_.allow_invalid_utf8 << 2)) + if( st == state::doc1 ) { - // no extensions - default: - cs = parse_value(cs.begin(), stack_empty, std::false_type(), std::false_type(), std::false_type()); - break; - // comments - case 1: - cs = parse_value(cs.begin(), stack_empty, std::true_type(), std::false_type(), std::false_type()); - break; - // trailing - case 2: - cs = parse_value(cs.begin(), stack_empty, std::false_type(), std::true_type(), std::false_type()); - break; - // comments & trailing - case 3: - cs = parse_value(cs.begin(), stack_empty, std::true_type(), std::true_type(), std::false_type()); - break; - // skip validation - case 4: - cs = parse_value(cs.begin(), stack_empty, std::false_type(), std::false_type(), std::true_type()); - break; - // comments & skip validation - case 5: - cs = parse_value(cs.begin(), stack_empty, std::true_type(), std::false_type(), std::true_type()); - break; - // trailing & skip validation - case 6: - cs = parse_value(cs.begin(), stack_empty, std::false_type(), std::true_type(), std::true_type()); - break; - // comments & trailing & skip validation - case 7: - cs = parse_value(cs.begin(), stack_empty, std::true_type(), std::true_type(), std::true_type()); - break; + switch(+opt_.allow_comments | + (opt_.allow_trailing_commas << 1) | + (opt_.allow_invalid_utf8 << 2)) + { + // no extensions + default: + cs = parse_value(cs.begin(), no_state(), std::false_type(), std::false_type(), std::false_type()); + break; + // comments + case 1: + cs = parse_value(cs.begin(), no_state(), std::true_type(), std::false_type(), std::false_type()); + break; + // trailing + case 2: + cs = parse_value(cs.begin(), no_state(), std::false_type(), std::true_type(), std::false_type()); + break; + // comments & trailing + case 3: + cs = parse_value(cs.begin(), no_state(), std::true_type(), std::true_type(), std::false_type()); + break; + // skip validation + case 4: + cs = parse_value(cs.begin(), no_state(), std::false_type(), std::false_type(), std::true_type()); + break; + // comments & skip validation + case 5: + cs = parse_value(cs.begin(), no_state(), std::true_type(), std::false_type(), std::true_type()); + break; + // trailing & skip validation + case 6: + cs = parse_value(cs.begin(), no_state(), std::false_type(), std::true_type(), std::true_type()); + break; + // comments & trailing & skip validation + case 7: + cs = parse_value(cs.begin(), no_state(), std::true_type(), std::true_type(), std::true_type()); + break; + } + } + else + { + switch(+opt_.allow_comments | + (opt_.allow_trailing_commas << 1) | + (opt_.allow_invalid_utf8 << 2)) + { + // no extensions + default: + cs = parse_value(cs.begin(), st, std::false_type(), std::false_type(), std::false_type()); + break; + // comments + case 1: + cs = parse_value(cs.begin(), st, std::true_type(), std::false_type(), std::false_type()); + break; + // trailing + case 2: + cs = parse_value(cs.begin(), st, std::false_type(), std::true_type(), std::false_type()); + break; + // comments & trailing + case 3: + cs = parse_value(cs.begin(), st, std::true_type(), std::true_type(), std::false_type()); + break; + // skip validation + case 4: + cs = parse_value(cs.begin(), st, std::false_type(), std::false_type(), std::true_type()); + break; + // comments & skip validation + case 5: + cs = parse_value(cs.begin(), st, std::true_type(), std::false_type(), std::true_type()); + break; + // trailing & skip validation + case 6: + cs = parse_value(cs.begin(), st, std::false_type(), std::true_type(), std::true_type()); + break; + // comments & trailing & skip validation + case 7: + cs = parse_value(cs.begin(), st, std::true_type(), std::true_type(), std::true_type()); + break; + } } if(BOOST_JSON_UNLIKELY(incomplete(cs))) // the appropriate state has already been pushed into stack @@ -610,8 +649,8 @@ parse_document(const char* p, } else if(opt_.allow_comments && *cs == '/') { -do_doc4: - cs = parse_comment(cs.begin(), stack_empty, std::true_type()); + cs = parse_comment( cs.begin(), no_state(), std::true_type() ); +after_doc4: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return sentinel(); goto do_doc3; @@ -621,96 +660,96 @@ parse_document(const char* p, template template< - bool StackEmpty_, + class PrevState, bool AllowComments_/*, bool AllowTrailing_, bool AllowBadUTF8_*/> const char* basic_parser:: -parse_value(const char* p, - std::integral_constant stack_empty, +parse_value( + const char* p, + PrevState st, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8) { - if(stack_empty || st_.empty()) - { + if( st != no_state() ) + return resume_value(p, st, allow_comments, allow_trailing, allow_bad_utf8); + loop: - switch(*p) - { - case '0': - return mp11::mp_with_index<3>( - static_cast(opt_.numbers), - parse_number_helper{ this, p }); - case '-': - return mp11::mp_with_index<3>( - static_cast(opt_.numbers), - parse_number_helper{ this, p }); - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - return mp11::mp_with_index<3>( - static_cast(opt_.numbers), - parse_number_helper{ this, p }); - case 'n': - return parse_literal( p, mp11::mp_int() ); - case 't': - return parse_literal( p, mp11::mp_int() ); - case 'f': - return parse_literal( p, mp11::mp_int() ); - case 'I': - if( !opt_.allow_infinity_and_nan ) - { - BOOST_STATIC_CONSTEXPR source_location loc - = BOOST_CURRENT_LOCATION; - return fail(p, error::syntax, &loc); - } - return parse_literal( p, mp11::mp_int() ); - case 'N': - if( !opt_.allow_infinity_and_nan ) - { - BOOST_STATIC_CONSTEXPR source_location loc - = BOOST_CURRENT_LOCATION; - return fail(p, error::syntax, &loc); - } - return parse_literal( p, mp11::mp_int() ); - case '"': - return parse_unescaped(p, std::true_type(), std::false_type(), allow_bad_utf8); - case '[': - return parse_array(p, std::true_type(), allow_comments, allow_trailing, allow_bad_utf8); - case '{': - return parse_object(p, std::true_type(), allow_comments, allow_trailing, allow_bad_utf8); - case '/': - if(! allow_comments) - { - BOOST_STATIC_CONSTEXPR source_location loc - = BOOST_CURRENT_LOCATION; - return fail(p, error::syntax, &loc); - } - p = parse_comment(p, stack_empty, std::false_type()); - // KRYSTIAN NOTE: incomplete takes const_stream, we either - // can add an overload, change the existing one to take a pointer, - // or just leave it as is - if(BOOST_JSON_UNLIKELY(p == sentinel())) - return maybe_suspend(p, state::val2); - // intentional fallthrough - case ' ': - case '\t': - case '\n': - case '\r': - p = detail::count_whitespace(p, end_); - if(BOOST_JSON_UNLIKELY(p == end_)) - return maybe_suspend(p, state::val1); - goto loop; - default: - { - BOOST_STATIC_CONSTEXPR source_location loc - = BOOST_CURRENT_LOCATION; - return fail(p, error::syntax, &loc); - } + switch(*p) + { + case '0': + return mp11::mp_with_index<3>( + static_cast(opt_.numbers), + parse_number_helper{ this, p, no_state() }); + case '-': + return mp11::mp_with_index<3>( + static_cast(opt_.numbers), + parse_number_helper{ this, p, no_state() }); + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + return mp11::mp_with_index<3>( + static_cast(opt_.numbers), + parse_number_helper{ this, p, no_state() }); + case 'n': + return parse_literal( p, mp11::mp_int() ); + case 't': + return parse_literal( p, mp11::mp_int() ); + case 'f': + return parse_literal( p, mp11::mp_int() ); + case 'I': + if( !opt_.allow_infinity_and_nan ) + { + BOOST_STATIC_CONSTEXPR source_location loc + = BOOST_CURRENT_LOCATION; + return fail(p, error::syntax, &loc); + } + return parse_literal( p, mp11::mp_int() ); + case 'N': + if( !opt_.allow_infinity_and_nan ) + { + BOOST_STATIC_CONSTEXPR source_location loc + = BOOST_CURRENT_LOCATION; + return fail(p, error::syntax, &loc); + } + return parse_literal( p, mp11::mp_int() ); + case '"': + return parse_unescaped(p, no_state(), 0, std::false_type(), allow_bad_utf8); + case '[': + return parse_array(p, no_state(), 0, allow_comments, allow_trailing, allow_bad_utf8); + case '{': + return parse_object(p, no_state(), 0, allow_comments, allow_trailing, allow_bad_utf8); + case '/': + if(! allow_comments) + { + BOOST_STATIC_CONSTEXPR source_location loc + = BOOST_CURRENT_LOCATION; + return fail(p, error::syntax, &loc); + } + p = parse_comment( p, no_state(), std::false_type() ); + // KRYSTIAN NOTE: incomplete takes const_stream, we either + // can add an overload, change the existing one to take a pointer, + // or just leave it as is + if(BOOST_JSON_UNLIKELY(p == sentinel())) + return maybe_suspend(p, state::val2); + // intentional fallthrough + case ' ': + case '\t': + case '\n': + case '\r': + p = detail::count_whitespace(p, end_); + if(BOOST_JSON_UNLIKELY(p == end_)) + return maybe_suspend(p, state::val1); + goto loop; + default: + { + BOOST_STATIC_CONSTEXPR source_location loc + = BOOST_CURRENT_LOCATION; + return fail(p, error::syntax, &loc); } } - return resume_value(p, allow_comments, allow_trailing, allow_bad_utf8); } template @@ -720,13 +759,38 @@ template< bool AllowBadUTF8_*/> const char* basic_parser:: -resume_value(const char* p, +resume_value( + const char* p, + state st, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8) { - state st; - st_.peek(st); + std::size_t n = 0; + switch(st) + { + case state::str1: case state::str2: + case state::str3: case state::str4: + case state::str5: case state::str6: + case state::str7: case state::str8: + case state::sur1: case state::sur2: + case state::sur3: case state::sur4: + case state::sur5: case state::sur6: + case state::arr1: case state::arr2: + case state::arr3: case state::arr4: + case state::arr5: case state::arr6: + case state::obj1: case state::obj2: + case state::obj3: case state::obj4: + case state::obj5: case state::obj6: + case state::obj7: case state::obj8: + case state::obj9: case state::obj10: + case state::obj11: + st_.pop(n); + break; + default: + break; + } + switch(st) { default: BOOST_JSON_UNREACHABLE(); @@ -734,7 +798,7 @@ resume_value(const char* p, return parse_literal(p, mp11::mp_int() ); case state::str1: - return parse_unescaped(p, std::false_type(), std::false_type(), allow_bad_utf8); + return parse_unescaped(p, st, n, std::false_type(), allow_bad_utf8); case state::str2: case state::str3: case state::str4: case state::str5: @@ -743,12 +807,12 @@ resume_value(const char* p, case state::sur1: case state::sur2: case state::sur3: case state::sur4: case state::sur5: case state::sur6: - return parse_escaped(p, 0, std::false_type(), std::false_type(), allow_bad_utf8); + return parse_escaped(p, st, n, std::false_type(), allow_bad_utf8); case state::arr1: case state::arr2: case state::arr3: case state::arr4: case state::arr5: case state::arr6: - return parse_array(p, std::false_type(), allow_comments, allow_trailing, allow_bad_utf8); + return parse_array(p, st, n, allow_comments, allow_trailing, allow_bad_utf8); case state::obj1: case state::obj2: case state::obj3: case state::obj4: @@ -756,7 +820,7 @@ resume_value(const char* p, case state::obj7: case state::obj8: case state::obj9: case state::obj10: case state::obj11: - return parse_object(p, std::false_type(), allow_comments, allow_trailing, allow_bad_utf8); + return parse_object(p, st, n, allow_comments, allow_trailing, allow_bad_utf8); case state::num1: case state::num2: case state::num3: case state::num4: @@ -766,36 +830,33 @@ resume_value(const char* p, case state::exp3: return mp11::mp_with_index<3>( static_cast(opt_.numbers), - parse_number_helper{ this, p }); + parse_number_helper{ this, p, st }); // KRYSTIAN NOTE: these are special cases case state::val1: { - st_.pop(st); - BOOST_ASSERT(st_.empty()); + BOOST_ASSERT( st_.empty() ); p = detail::count_whitespace(p, end_); if(BOOST_JSON_UNLIKELY(p == end_)) return maybe_suspend(p, state::val1); - return parse_value(p, std::true_type(), allow_comments, allow_trailing, allow_bad_utf8); + return parse_value(p, no_state(), allow_comments, allow_trailing, allow_bad_utf8); } case state::val2: { - st_.pop(st); - p = parse_comment(p, std::false_type(), std::false_type()); + state st_c; + st_.pop(st_c); + p = parse_comment( p, st_c, std::false_type() ); if(BOOST_JSON_UNLIKELY(p == sentinel())) return maybe_suspend(p, state::val2); if(BOOST_JSON_UNLIKELY( p == end_ )) return maybe_suspend(p, state::val3); BOOST_ASSERT(st_.empty()); - return parse_value(p, std::true_type(), std::true_type(), allow_trailing, allow_bad_utf8); + return parse_value(p, no_state(), std::true_type(), allow_trailing, allow_bad_utf8); } case state::val3: - { - st_.pop(st); - return parse_value(p, std::true_type(), std::true_type(), allow_trailing, allow_bad_utf8); - } + return parse_value(p, no_state(), std::true_type(), allow_trailing, allow_bad_utf8); } } @@ -906,10 +967,6 @@ parse_literal(const char* p, } else { - state st; - st_.pop(st); - BOOST_ASSERT( st == state::lit1 ); - cur_lit = cur_lit_; offset = lit_offset_; } @@ -992,66 +1049,60 @@ parse_literal(const char* p, template template< - bool StackEmpty_, + class PrevState, bool IsKey_/*, bool AllowBadUTF8_*/> const char* basic_parser:: -parse_string(const char* p, - std::integral_constant stack_empty, +parse_string( + const char* p, + PrevState st, + std::size_t total, std::integral_constant is_key, /*std::integral_constant*/ bool allow_bad_utf8) { - if(! stack_empty && ! st_.empty()) + if( st == no_state() ) + return parse_unescaped(p, st, 0, is_key, allow_bad_utf8); + + switch(st) { - state st; - st_.peek(st); - switch(st) - { - default: BOOST_JSON_UNREACHABLE(); - case state::str1: - return parse_unescaped(p, stack_empty, is_key, allow_bad_utf8); + default: BOOST_JSON_UNREACHABLE(); + case state::str1: + return parse_unescaped(p, st, total, is_key, allow_bad_utf8); - case state::str2: case state::str3: - case state::str4: case state::str5: - case state::str6: case state::str7: - case state::str8: - case state::sur1: case state::sur2: - case state::sur3: case state::sur4: - case state::sur5: case state::sur6: - return parse_escaped(p, 0, stack_empty, is_key, allow_bad_utf8); - } + case state::str2: case state::str3: + case state::str4: case state::str5: + case state::str6: case state::str7: + case state::str8: + case state::sur1: case state::sur2: + case state::sur3: case state::sur4: + case state::sur5: case state::sur6: + return parse_escaped(p, st, total, is_key, allow_bad_utf8); } - - return parse_unescaped(p, std::true_type(), is_key, allow_bad_utf8); } template template< - bool StackEmpty_, + class PrevState, bool IsKey_/*, bool AllowBadUTF8_*/> const char* basic_parser:: -parse_unescaped(const char* p, - std::integral_constant stack_empty, +parse_unescaped( + const char* p, + PrevState st, + std::size_t total, std::integral_constant is_key, /*std::integral_constant*/ bool allow_bad_utf8) { detail::const_stream_wrapper cs(p, end_); - std::size_t total; - if(stack_empty || st_.empty()) + if( st == no_state() ) { BOOST_ASSERT(*cs == '\x22'); // '"' ++cs; total = 0; } - else - { - state st; - st_.pop(st); - st_.pop(total); - } + char const* start = cs.begin(); cs = allow_bad_utf8? detail::count_valid(cs.begin(), cs.end()): @@ -1144,7 +1195,7 @@ parse_unescaped(const char* p, } } } - return parse_escaped(cs.begin(), total, stack_empty, is_key, allow_bad_utf8); + return parse_escaped(cs.begin(), no_state(), total, is_key, allow_bad_utf8); } // illegal control BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION; @@ -1168,15 +1219,15 @@ parse_unescaped(const char* p, template template< - bool StackEmpty_/*, + class PrevState/*, bool IsKey_, bool AllowBadUTF8_*/> const char* basic_parser:: parse_escaped( const char* p, + PrevState st, std::size_t total, - std::integral_constant stack_empty, /*std::integral_constant*/ bool is_key, /*std::integral_constant*/ bool allow_bad_utf8) { @@ -1202,11 +1253,8 @@ parse_escaped( int digit; char c; cs.clip(temp.max_size()); - if(! stack_empty && ! st_.empty()) + if( st != no_state() ) { - state st; - st_.pop(st); - st_.pop(total); switch(st) { default: BOOST_JSON_UNREACHABLE(); @@ -1225,6 +1273,10 @@ parse_escaped( case state::sur6: goto do_sur6; } } + else + { + total = 0; + } // Unescaped JSON is never larger than its escaped version. // To efficiently process only what will fit in the temporary buffer, // the size of the input stream is temporarily "clipped" to the size @@ -1726,44 +1778,82 @@ parse_escaped( template template< - bool StackEmpty_, + class PrevState, bool AllowComments_/*, bool AllowTrailing_, bool AllowBadUTF8_*/> const char* basic_parser:: parse_object(const char* p, - std::integral_constant stack_empty, + PrevState st, + std::size_t size, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8) { detail::const_stream_wrapper cs(p, end_); - std::size_t size; - if(! stack_empty && ! st_.empty()) + if( st != no_state() ) { // resume - state st; - st_.pop(st); - st_.pop(size); switch(st) { default: BOOST_JSON_UNREACHABLE(); case state::obj1: goto do_obj1; - case state::obj2: goto do_obj2; - case state::obj3: goto do_obj3; + case state::obj2: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_obj2; + } + case state::obj3: + { + state st_c; + std::size_t total; + st_.pop(st_c); + st_.pop(total); + cs = parse_string(cs.begin(), st_c, total, std::true_type(), allow_bad_utf8); + goto after_obj3; + } case state::obj4: goto do_obj4; - case state::obj5: goto do_obj5; + case state::obj5: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_obj5; + } case state::obj6: goto do_obj6; - case state::obj7: goto do_obj7; + case state::obj7: + { + state st_v; + st_.pop(st_v); + cs = parse_value(cs.begin(), st_v, allow_comments, allow_trailing, allow_bad_utf8); + goto after_obj7; + } case state::obj8: goto do_obj8; case state::obj9: goto do_obj9; - case state::obj10: goto do_obj10; - case state::obj11: goto do_obj11; + case state::obj10: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_obj10; } + case state::obj11: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_obj11; + } + } + } + else + { + size = 0; } BOOST_ASSERT(*cs == '{'); - size = 0; if(BOOST_JSON_UNLIKELY(! depth_)) { BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION; @@ -1787,8 +1877,8 @@ parse_object(const char* p, { if(allow_comments && *cs == '/') { -do_obj2: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_obj2: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj2, size); goto do_obj1; @@ -1805,8 +1895,8 @@ parse_object(const char* p, = BOOST_CURRENT_LOCATION; return fail(cs.begin(), error::object_too_large, &loc); } -do_obj3: - cs = parse_string(cs.begin(), stack_empty, std::true_type(), allow_bad_utf8); + cs = parse_string(cs.begin(), no_state(), 0, std::true_type(), allow_bad_utf8); +after_obj3: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj3, size); do_obj4: @@ -1817,8 +1907,8 @@ parse_object(const char* p, { if(allow_comments && *cs == '/') { -do_obj5: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_obj5: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj5, size); goto do_obj4; @@ -1832,8 +1922,8 @@ parse_object(const char* p, cs = detail::count_whitespace(cs.begin(), cs.end()); if(BOOST_JSON_UNLIKELY(! cs)) return maybe_suspend(cs.begin(), state::obj6, size); -do_obj7: - cs = parse_value(cs.begin(), stack_empty, allow_comments, allow_trailing, allow_bad_utf8); + cs = parse_value(cs.begin(), no_state(), allow_comments, allow_trailing, allow_bad_utf8); +after_obj7: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj7, size); do_obj8: @@ -1855,8 +1945,8 @@ parse_object(const char* p, { if(allow_comments && *cs == '/') { -do_obj10: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_obj10: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj10, size); goto do_obj9; @@ -1870,8 +1960,8 @@ parse_object(const char* p, { if(allow_comments && *cs == '/') { -do_obj11: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_obj11: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::obj11, size); goto do_obj8; @@ -1894,39 +1984,57 @@ parse_object(const char* p, template template< - bool StackEmpty_, + class PrevState, bool AllowComments_/*, bool AllowTrailing_, bool AllowBadUTF8_*/> const char* basic_parser:: -parse_array(const char* p, - std::integral_constant stack_empty, +parse_array( + const char* p, + PrevState st, + std::size_t size, std::integral_constant allow_comments, /*std::integral_constant*/ bool allow_trailing, /*std::integral_constant*/ bool allow_bad_utf8) { detail::const_stream_wrapper cs(p, end_); - std::size_t size; - if(! stack_empty && ! st_.empty()) + if( st != no_state() ) { - // resume - state st; - st_.pop(st); - st_.pop(size); switch(st) { default: BOOST_JSON_UNREACHABLE(); case state::arr1: goto do_arr1; - case state::arr2: goto do_arr2; - case state::arr3: goto do_arr3; + case state::arr2: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_arr2; + } + case state::arr3: + { + state st_v; + st_.pop(st_v); + cs = parse_value(cs.begin(), st_v, allow_comments, allow_trailing, allow_bad_utf8); + goto after_arr3; + } case state::arr4: goto do_arr4; case state::arr5: goto do_arr5; - case state::arr6: goto do_arr6; + case state::arr6: + { + state st_c; + st_.pop(st_c); + cs = parse_comment( cs.begin(), st_c, std::false_type() ); + goto after_arr6; + } } } + else + { + size = 0; + } BOOST_ASSERT(*cs == '['); - size = 0; if(BOOST_JSON_UNLIKELY(! depth_)) { BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION; @@ -1949,8 +2057,8 @@ parse_array(const char* p, loop: if(allow_comments && *cs == '/') { -do_arr2: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_arr2: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::arr2, size); goto do_arr1; @@ -1962,9 +2070,9 @@ parse_array(const char* p, = BOOST_CURRENT_LOCATION; return fail(cs.begin(), error::array_too_large, &loc); } -do_arr3: // array is not empty, value required - cs = parse_value(cs.begin(), stack_empty, allow_comments, allow_trailing, allow_bad_utf8); + cs = parse_value(cs.begin(), no_state(), allow_comments, allow_trailing, allow_bad_utf8); +after_arr3: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::arr3, size); do_arr4: @@ -1986,8 +2094,8 @@ parse_array(const char* p, { if(allow_comments && *cs == '/') { -do_arr6: - cs = parse_comment(cs.begin(), stack_empty, std::false_type()); + cs = parse_comment( cs.begin(), no_state(), std::false_type() ); +after_arr6: if(BOOST_JSON_UNLIKELY(incomplete(cs))) return suspend_or_fail(state::arr6, size); goto do_arr4; @@ -2009,11 +2117,11 @@ parse_array(const char* p, //---------------------------------------------------------- template -template +template const char* basic_parser:: parse_number(const char* p, - std::integral_constant stack_empty, + PrevState st, std::integral_constant first, std::integral_constant mode) { @@ -2030,7 +2138,7 @@ parse_number(const char* p, detail::const_stream_wrapper cs(p, end_); number num; const char* begin = cs.begin(); - if(stack_empty || st_.empty()) + if( st == no_state() ) { num.bias = 0; num.exp = 0; @@ -2161,8 +2269,6 @@ parse_number(const char* p, else { num = num_; - state st; - st_.pop(st); switch(st) { default: BOOST_JSON_UNREACHABLE(); @@ -2211,7 +2317,6 @@ parse_number(const char* p, } else if( (negative || num.neg) && opt_.allow_infinity_and_nan ) { - st_.push(state::lit1); cur_lit_ = detail::neg_infinity_literal; lit_offset_ = 1; return parse_literal( @@ -2243,7 +2348,7 @@ parse_number(const char* p, // significant digits left of decimal // do_num2: - if(negative || (!stack_empty && num.neg)) + if(negative || ((st != no_state()) && num.neg)) { for(;;) { @@ -2730,7 +2835,7 @@ parse_number(const char* p, } finish_int: - if(negative || (!stack_empty && num.neg)) + if(negative || ((st != no_state()) && num.neg)) { if(BOOST_JSON_UNLIKELY( ! h_.on_int64(static_cast< @@ -2874,11 +2979,13 @@ write_some( ec = ec_; return 0; } - p = parse_document(data, std::true_type()); + p = parse_document(data, no_state()); } else { - p = parse_document(data, std::false_type()); + state st; + st_.pop(st); + p = parse_document(data, st); } if(BOOST_JSON_LIKELY(p != sentinel()))