/* * Copyright (c) 2015 Cryptonomex, Inc., and contributors. * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include #define GRAPHENE_DB_MAX_INSTANCE_ID (uint64_t(-1)>>16) namespace graphene { namespace db { using std::shared_ptr; using std::unique_ptr; using std::vector; using fc::flat_map; using fc::variant; using fc::unsigned_int; using fc::signed_int; struct object_id_type { object_id_type( uint8_t s, uint8_t t, uint64_t i ) { assert( i>>48 == 0 ); FC_ASSERT( i >> 48 == 0, "instance overflow", ("instance",i) ); number = (uint64_t(s)<<56) | (uint64_t(t)<<48) | i; } object_id_type(){ number = 0; } uint8_t space()const { return number >> 56; } uint8_t type()const { return number >> 48 & 0x00ff; } uint16_t space_type()const { return number >> 48; } uint64_t instance()const { return number & GRAPHENE_DB_MAX_INSTANCE_ID; } bool is_null()const { return number == 0; } explicit operator uint64_t()const { return number; } friend bool operator == ( const object_id_type& a, const object_id_type& b ) { return a.number == b.number; } friend bool operator != ( const object_id_type& a, const object_id_type& b ) { return a.number != b.number; } friend bool operator < ( const object_id_type& a, const object_id_type& b ) { return a.number < b.number; } friend bool operator > ( const object_id_type& a, const object_id_type& b ) { return a.number > b.number; } object_id_type& operator++(int) { ++number; return *this; } object_id_type& operator++() { ++number; return *this; } friend object_id_type operator+(const object_id_type& a, int delta ) { return object_id_type( a.space(), a.type(), a.instance() + delta ); } friend object_id_type operator+(const object_id_type& a, int64_t delta ) { return object_id_type( a.space(), a.type(), a.instance() + delta ); } friend size_t hash_value( object_id_type v ) { return std::hash()(v.number); } template< typename T > bool is() const { return (number >> 48) == ((T::space_id << 8) | (T::type_id)); } template< typename T > T as() const { FC_ASSERT( is() ); return T( *this ); } explicit operator std::string() const { return fc::to_string(space()) + "." + fc::to_string(type()) + "." + fc::to_string(instance()); } uint64_t number; }; class object; class object_database; template struct object_id { typedef T type; static const uint8_t space_id = SpaceID; static const uint8_t type_id = TypeID; object_id(){} object_id( unsigned_int i ):instance(i){} explicit object_id( uint64_t i ):instance(i) { FC_ASSERT( (i >> 48) == 0 ); } object_id( object_id_type id ):instance(id.instance()) { } friend object_id operator+(const object_id a, int64_t delta ) { return object_id( uint64_t(a.instance.value+delta) ); } friend object_id operator+(const object_id a, int delta ) { return object_id( uint64_t(a.instance.value+delta) ); } operator object_id_type()const { return object_id_type( SpaceID, TypeID, instance.value ); } explicit operator uint64_t()const { return object_id_type( *this ).number; } template const T& operator()(const DB& db)const { return db.get(*this); } friend bool operator == ( const object_id& a, const object_id& b ) { return a.instance == b.instance; } friend bool operator != ( const object_id& a, const object_id& b ) { return a.instance != b.instance; } friend bool operator == ( const object_id_type& a, const object_id& b ) { return a == object_id_type(b); } friend bool operator != ( const object_id_type& a, const object_id& b ) { return a != object_id_type(b); } friend bool operator == ( const object_id& b, const object_id_type& a ) { return a == object_id_type(b); } friend bool operator != ( const object_id& b, const object_id_type& a ) { return a != object_id_type(b); } friend bool operator < ( const object_id& a, const object_id& b ) { return a.instance.value < b.instance.value; } friend bool operator > ( const object_id& a, const object_id& b ) { return a.instance.value > b.instance.value; } friend size_t hash_value( object_id v ) { return std::hash()(v.instance.value); } unsigned_int instance; }; } } // graphene::db FC_REFLECT( graphene::db::object_id_type, (number) ) // REFLECT object_id manually because it has 2 template params namespace fc { template struct get_typename> { static const char* name() { return typeid(get_typename).name(); static std::string _str = string("graphene::db::object_id<")+fc::to_string(SpaceID) + ":" + fc::to_string(TypeID)+">"; return _str.c_str(); } }; template struct reflector > { typedef graphene::db::object_id type; typedef fc::true_type is_defined; typedef fc::false_type is_enum; enum member_count_enum { local_member_count = 1, total_member_count = 1 }; template static inline void visit( const Visitor& visitor ) { typedef decltype(((type*)nullptr)->instance) member_type; visitor.TEMPLATE operator()( "instance" ); } }; inline void to_variant( const graphene::db::object_id_type& var, fc::variant& vo, uint32_t max_depth = 1 ) { vo = std::string( var ); } inline void from_variant( const fc::variant& var, graphene::db::object_id_type& vo, uint32_t max_depth = 1 ) { try { vo.number = 0; const auto& s = var.get_string(); auto first_dot = s.find('.'); auto second_dot = s.find('.',first_dot+1); FC_ASSERT( first_dot != second_dot ); FC_ASSERT( first_dot != 0 && first_dot != std::string::npos ); vo.number = fc::to_uint64(s.substr( second_dot+1 )); FC_ASSERT( vo.number <= GRAPHENE_DB_MAX_INSTANCE_ID ); auto space_id = fc::to_uint64( s.substr( 0, first_dot ) ); FC_ASSERT( space_id <= 0xff ); auto type_id = fc::to_uint64( s.substr( first_dot+1, second_dot-first_dot-1 ) ); FC_ASSERT( type_id <= 0xff ); vo.number |= (space_id << 56) | (type_id << 48); } FC_CAPTURE_AND_RETHROW( (var) ) } template void to_variant( const graphene::db::object_id& var, fc::variant& vo, uint32_t max_depth = 1 ) { vo = fc::to_string(SpaceID) + "." + fc::to_string(TypeID) + "." + fc::to_string(var.instance.value); } template void from_variant( const fc::variant& var, graphene::db::object_id& vo, uint32_t max_depth = 1 ) { try { const auto& s = var.get_string(); auto first_dot = s.find('.'); auto second_dot = s.find('.',first_dot+1); FC_ASSERT( first_dot != second_dot ); FC_ASSERT( first_dot != 0 && first_dot != std::string::npos ); FC_ASSERT( fc::to_uint64( s.substr( 0, first_dot ) ) == SpaceID && fc::to_uint64( s.substr( first_dot+1, second_dot-first_dot-1 ) ) == TypeID, "Space.Type.0 (${SpaceID}.${TypeID}.0) doesn't match expected value ${var}", ("TypeID",TypeID)("SpaceID",SpaceID)("var",var) ); vo.instance = fc::to_uint64(s.substr( second_dot+1 )); } FC_CAPTURE_AND_RETHROW( (var) ) } } // namespace fc namespace std { template <> struct hash { size_t operator()(const graphene::db::object_id_type& x) const { return std::hash()(x.number); } }; }