| #include <ft2build.h> |
| #include FT_INTERNAL_OBJECT_H |
| #include FT_INTERNAL_DEBUG_H |
| #include FT_INTERNAL_OBJECTS_H |
| |
| #define FT_MAGIC_DEATH 0xDEADdead |
| #define FT_MAGIC_CLASS 0x12345678 |
| |
| #define FT_TYPE_HASH(x) (( (FT_UInt32)(x) >> 2 )^( (FT_UInt32)(x) >> 10 )) |
| |
| #define FT_OBJECT_CHECK(o) \ |
| ( FT_OBJECT(o) != NULL && \ |
| FT_OBJECT(o)->clazz != NULL && \ |
| FT_OBJECT(o)->ref_count >= 1 && \ |
| FT_OBJECT(o)->clazz->magic == FT_MAGIC_CLASS ) |
| |
| #define FT_CLASS_CHECK(c) \ |
| ( FT_CLASS(c) != NULL && FT_CLASS(c)->magic == FT_MAGIC_CLASS ) |
| |
| #define FT_ASSERT_IS_CLASS(c) FT_ASSERT( FT_CLASS_CHECK(c) ) |
| |
| /*******************************************************************/ |
| /*******************************************************************/ |
| /***** *****/ |
| /***** *****/ |
| /***** M E T A - C L A S S *****/ |
| /***** *****/ |
| /***** *****/ |
| /*******************************************************************/ |
| /*******************************************************************/ |
| |
| /* forward declaration */ |
| FT_BASE_DEF( FT_Error ) |
| ft_metaclass_init( FT_MetaClass meta, |
| FT_Library library ); |
| |
| /* forward declaration */ |
| FT_BASE_DEF( void ) |
| ft_metaclass_done( FT_MetaClass meta ); |
| |
| |
| /* class type for the meta-class itself */ |
| static const FT_TypeRec ft_meta_class_type = |
| { |
| "FT2.MetaClass", |
| NULL, |
| |
| sizeof( FT_MetaClassRec ), |
| (FT_Object_InitFunc) ft_metaclass_init, |
| (FT_Object_DoneFunc) ft_metaclass_done, |
| |
| sizeof( FT_ClassRec ), |
| (FT_Object_InitFunc) NULL, |
| (FT_Object_DoneFunc) NULL |
| }; |
| |
| |
| |
| |
| /* destroy a given class */ |
| static void |
| ft_class_hnode_destroy( FT_ClassHNode node ) |
| { |
| FT_Class clazz = node->clazz; |
| FT_Memory memory = clazz->memory; |
| |
| if ( clazz->class_done ) |
| clazz->class_done( (FT_Object) clazz ); |
| |
| FT_FREE( clazz ); |
| |
| node->clazz = NULL; |
| node->type = NULL; |
| |
| FT_FREE( node ); |
| } |
| |
| |
| static FT_Int |
| ft_type_equal( FT_Type type1, |
| FT_Type type2 ) |
| { |
| if ( type1 == type2 ) |
| goto Ok; |
| |
| if ( type1 == NULL || type2 == NULL ) |
| goto Fail; |
| |
| /* compare parent types */ |
| if ( type1->super != type2->super ) |
| { |
| if ( type1->super == NULL || |
| type2->super == NULL || |
| !ft_type_equal( type1, type2 ) ) |
| goto Fail; |
| } |
| |
| /* compare type names */ |
| if ( type1->name != type2->name ) |
| { |
| if ( type1->name == NULL || |
| type2->name == NULL || |
| ft_strcmp( type1->name, type2->name ) != 0 ) |
| goto Fail; |
| } |
| |
| /* compare the other type fields */ |
| if ( type1->class_size != type2->class_size || |
| type1->class_init != type2->class_init || |
| type1->class_done != type2->class_done || |
| type1->obj_size != type2->obj_size || |
| type1->obj_init != type2->obj_init || |
| type1->obj_done != type2->obj_done ) |
| goto Fail; |
| |
| Ok: |
| return 1; |
| |
| Fail: |
| return 0; |
| } |
| |
| |
| static FT_Int |
| ft_class_hnode_equal( const FT_ClassHNode node1, |
| const FT_ClassHNode node2 ) |
| { |
| FT_Type type1 = node1->type; |
| FT_Type type2 = node2->type; |
| |
| /* comparing the pointers should work in 99% of cases */ |
| return ( type1 == type2 ) ? 1 : ft_type_equal( type1, type2 ); |
| } |
| |
| |
| FT_BASE_DEF( void ) |
| ft_metaclass_done( FT_MetaClass meta ) |
| { |
| /* clear all classes */ |
| ft_hash_done( &meta->type_to_class, |
| (FT_Hash_ForeachFunc) ft_class_hnode_destroy, |
| NULL ); |
| |
| meta->clazz.object.clazz = NULL; |
| meta->clazz.object.ref_count = 0; |
| meta->clazz.magic = FT_MAGIC_DEATH; |
| } |
| |
| |
| FT_BASE_DEF( FT_Error ) |
| ft_metaclass_init( FT_MetaClass meta, |
| FT_Library library ) |
| { |
| FT_ClassRec* clazz = (FT_ClassRec*) &meta->clazz; |
| |
| /* the meta-class is its OWN class !! */ |
| clazz->object.clazz = (FT_Class) clazz; |
| clazz->object.ref_count = 1; |
| clazz->magic = FT_MAGIC_CLASS; |
| clazz->library = library; |
| clazz->memory = library->memory; |
| clazz->type = &ft_meta_class_type; |
| clazz->info = NULL; |
| |
| clazz->class_done = (FT_Object_DoneFunc) ft_metaclass_done; |
| |
| clazz->obj_size = sizeof( FT_ClassRec ); |
| clazz->obj_init = NULL; |
| clazz->obj_done = NULL; |
| |
| return ft_hash_init( &meta->type_to_class, |
| (FT_Hash_EqualFunc) ft_class_hnode_equal, |
| library->memory ); |
| } |
| |
| |
| /* find or create the class corresponding to a given type */ |
| /* note that this function will retunr NULL in case of */ |
| /* memory overflow */ |
| /* */ |
| static FT_Class |
| ft_metaclass_get_class( FT_MetaClass meta, |
| FT_Type ctype ) |
| { |
| FT_ClassHNodeRec keynode, *node, **pnode; |
| FT_Memory memory; |
| FT_ClassRec* clazz; |
| FT_Class parent; |
| FT_Error error; |
| |
| keynode.hnode.hash = FT_TYPE_HASH( ctype ); |
| keynode.type = ctype; |
| |
| pnode = (FT_ClassHNode*) ft_hash_lookup( &meta->type_to_class, |
| (FT_HashNode) &keynode ); |
| node = *pnode; |
| if ( node != NULL ) |
| { |
| clazz = (FT_ClassRec*) node->clazz; |
| goto Exit; |
| } |
| |
| memory = FT_CLASS__MEMORY(meta); |
| clazz = NULL; |
| parent = NULL; |
| if ( ctype->super != NULL ) |
| { |
| FT_ASSERT( ctype->super->class_size <= ctype->class_size ); |
| FT_ASSERT( ctype->super->obj_size <= ctype->obj_size ); |
| |
| parent = ft_metaclass_get_class( meta, ctype->super ); |
| } |
| |
| if ( !FT_NEW( node ) ) |
| { |
| if ( !FT_ALLOC( clazz, ctype->class_size ) ) |
| { |
| if ( parent ) |
| FT_MEM_COPY( (FT_ClassRec*)clazz, parent, parent->type->class_size ); |
| |
| clazz->object.clazz = (FT_Class) meta; |
| clazz->object.ref_count = 1; |
| |
| clazz->memory = memory; |
| clazz->library = FT_CLASS__LIBRARY(meta); |
| clazz->super = parent; |
| clazz->type = ctype; |
| clazz->info = NULL; |
| clazz->magic = FT_MAGIC_CLASS; |
| |
| clazz->class_done = ctype->class_done; |
| clazz->obj_size = ctype->obj_size; |
| clazz->obj_init = ctype->obj_init; |
| clazz->obj_done = ctype->obj_done; |
| |
| if ( parent ) |
| { |
| if ( clazz->class_done == NULL ) |
| clazz->class_done = parent->class_done; |
| |
| if ( clazz->obj_init == NULL ) |
| clazz->obj_init = parent->obj_init; |
| |
| if ( clazz->obj_done == NULL ) |
| clazz->obj_done = parent->obj_done; |
| } |
| |
| /* find class initializer, if any */ |
| { |
| FT_Type ztype = ctype; |
| FT_Object_InitFunc cinit = NULL; |
| |
| do |
| { |
| cinit = ztype->class_init; |
| if ( cinit != NULL ) |
| break; |
| |
| ztype = ztype->super; |
| } |
| while ( ztype != NULL ); |
| |
| /* then call it when needed */ |
| if ( cinit != NULL ) |
| error = cinit( (FT_Object) clazz, NULL ); |
| } |
| } |
| |
| if (error) |
| { |
| if ( clazz ) |
| { |
| /* we always call the class destructor when */ |
| /* an error was detected in the constructor !! */ |
| if ( clazz->class_done ) |
| clazz->class_done( (FT_Object) clazz ); |
| |
| FT_FREE( clazz ); |
| } |
| FT_FREE( node ); |
| } |
| } |
| |
| Exit: |
| return (FT_Class) clazz; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| FT_BASE_DEF( FT_Int ) |
| ft_object_check( FT_Pointer obj ) |
| { |
| return FT_OBJECT_CHECK(obj); |
| } |
| |
| |
| FT_BASE_DEF( FT_Int ) |
| ft_object_is_a( FT_Pointer obj, |
| FT_Class clazz ) |
| { |
| if ( FT_OBJECT_CHECK(obj) ) |
| { |
| FT_Class c = FT_OBJECT__CLASS(obj); |
| |
| do |
| { |
| if ( c == clazz ) |
| return 1; |
| |
| c = c->super; |
| } |
| while ( c == NULL ); |
| |
| return (clazz == NULL); |
| } |
| return 0; |
| } |
| |
| |
| FT_BASE_DEF( FT_Error ) |
| ft_object_create( FT_Object *pobject, |
| FT_Class clazz, |
| FT_Pointer init_data ) |
| { |
| FT_Memory memory; |
| FT_Error error; |
| FT_Object obj; |
| |
| FT_ASSERT_IS_CLASS(clazz); |
| |
| memory = FT_CLASS__MEMORY(clazz); |
| if ( !FT_ALLOC( obj, clazz->obj_size ) ) |
| { |
| obj->clazz = clazz; |
| obj->ref_count = 1; |
| |
| if ( clazz->obj_init ) |
| { |
| error = clazz->obj_init( obj, init_data ); |
| if ( error ) |
| { |
| /* IMPORTANT: call the destructor when an error */ |
| /* was detected in the constructor !! */ |
| if ( clazz->obj_done ) |
| clazz->obj_done( obj ); |
| |
| FT_FREE( obj ); |
| } |
| } |
| } |
| *pobject = obj; |
| return error; |
| } |
| |
| |
| FT_BASE_DEF( FT_Class ) |
| ft_class_find_by_type( FT_Type type, |
| FT_Library library ) |
| { |
| FT_MetaClass meta = &library->meta_class; |
| |
| return ft_metaclass_get_class( meta, type ); |
| } |
| |
| |
| FT_BASE_DEF( FT_Error ) |
| ft_object_create_from_type( FT_Object *pobject, |
| FT_Type type, |
| FT_Pointer init_data, |
| FT_Library library ) |
| { |
| FT_Class clazz; |
| FT_Error error; |
| |
| clazz = ft_class_find_by_type( type, library ); |
| if ( clazz ) |
| error = ft_object_create( pobject, clazz, init_data ); |
| else |
| { |
| *pobject = NULL; |
| error = FT_Err_Out_Of_Memory; |
| } |
| |
| return error; |
| } |