diff --git a/libraries/db/CMakeLists.txt b/libraries/db/CMakeLists.txt index 91e26cc6..8538cf01 100644 --- a/libraries/db/CMakeLists.txt +++ b/libraries/db/CMakeLists.txt @@ -1,4 +1,4 @@ file(GLOB HEADERS "include/graphene/db/*.hpp") -add_library( graphene_db undo_database.cpp index.cpp object_database.cpp ${HEADERS} ) +add_library( graphene_db undo_database.cpp index.cpp object_database.cpp type_serializer.cpp ${HEADERS} ) target_link_libraries( graphene_db fc ) target_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index 89e8aafe..0f341416 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -17,6 +17,7 @@ */ #pragma once #include +#include #include #include #include @@ -209,11 +210,8 @@ namespace graphene { namespace db { fc::sha256 get_object_version()const { - // TODO: use something other than json to describe the type and detect changes in serialization - // json will only detect adding, removing, or renaming fields but will not detect changing types - // or changes in the contents of optional values. - auto json_obj = fc::json::to_string( object_type() ); - return fc::sha256::hash(json_obj); + auto desc = get_type_description(); + return fc::sha256::hash(desc); } virtual void open( const path& db )override diff --git a/libraries/db/include/graphene/db/type_serializer.hpp b/libraries/db/include/graphene/db/type_serializer.hpp new file mode 100644 index 00000000..17dc6e35 --- /dev/null +++ b/libraries/db/include/graphene/db/type_serializer.hpp @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include + +namespace graphene { namespace db { +using std::set; +using std::map; +using fc::time_point_sec; +using fc::time_point; +using std::string; + +std::set& processed_types(); +std::stringstream& current_stream( std::unique_ptr&& s = std::unique_ptr() ); +string remove_tail_if( const string& str, char c, const string& match ); +string remove_namespace_if( const string& str, const string& match ); +string remove_namespace( string str ); + +template +void generate_serializer(); +template +void register_serializer(); + +extern map st; +/* +extern vector> serializers; +*/ + +bool register_serializer( const string& name, std::function sr ); + +template struct js_name { static std::string name(){ return remove_namespace(fc::get_typename::name()); }; }; + +template +struct js_name> +{ + static std::string name(){ return "fixed_array "+ fc::to_string(N) + ", " + remove_namespace(fc::get_typename::name()); }; +}; +template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; +template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; +template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; +template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } }; +template struct js_name< fc::flat_set > { static std::string name(){ return "set " + js_name::name(); } }; +template struct js_name< std::vector > { static std::string name(){ return "array " + js_name::name(); } }; +template struct js_name< fc::safe > { static std::string name(){ return js_name::name(); } }; + + +template<> struct js_name< std::vector > { static std::string name(){ return "bytes()"; } }; +//template<> struct js_name< op_wrapper > { static std::string name(){ return "operation "; } }; +template<> struct js_name { static std::string name(){ return "bytes 20"; } }; +template<> struct js_name { static std::string name(){ return "bytes 28"; } }; +template<> struct js_name { static std::string name(){ return "varuint32"; } }; +template<> struct js_name { static std::string name(){ return "varint32"; } }; +template<> struct js_name< time_point_sec > { static std::string name(){ return "time_point_sec"; } }; + +template +struct js_name > +{ + static std::string name(){ + return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; + }; +}; + + +template struct js_name< std::set > { static std::string name(){ return "set " + js_name::name(); } }; + +template +struct js_name< std::map > { static std::string name(){ return "map (" + js_name::name() + "), (" + js_name::name() +")"; } }; + +template +struct js_name< fc::flat_map > { static std::string name(){ return "map (" + js_name::name() + "), (" + js_name::name() +")"; } }; + + +template struct js_sv_name; + +template struct js_sv_name +{ static std::string name(){ return "\n " + js_name::name(); } }; + +template +struct js_sv_name { static std::string name(){ return "\n " + js_name::name() +" " + js_sv_name::name(); } }; + + +template +struct js_name< fc::static_variant > +{ + static std::string name( std::string n = ""){ + static const std::string name = n; + if( name == "" ) + return "static_variant [" + js_sv_name::name() + "\n]"; + else return name; + } +}; + + +template::is_defined::value> +struct serializer; + + +struct register_type_visitor +{ + typedef void result_type; + + template + result_type operator()( const Type& op )const { serializer::init(); } +}; + +class register_member_visitor; + +struct serialize_type_visitor +{ + typedef void result_type; + + fc::mutable_variant_object& obj; + int t = 0; + serialize_type_visitor( fc::mutable_variant_object& o, int _t ):obj(o),t(_t){} + + template + result_type operator()( const Type& op )const + { + obj(remove_namespace( fc::get_typename::name()),t); + } +}; + + +class serialize_member_visitor +{ + public: + template + void operator()( const char* name )const + { + current_stream() << name << " : " << "";//js_name::name(); + } +}; + +template +struct serializer +{ + static_assert( fc::reflector::is_defined::value == false, "invalid template arguments" ); + static void init() + {} + + static void generate() + {} +}; + +template +struct serializer,false> +{ + static void init() { serializer::init(); } + static void generate() {} +}; +template +struct serializer,false> +{ + static void init() { serializer::init(); } + static void generate() {} +}; +/* +template<> +struct serializer,false> +{ + static void init() { } + static void generate() {} +}; +*/ + +template<> +struct serializer +{ + static void init() {} + + static void generate() {} +}; +template<> +struct serializer +{ + static void init() {} + static void generate() {} +}; +#ifdef __APPLE__ +// on mac, size_t is a distinct type from uint64_t or uint32_t and needs a separate specialization +template<> struct serializer { static void init() {} static void generate() {} }; +#endif +template<> struct serializer { static void init() {} static void generate() {} }; +template<> struct serializer { static void init() {} static void generate() {} }; + +template +struct serializer,false> +{ + static void init() { serializer::init(); } + static void generate(){} +}; + +template +struct serializer< graphene::db::object_id ,true> +{ + static void init() {} + static void generate() {} +}; + +template +struct serializer< fc::static_variant, false > +{ + static void init() + { + auto name = js_name>::name(); + if( processed_types().find( name ) == processed_types().end() ) + { + processed_types().insert( name ); + fc::static_variant var; + for( int i = 0; i < var.count(); ++i ) + { + var.set_which(i); + var.visit( register_type_visitor() ); + } + register_serializer( js_name>::name(), [=](){ generate(); } ); + } + } + + static void generate() + { + current_stream() << js_name>::name() << " = static_variant [" + js_sv_name::name() + "\n]\n\n"; + } +}; + +class register_member_visitor +{ + public: + template + void operator()( const char* name )const + { + serializer::init(); + } +}; + +template +struct serializer +{ + static_assert( fc::reflector::is_defined::value == reflected, "invalid template arguments" ); + static void init() + { + auto name = js_name::name(); + if( st.find(name) == st.end() ) + { + fc::reflector::visit( register_member_visitor() ); + register_serializer( name, [=](){ generate(); } ); + } + } + + static void generate() + { + auto name = remove_namespace( js_name::name() ); + if( name == "int64" ) return; + current_stream() << "" << name + << " = new Serializer( \n" + << " \"" + name + "\"\n"; + + fc::reflector::visit( serialize_member_visitor() ); + + current_stream() <<")\n\n"; + } +}; + + +template +std::string get_type_description() +{ + current_stream( std::unique_ptr(new std::stringstream()) ); + processed_types().clear(); + serializer::init(); + return current_stream().str(); +} + + +} } // graphene::db + + + + + + + + diff --git a/libraries/db/type_serializer.cpp b/libraries/db/type_serializer.cpp new file mode 100644 index 00000000..b737101d --- /dev/null +++ b/libraries/db/type_serializer.cpp @@ -0,0 +1,63 @@ +#include + +namespace graphene { namespace db { +map st; +vector> serializers; + +std::set& processed_types() +{ + static std::set p; + return p; +} + +std::stringstream& current_stream( std::unique_ptr&& s ) +{ + static std::unique_ptr stream; + if( s ) stream = std::move(s); + FC_ASSERT( stream ); + return *stream; +} + +string remove_tail_if( const string& str, char c, const string& match ) +{ + auto last = str.find_last_of( c ); + if( last != std::string::npos ) + if( str.substr( last + 1 ) == match ) + return str.substr( 0, last ); + return str; +} +string remove_namespace_if( const string& str, const string& match ) +{ + auto last = str.find( match ); + if( last != std::string::npos ) + return str.substr( match.size()+2 ); + return str; +} + +string remove_namespace( string str ) +{ + str = remove_tail_if( str, '_', "operation" ); + str = remove_tail_if( str, '_', "t" ); + str = remove_tail_if( str, '_', "object" ); + str = remove_tail_if( str, '_', "type" ); + str = remove_namespace_if( str, "graphene::chain" ); + str = remove_namespace_if( str, "graphene::db" ); + str = remove_namespace_if( str, "std" ); + str = remove_namespace_if( str, "fc" ); + auto pos = str.find( ":" ); + if( pos != str.npos ) + str.replace( pos, 2, "_" ); + return str; +} +bool register_serializer( const string& name, std::function sr ) +{ + if( st.find(name) == st.end() ) + { + serializers.push_back( sr ); + st[name] = serializers.size() - 1; + return true; + } + return false; +} + +} }// graphene