// Copyright (c) 2016-2020 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/json/ #ifndef TAO_JSON_EVENTS_VALIDATE_EVENT_ORDER_HPP #define TAO_JSON_EVENTS_VALIDATE_EVENT_ORDER_HPP #include #include #include #include #include #include #include #include #include "../binary_view.hpp" namespace tao::json::events { // Events consumer that validates the order of events. class validate_event_order { private: enum state_t { EXPECT_TOP_LEVEL_VALUE, EXPECT_ARRAY_VALUE_OR_END, EXPECT_ARRAY_ELEMENT, EXPECT_SIZED_ARRAY_VALUE_OR_END, EXPECT_SIZED_ARRAY_ELEMENT, EXPECT_OBJECT_KEY_OR_END, EXPECT_OBJECT_VALUE, EXPECT_OBJECT_MEMBER, EXPECT_SIZED_OBJECT_KEY_OR_END, EXPECT_SIZED_OBJECT_VALUE, EXPECT_SIZED_OBJECT_MEMBER, EXPECT_NOTHING }; struct sizes_t { explicit sizes_t( const std::size_t in_expected ) : expected( in_expected ) {} void check( const std::size_t in_expected ) { if( expected != in_expected ) { throw std::logic_error( "inconsistent size" ); } if( expected != counted ) { throw std::logic_error( "wrong size" ); } } std::size_t expected; std::size_t counted = 0; }; state_t state = EXPECT_TOP_LEVEL_VALUE; std::vector< state_t > stack; std::vector< sizes_t > sizes; void atom( const std::string& function ) { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: state = EXPECT_NOTHING; return; case EXPECT_ARRAY_VALUE_OR_END: state = EXPECT_ARRAY_ELEMENT; return; case EXPECT_SIZED_ARRAY_VALUE_OR_END: state = EXPECT_SIZED_ARRAY_ELEMENT; return; case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but " + function + " was called" ); case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but " + function + " was called" ); case EXPECT_OBJECT_VALUE: state = EXPECT_OBJECT_MEMBER; return; case EXPECT_SIZED_OBJECT_VALUE: state = EXPECT_SIZED_OBJECT_MEMBER; return; case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but " + function + " was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but " + function + " was called" ); } throw std::logic_error( "invalid state" ); } public: [[nodiscard]] bool is_complete() const noexcept { return state == EXPECT_NOTHING; } void null() { atom( "null()" ); } void boolean( const bool /*unused*/ ) { atom( "boolen()" ); } void number( const std::int64_t /*unused*/ ) { atom( "number(std::int64_t)" ); } void number( const std::uint64_t /*unused*/ ) { atom( "number(std::uint64_t)" ); } void number( const double /*unused*/ ) { atom( "number(double)" ); } void string( const std::string_view /*unused*/ ) { atom( "string(...)" ); } void binary( const tao::binary_view /*unused*/ ) { atom( "binary(...)" ); } void begin_array() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: stack.push_back( EXPECT_NOTHING ); state = EXPECT_ARRAY_VALUE_OR_END; return; case EXPECT_ARRAY_VALUE_OR_END: stack.push_back( EXPECT_ARRAY_ELEMENT ); return; case EXPECT_SIZED_ARRAY_VALUE_OR_END: stack.push_back( EXPECT_SIZED_ARRAY_ELEMENT ); return; case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but begin_array(...) was called" ); case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but begin_array(...) was called" ); case EXPECT_OBJECT_VALUE: stack.push_back( EXPECT_OBJECT_MEMBER ); state = EXPECT_ARRAY_VALUE_OR_END; return; case EXPECT_SIZED_OBJECT_VALUE: stack.push_back( EXPECT_SIZED_OBJECT_MEMBER ); state = EXPECT_ARRAY_VALUE_OR_END; return; case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but begin_array(...) was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but begin_array(...) was called" ); } throw std::logic_error( "invalid state" ); } void begin_array( const std::size_t expected ) { begin_array(); sizes.emplace_back( expected ); state = EXPECT_SIZED_ARRAY_VALUE_OR_END; } void element() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: throw std::logic_error( "expected any value, but element() was called" ); case EXPECT_ARRAY_VALUE_OR_END: case EXPECT_SIZED_ARRAY_VALUE_OR_END: throw std::logic_error( "expected any value or end_array(...), but element() was called" ); case EXPECT_ARRAY_ELEMENT: state = EXPECT_ARRAY_VALUE_OR_END; return; case EXPECT_SIZED_ARRAY_ELEMENT: state = EXPECT_SIZED_ARRAY_VALUE_OR_END; ++sizes.back().counted; return; case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but element() was called" ); case EXPECT_OBJECT_VALUE: case EXPECT_SIZED_OBJECT_VALUE: throw std::logic_error( "expected any value, but element() was called" ); case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but element() was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but element() was called" ); } throw std::logic_error( "invalid state" ); } void end_array() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: throw std::logic_error( "expected any value, but end_array(....) was called" ); case EXPECT_ARRAY_VALUE_OR_END: state = stack.back(); stack.pop_back(); return; case EXPECT_SIZED_ARRAY_VALUE_OR_END: throw std::logic_error( "expected end_array(std::size_t), but end_array() was called" ); case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but end_array(...) was called" ); case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but end_array(...) was called" ); case EXPECT_OBJECT_VALUE: case EXPECT_SIZED_OBJECT_VALUE: throw std::logic_error( "expected any value, but end_array(...) was called" ); case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but end_array(...) was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but end_array(...) was called" ); } throw std::logic_error( "invalid state" ); } void end_array( const std::size_t expected ) { switch( state ) { case EXPECT_SIZED_ARRAY_VALUE_OR_END: state = EXPECT_ARRAY_VALUE_OR_END; break; case EXPECT_ARRAY_VALUE_OR_END: throw std::logic_error( "expected any value or end_array(), but end_array(std::size_t) was called" ); default: break; } assert( sizes.size() ); sizes.back().check( expected ); sizes.pop_back(); end_array(); } void begin_object() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: stack.push_back( EXPECT_NOTHING ); state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_ARRAY_VALUE_OR_END: stack.push_back( EXPECT_ARRAY_ELEMENT ); state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_SIZED_ARRAY_VALUE_OR_END: stack.push_back( EXPECT_SIZED_ARRAY_ELEMENT ); state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but begin_object(...) was called" ); case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but begin_object(...) was called" ); case EXPECT_OBJECT_VALUE: stack.push_back( EXPECT_OBJECT_MEMBER ); state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_SIZED_OBJECT_VALUE: stack.push_back( EXPECT_SIZED_OBJECT_MEMBER ); state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but begin_object(...) was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but begin_object(...) was called" ); } throw std::logic_error( "invalid state" ); } void begin_object( const std::size_t expected ) { begin_object(); sizes.emplace_back( expected ); state = EXPECT_SIZED_OBJECT_KEY_OR_END; } void key( const std::string_view /*unused*/ ) { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: throw std::logic_error( "expected any value, but key() was called" ); case EXPECT_ARRAY_VALUE_OR_END: case EXPECT_SIZED_ARRAY_VALUE_OR_END: throw std::logic_error( "expected any value or end_array(...), but key() was called" ); case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but key() was called" ); case EXPECT_OBJECT_KEY_OR_END: state = EXPECT_OBJECT_VALUE; return; case EXPECT_SIZED_OBJECT_KEY_OR_END: state = EXPECT_SIZED_OBJECT_VALUE; return; case EXPECT_OBJECT_VALUE: case EXPECT_SIZED_OBJECT_VALUE: throw std::logic_error( "expected any value, but key() was called" ); case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but key() was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but key() was called" ); } throw std::logic_error( "invalid state" ); } void member() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: throw std::logic_error( "expected any value, but member() was called" ); case EXPECT_ARRAY_VALUE_OR_END: case EXPECT_SIZED_ARRAY_VALUE_OR_END: throw std::logic_error( "expected any value or end_array(...), but member() was called" ); case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but member() was called" ); case EXPECT_OBJECT_KEY_OR_END: case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected key() or end_object(...), but member() was called" ); case EXPECT_OBJECT_VALUE: case EXPECT_SIZED_OBJECT_VALUE: throw std::logic_error( "expected any value, but member() was called" ); case EXPECT_OBJECT_MEMBER: state = EXPECT_OBJECT_KEY_OR_END; return; case EXPECT_SIZED_OBJECT_MEMBER: state = EXPECT_SIZED_OBJECT_KEY_OR_END; ++sizes.back().counted; return; case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but member() was called" ); } throw std::logic_error( "invalid state" ); } void end_object() { switch( state ) { case EXPECT_TOP_LEVEL_VALUE: throw std::logic_error( "expected any value, but end_object(...) was called" ); case EXPECT_ARRAY_VALUE_OR_END: case EXPECT_SIZED_ARRAY_VALUE_OR_END: throw std::logic_error( "expected any value or end_array(...), but end_object(...) was called" ); case EXPECT_ARRAY_ELEMENT: case EXPECT_SIZED_ARRAY_ELEMENT: throw std::logic_error( "expected element(), but end_object(...) was called" ); case EXPECT_OBJECT_KEY_OR_END: state = stack.back(); stack.pop_back(); return; case EXPECT_SIZED_OBJECT_KEY_OR_END: throw std::logic_error( "expected any value or end_object(std::size_t), but end_object() was called" ); case EXPECT_OBJECT_VALUE: case EXPECT_SIZED_OBJECT_VALUE: throw std::logic_error( "expected any value, but end_object(...) was called" ); case EXPECT_OBJECT_MEMBER: case EXPECT_SIZED_OBJECT_MEMBER: throw std::logic_error( "expected member(), but end_object(...) was called" ); case EXPECT_NOTHING: throw std::logic_error( "expected nothing, but end_object(...) was called" ); } throw std::logic_error( "invalid state" ); } void end_object( const std::size_t expected ) { switch( state ) { case EXPECT_SIZED_OBJECT_KEY_OR_END: state = EXPECT_OBJECT_KEY_OR_END; break; case EXPECT_OBJECT_KEY_OR_END: throw std::logic_error( "expected any value or end_object(), but end_object(std::size_t) was called" ); default: break; } assert( sizes.size() ); sizes.back().check( expected ); sizes.pop_back(); end_object(); } }; } // namespace tao::json::events #endif