|
|
|
@ -1,13 +1,16 @@ |
|
|
|
|
#include "ftobject.h" |
|
|
|
|
#include "fthash.h" |
|
|
|
|
#include <ft2build.h> |
|
|
|
|
#include FT_INTERNAL_OBJECT_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) != NULL && \
|
|
|
|
|
FT_OBJECT(o)->clazz != NULL && \
|
|
|
|
|
FT_OBJECT(o)->ref_count >= 1 && \
|
|
|
|
|
FT_OBJECT(o)->clazz->magic == FT_MAGIC_CLASS ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -21,28 +24,6 @@ |
|
|
|
|
/*******************************************************************/ |
|
|
|
|
/*******************************************************************/ |
|
|
|
|
|
|
|
|
|
/* we use a dynamic hash table to map types to classes */ |
|
|
|
|
/* this structure defines the layout of each node of */ |
|
|
|
|
/* this table */ |
|
|
|
|
typedef struct FT_ClassHNodeRec_ |
|
|
|
|
{ |
|
|
|
|
FT_HashNodeRec hnode; |
|
|
|
|
FT_Type ctype; |
|
|
|
|
FT_Class clazz; |
|
|
|
|
|
|
|
|
|
} FT_ClassHNodeRec, *FT_ClassHNode; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* the meta class contains a type -> class mapping */ |
|
|
|
|
/* and owns all class objects.. */ |
|
|
|
|
/* */ |
|
|
|
|
typedef struct FT_MetaClassRec_ |
|
|
|
|
{ |
|
|
|
|
FT_ClassRec clazz; |
|
|
|
|
FT_HashRec type_to_class; |
|
|
|
|
|
|
|
|
|
} FT_MetaClassRec, *FT_MetaClass; |
|
|
|
|
|
|
|
|
|
/* forward declaration */ |
|
|
|
|
static const FT_TypeRec ft_meta_class_type; |
|
|
|
|
|
|
|
|
@ -51,12 +32,12 @@ |
|
|
|
|
static void |
|
|
|
|
ft_class_hnode_destroy( FT_ClassHNode node ) |
|
|
|
|
{ |
|
|
|
|
FT_Clazz clazz = node->clazz; |
|
|
|
|
FT_Class clazz = node->clazz; |
|
|
|
|
FT_Memory memory = clazz->memory; |
|
|
|
|
FT_Type ctype = clazz->type; |
|
|
|
|
|
|
|
|
|
if ( ctype->class_done ) |
|
|
|
|
ctype->class_done( clazz ); |
|
|
|
|
if ( clazz->class_done ) |
|
|
|
|
clazz->class_done( (FT_Object) clazz ); |
|
|
|
|
|
|
|
|
|
FT_FREE( clazz ); |
|
|
|
|
|
|
|
|
@ -68,32 +49,81 @@ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static FT_Int |
|
|
|
|
ft_class_hnode_compare( const FT_ClassHNode node1, |
|
|
|
|
const FT_ClassHNode node2 ) |
|
|
|
|
ft_type_equal( FT_Type type1, |
|
|
|
|
FT_Type type2 ) |
|
|
|
|
{ |
|
|
|
|
return ( node1->type == node2->type ); |
|
|
|
|
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_compare( 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 void |
|
|
|
|
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 objects */ |
|
|
|
|
/* clear all classes */ |
|
|
|
|
ft_hash_done( &meta->type_to_class, |
|
|
|
|
(FT_Hash_ForeachFunc) ft_class_destroy, |
|
|
|
|
(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; |
|
|
|
|
meta->clazz.object.clazz = NULL; |
|
|
|
|
meta->clazz.object.ref_count = 0; |
|
|
|
|
meta->clazz.magic = FT_MAGIC_DEATH; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
FT_BASE_DEF( FT_Error ) |
|
|
|
|
ft_metaclass_init( FT_MetaClass meta, |
|
|
|
|
FT_Library library ) |
|
|
|
|
{ |
|
|
|
|
FT_ClassRec* clazz = meta->clazz; |
|
|
|
|
FT_ClassRec* clazz = (FT_ClassRec*) &meta->clazz; |
|
|
|
|
|
|
|
|
|
/* the meta-class is its OWN class !! */ |
|
|
|
|
clazz->object.clazz = (FT_Class) clazz; |
|
|
|
@ -101,49 +131,130 @@ |
|
|
|
|
clazz->magic = FT_MAGIC_CLASS; |
|
|
|
|
clazz->library = library; |
|
|
|
|
clazz->memory = library->memory; |
|
|
|
|
clazz->type = &ft_metaclass_type; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
ft_hash_init( &meta->type_to_class, |
|
|
|
|
(FT_Hash_CompareFunc) ft_class_hnode_compare, |
|
|
|
|
library->memory ); |
|
|
|
|
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_UInt32)( ctype >> 2 ); |
|
|
|
|
keynode.type = type; |
|
|
|
|
keynode.hnode.hash = FT_TYPE_HASH( ctype ); |
|
|
|
|
keynode.type = ctype; |
|
|
|
|
|
|
|
|
|
pnode = (FT_ClassHNode) ft_hash_lookup( &meta->type_to_class, |
|
|
|
|
&noderec ); |
|
|
|
|
pnode = (FT_ClassHNode*) ft_hash_lookup( &meta->type_to_class, |
|
|
|
|
(FT_HashNode) &keynode ); |
|
|
|
|
node = *pnode; |
|
|
|
|
if ( node != NULL ) |
|
|
|
|
return node->clazz; |
|
|
|
|
{ |
|
|
|
|
clazz = (FT_ClassRec*) node->clazz; |
|
|
|
|
goto Exit; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
memory = FT_CLASS__MEMORY(meta); |
|
|
|
|
node = FT_MEM_SAFE_ALLOC( sizeof(*node) ); |
|
|
|
|
if ( node != NULL ) |
|
|
|
|
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 ) ) |
|
|
|
|
{ |
|
|
|
|
FT_ClassRec* clazz; |
|
|
|
|
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 ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
clazz = FT_MEM_SAFE_ALLOC( ctype->class_size ); |
|
|
|
|
if ( 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 ); |
|
|
|
|
FT_XTHROW( FT_Err_Out_Of_Memory ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Exit: |
|
|
|
|
return (FT_Class) clazz; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -157,8 +268,8 @@ |
|
|
|
|
(FT_Object_DoneFunc) ft_metaclass_done, |
|
|
|
|
|
|
|
|
|
sizeof( FT_ClassRec ), |
|
|
|
|
(FT_Object_InitFunc) ft_class_init, |
|
|
|
|
(FT_Object_DoneFunc) ft_class_done |
|
|
|
|
(FT_Object_InitFunc) NULL, |
|
|
|
|
(FT_Object_DoneFunc) NULL |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -204,90 +315,69 @@ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* the cleanup routine for all objects */ |
|
|
|
|
static void |
|
|
|
|
ft_object_cleanup( FT_Object object ) |
|
|
|
|
{ |
|
|
|
|
FT_Memory memory = FT_OBJECT__MEMORY(object); |
|
|
|
|
FT_Class clazz = FT_OBJECT__CLASS(object); |
|
|
|
|
|
|
|
|
|
if ( clazz->obj_done ) |
|
|
|
|
clazz->obj_done( object ); |
|
|
|
|
|
|
|
|
|
FT_FREE( object ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FT_BASE_DEF( FT_Object ) |
|
|
|
|
ft_object_new( FT_Class clazz, |
|
|
|
|
FT_Pointer init_data ) |
|
|
|
|
{ |
|
|
|
|
FT_Memory memory; |
|
|
|
|
FT_Object obj; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FT_ASSERT_IS_CLASS(clazz); |
|
|
|
|
|
|
|
|
|
memory = FT_CLASS__MEMORY(clazz); |
|
|
|
|
obj = ft_mem_alloc( clazz->obj_size, memory ); |
|
|
|
|
obj->clazz = clazz; |
|
|
|
|
obj->ref_count = 1; |
|
|
|
|
|
|
|
|
|
if ( clazz->obj_init ) |
|
|
|
|
{ |
|
|
|
|
FT_CleanupStack stack = FT_MEMORY__CLEANUP(memory); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ft_cleanup_push( stack, obj, (FT_CleanupFunc) ft_object_cleanup, NULL ); |
|
|
|
|
|
|
|
|
|
clazz->obj_init( obj, init_data ); |
|
|
|
|
|
|
|
|
|
ft_cleanup_pop( stack, obj, 0 ); |
|
|
|
|
} |
|
|
|
|
return obj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FT_BASE_DEF( void ) |
|
|
|
|
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(memory); |
|
|
|
|
obj = ft_mem_alloc( clazz->obj_size, memory ); |
|
|
|
|
obj->clazz = clazz; |
|
|
|
|
obj->ref_count = 1; |
|
|
|
|
*pobject = obj; |
|
|
|
|
memory = FT_CLASS__MEMORY(memory); |
|
|
|
|
if ( !FT_ALLOC( obj, clazz->obj_size ) ) |
|
|
|
|
{ |
|
|
|
|
obj->clazz = clazz; |
|
|
|
|
obj->ref_count = 1; |
|
|
|
|
|
|
|
|
|
if ( clazz->obj_init ) |
|
|
|
|
clazz->obj_init( obj, init_data ); |
|
|
|
|
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_Memory memory ) |
|
|
|
|
ft_class_find_by_type( FT_Type type, |
|
|
|
|
FT_Library library ) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FT_MetaClass meta = &library->meta_class; |
|
|
|
|
|
|
|
|
|
FT_BASE_DEF( FT_Class ) |
|
|
|
|
ft_class_find_by_name( FT_CString class_name, |
|
|
|
|
FT_Memory memory ); |
|
|
|
|
return ft_metaclass_get_class( meta, type ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FT_BASE_DEF( FT_Object ) |
|
|
|
|
ft_object_new_from_type( FT_Type type, |
|
|
|
|
FT_Pointer data, |
|
|
|
|
FT_Memory memory ); |
|
|
|
|
|
|
|
|
|
FT_BASE_DEF( void ) |
|
|
|
|
FT_BASE_DEF( FT_Error ) |
|
|
|
|
ft_object_create_from_type( FT_Object *pobject, |
|
|
|
|
FT_Type type, |
|
|
|
|
FT_Pointer init_data, |
|
|
|
|
FT_Memory memory ); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|