diff --git a/ChangeLog b/ChangeLog index ebd95abb1..7a008c778 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,35 @@ * src/sfnt/ttload.c (TT_Load_Names): applied a small work-around to manage fonts containing a broken name table (e.g. "hya6gb.ttf") + * src/sfnt/ttcmap0.c (tt_cmap4_validate): + fixed over-restrictive validation test. the charmap validator + now accepts overlapping ranges in format 4 charmaps. + + * src/sfnt/ttcmap0.c (tt_cmap4_char_index): + switched to a binary search algorithm. Certain fonts contain + more than 170 distinct segments !! + + * include/freetype/config/ftstdlib.h: adding an alias for the + 'exit' function. This will be used in the near future to panic + in case of un-expected exception (which shouldn't happen in + theory, but as everyone knows, shit happens :-) ) + + * include/freetype/internal/fthash.h, src/base/fthash.c: + adding a generic implementation of dynamic hash tables using + linear algorithm (to get rid of 'stalls' during resizes). This + will be used in the future in at least three parts of the + library: the cache sub-system, the object sub-system and + the memory debugger. + + * include/freetype/internal/ftcore.h: added this header file to + group all new definitions related to exception handling and + memory management. It's very likely that this file will disappear + or be renamed in the future.. + + * include/freetype/internal/ftobject.h, include/freetype/ftsysmem.h: + adding comments to better explain the object sub-system as well + as the new memory manager interface. + 2002-04-30 Wenlin Institute (Tom Bishop) * src/base/ftmac.c (p2c_str): Removed. diff --git a/include/freetype/ftsysio.h b/include/freetype/ftsysio.h index da2466290..f28d0ed0c 100644 --- a/include/freetype/ftsysio.h +++ b/include/freetype/ftsysio.h @@ -1,42 +1,146 @@ #ifndef __FT_SYSTEM_IO_H__ #define __FT_SYSTEM_IO_H__ + /******************************************************************** + * + * designing custom streams is a bit different now + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + #include #include FT_INTERNAL_OBJECT_H FT_BEGIN_HEADER - /* handle to a FT_Stream object */ + /******************************************************************** + * + * @type: FT_Stream + * + * @description: + * handle to an input stream object. These are also @FT_Object handles + */ typedef struct FT_StreamRec_* FT_Stream; - /* handle to the FT_Stream class */ + + /******************************************************************** + * + * @type: FT_Stream_Class + * + * @description: + * opaque handle to a @FT_Stream_ClassRec class structure describing + * the methods of input streams + */ typedef const struct FT_Stream_ClassRec_* FT_Stream_Class; - /* a method used to read data from a stream into a buffer */ + + /******************************************************************** + * + * @functype: FT_Stream_ReadFunc + * + * @description: + * a method used to read bytes from an input stream into memory + * + * @input: + * stream :: target stream handle + * buffer :: target buffer address + * size :: number of bytes to read + * + * @return: + * number of bytes effectively read. Must be <= 'size'. + */ typedef FT_ULong (*FT_Stream_ReadFunc)( FT_Stream stream, FT_Byte* buffer, FT_ULong size ); - /* a method used to seek to a new position within a stream */ - /* position is always relative to the start */ + + /******************************************************************** + * + * @functype: FT_Stream_SeekFunc + * + * @description: + * a method used to seek to a new position within a stream + * + * @input: + * stream :: target stream handle + * pos :: new read position, from start of stream + * + * @return: + * error code. 0 means success + */ typedef FT_Error (*FT_Stream_SeekFunc)( FT_Stream stream, FT_ULong pos ); - /* the stream class structure + some useful macros */ + /******************************************************************** + * + * @struct: FT_Stream_ClassRec + * + * @description: + * a structure used to describe an input stream class + * + * @input: + * clazz :: root @FT_ClassRec fields + * stream_read :: stream byte read method + * stream_seek :: stream seek method + */ typedef struct FT_Stream_ClassRec_ { FT_ClassRec clazz; FT_Stream_ReadFunc stream_read; FT_Stream_SeekFunc stream_seek; - + } FT_Stream_ClassRec; -#define FT_STREAM_CLASS(x) ((FT_Stream_Class)(x)) + /* */ +#define FT_STREAM_CLASS(x) ((FT_Stream_Class)(x)) #define FT_STREAM_CLASS__READ(x) FT_STREAM_CLASS(x)->stream_read #define FT_STREAM_CLASS__SEEK(x) FT_STREAM_CLASS(x)->stream_seek; - /* the base FT_Stream object structure */ + /******************************************************************** + * + * @struct: FT_StreamRec + * + * @description: + * the input stream object structure. See @FT_Stream_ClassRec for + * its class descriptor + * + * @fields: + * object :: root @FT_ObjectRec fields + * size :: size of stream in bytes (0 if unknown) + * pos :: current position within stream + * base :: for memory-based streams, the address of the stream's + * first data byte in memory. NULL otherwise + * + * cursor :: the current cursor position within an input stream + * frame. Only valid within a FT_FRAME_ENTER .. FT_FRAME_EXIT + * block; NULL otherwise + * + * limit :: the current frame limit within a FT_FRAME_ENTER .. + * FT_FRAME_EXIT block. NULL otherwise + */ typedef struct FT_StreamRec_ { FT_ObjectRec object; @@ -57,12 +161,23 @@ FT_BEGIN_HEADER #define FT_STREAM_IS_BASED(x) ( FT_STREAM(x)->base != NULL ) + /* */ + /* create new memory-based stream */ + FT_BASE( FT_Error ) ft_stream_new_memory( const FT_Byte* stream_base, + FT_ULong stream_size, + FT_Memory memory, + FT_Stream *astream ); -#if 0 -#endif + FT_BASE( FT_Error ) ft_stream_new_iso( const char* pathanme, + FT_Memory memory, + FT_Stream *astream ); - /* */ + + /* handle to default stream class implementation for a given build */ + /* this is used by "FT_New_Face" */ + /* */ + FT_APIVAR( FT_Type ) ft_stream_default_type; FT_END_HEADER diff --git a/include/freetype/ftsysmem.h b/include/freetype/ftsysmem.h index cf58e8a20..70fe6b520 100644 --- a/include/freetype/ftsysmem.h +++ b/include/freetype/ftsysmem.h @@ -5,35 +5,131 @@ FT_BEGIN_HEADER - /* handle to memory structure */ + /*********************************************************************** + * + * @type: FT_Memory + * + * @description: + * opaque handle to a memory manager handle. Note that since FreeType + * 2.2, the memory manager structure FT_MemoryRec is hidden to client + * applications. + * + * however, you can still define custom allocators easily using the + * @ft_memory_new API + */ typedef struct FT_MemoryRec_* FT_Memory; - /* a function used to allocate a new block of memory from a heap */ + + /*********************************************************************** + * + * @functype: FT_Memory_AllocFunc + * + * @description: + * a function used to allocate a block of memory. + * + * @input: + * size :: size of blocks in bytes. Always > 0 !! + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + * + * @return: + * address of new block. NULL in case of memory exhaustion + */ typedef FT_Pointer (*FT_Memory_AllocFunc)( FT_ULong size, - FT_Memory memory ); - - /* a function used to free a block of memory */ + FT_Pointer mem_data ); + + + /*********************************************************************** + * + * @functype: FT_Memory_FreeFunc + * + * @description: + * a function used to release a block of memory created through + * @FT_Memory_AllocFunc or @FT_Memory_ReallocFunc + * + * @input: + * block :: address of target memory block. cannot be NULL !! + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + */ typedef void (*FT_Memory_FreeFunc) ( FT_Pointer block, - FT_Memory memory ); - - /* a function used to reallocate a given memory block to a new size */ + FT_Pointer mem_data ); + + + /*********************************************************************** + * + * @functype: FT_Memory_ReallocFunc + * + * @description: + * a function used to reallocate a memory block. + * + * @input: + * block :: address of target memory block. cannot be NULL !! + * new_size :: new requested size in bytes + * cur_size :: current block size in bytes + * mem_data :: memory-manager specific optional argument + * (see @ft_memory_new) + */ typedef FT_Pointer (*FT_Memory_ReallocFunc)( FT_Pointer block, FT_ULong new_size, FT_ULong cur_size, - FT_Memory memory ); + FT_Pointer mem_data ); + - /* a function called to allocate a new structure of 'size' bytes that */ - /* will be used for a new FT_Memory object.. */ - /* */ + /*********************************************************************** + * + * @functype: FT_Memory_CreateFunc + * + * @description: + * a function used to create a @FT_Memory object to model a + * memory manager + * + * @input: + * size :: size of memory manager structure in bytes + * init_data :: optional initialisation argument + * + * @output: + * amem_data :: memory-manager specific argument to block management + * routines. + * + * @return: + * handle to new memory manager object. NULL in case of failure + */ typedef FT_Pointer (*FT_Memory_CreateFunc)( FT_UInt size, - FT_Pointer init_data ); + FT_Pointer init_data, + FT_Pointer *amem_data ); + - /* a function used to destroy a FT_Memory object */ - typedef void (*FT_Memory_DestroyFunc)( FT_Memory memory ); + /*********************************************************************** + * + * @functype: FT_Memory_DestroyFunc + * + * @description: + * a function used to destroy a given @FT_Memory manager + * + * @input: + * memory :: target memory manager handle + * mem_data :: option manager-specific argument + */ + typedef void (*FT_Memory_DestroyFunc)( FT_Memory memory, + FT_Pointer mem_data ); - /* a structure holding the functions used to describe a given FT_Memory */ - /* implementation.. */ - /* */ + + /*********************************************************************** + * + * @struct: FT_Memory_FuncsRec + * + * @description: + * a function used to hold all methods of a given memory manager + * implementation. + * + * @fields: + * mem_alloc :: block allocation routine + * mem_free :: block release routine + * mem_realloc :: block re-allocation routine + * mem_create :: manager creation routine + * mem_destroy :: manager destruction routine + */ typedef struct FT_Memory_FuncsRec_ { FT_Memory_AllocFunc mem_alloc; @@ -41,24 +137,53 @@ FT_BEGIN_HEADER FT_Memory_ReallocFunc mem_realloc; FT_Memory_CreateFunc mem_create; FT_Memory_DestroyFunc mem_destroy; - + } FT_Memory_FuncsRec, *FT_Memory_Funcs; - /* a function used to create a new custom FT_Memory object */ - /* */ - FT_BASE_DEF( FT_Memory ) + /*********************************************************************** + * + * @type: FT_Memory_Funcs + * + * @description: + * a pointer to a constant @FT_Memory_FuncsRec structure used to + * describe a given memory manager implementation. + */ + typedef const FT_Memory_FuncsRec* FT_Memory_Funcs; + + + /*********************************************************************** + * + * @function: ft_memory_new + * + * @description: + * create a new memory manager, given a set of memory methods + * + * @input: + * mem_funcs :: handle to memory manager implementation descriptor + * mem_init_data :: optional initialisation argument, passed to + * @FT_Memory_CreateFunc + * + * @return: + * new memory manager handle. NULL in case of failure + */ + FT_BASE( FT_Memory ) ft_memory_new( FT_Memory_Funcs mem_funcs, FT_Pointer mem_init_data ); - /* a function used to destroy a custom FT_Memory object */ - FT_BASE_DEF( void ) - ft_memory_destroy( FT_Memory memory ); - /* a pointer to the default memory functions used by FreeType in a */ - /* given build. By default, uses the ISO C malloc/free/realloc */ - /* */ - FT_APIVAR( const FT_MemoryFuncs ) ft_memory_funcs_default; + /*********************************************************************** + * + * @function: ft_memory_destroy + * + * @description: + * destroy a given memory manager + * + * @input: + * memory :: handle to target memory manager + */ + FT_BASE( void ) + ft_memory_destroy( FT_Memory memory ); /* */ diff --git a/include/freetype/internal/ftcore.h b/include/freetype/internal/ftcore.h new file mode 100644 index 000000000..3c3c94ff3 --- /dev/null +++ b/include/freetype/internal/ftcore.h @@ -0,0 +1,185 @@ +#ifndef __FT_CORE_H__ +#define __FT_CORE_H__ + +#include +#include FT_TYPES_H +#include FT_SYSTEM_MEMORY_H + +FT_BEGIN_HEADER + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** C L E A N U P S T A C K *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + + /************************************************************************ + * + * @functype: FT_CleanupFunc + * + * @description: + * a function used to cleanup a given item on the cleanup stack + * + * @input: + * item :: target item pointer + * item_data :: optional argument to cleanup routine + */ + typedef void (*FT_CleanupFunc)( FT_Pointer item, + FT_Pointer item_data ); + + + + /************************************************************************ + * + * @type: FT_XHandler + * + * @description: + * handle to an exception-handler structure for the FreeType + * exception sub-system + * + * @note: + * exception handlers are allocated on the stack within a + * @FT_XTRY macro. Do not try to access them directly. + */ + typedef struct FT_XHandlerRec_* FT_XHandler; + + +/* the size of a cleanup chunk in bytes is FT_CLEANUP_CHUNK_SIZE*12 + 4 */ +/* this must be a small power of 2 whenever possible.. */ +/* */ +/* with a value of 5, we have a byte size of 64 bytes per chunk.. */ +/* */ +#define FT_CLEANUP_CHUNK_SIZE 5 + + + + typedef struct FT_CleanupItemRec_ + { + FT_Pointer item; + FT_CleanupFunc item_func; + FT_Pointer item_data; + + } FT_CleanupItemRec; + + + typedef struct FT_CleanupChunkRec_* FT_CleanupChunk; + + typedef struct FT_CleanupChunkRec_ + { + FT_CleanupChunk link; + FT_CleanupItemRec items[ FT_CLEANUP_CHUNK_SIZE ]; + + } FT_CleanupChunkRec; + + + typedef struct FT_CleanupStackRec_ + { + FT_CleanupItem top; + FT_CleanupItem limit; + FT_CleanupChunk chunk; + FT_CleanupChunkRec chunk_0; /* avoids stupid dynamic allocation */ + FT_Memory memory; + + } FT_CleanupStackRec, *FT_CleanupStack; + + + FT_BASE( void ) + ft_cleanup_stack_push( FT_CleanupStack stack, + FT_Pointer item, + FT_CleanupFunc item_func, + FT_Pointer item_data ); + + FT_BASE( void ) + ft_cleanup_stack_pop( FT_CleanupStack stack, + FT_Int destroy ); + + FT_BASE( FT_CleanupItem ) + ft_cleanup_stack_peek( FT_CleanupStack stack ); + + FT_BASE( void ) + ft_cleanup_throw( FT_CleanupStack stack, + FT_Error error ); + + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** M E M O R Y M A N A G E R *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + typedef struct FT_MemoryRec_ + { + FT_Memory_AllocFunc mem_alloc; /* shortcut to funcs->mem_alloc */ + FT_Memory_FreeFunc mem_free; /* shortcut to funcs->mem_free */ + FT_Pointer mem_data; + const FT_Memory_Funcs mem_funcs; + + FT_CleanupStackRec cleanup_stack; + FT_Pointer meta_class; + + } FT_MemoryRec; + + +#define FT_MEMORY(x) ((FT_Memory)(x)) +#define FT_MEMORY__ALLOC(x) FT_MEMORY(x)->mem_alloc +#define FT_MEMORY__FREE(x) FT_MEMORY(x)->mem_free +#define FT_MEMORY__REALLOC(x) FT_MEMORY(x)->mem_funcs->mem_realloc +#define FT_MEMORY__CLEANUP(x) (&FT_MEMORY(x)->cleanup_stack) +#define FT_MEMORY__META_CLASS(x) ((FT_MetaClass)(FT_MEMORY(x)->meta_class)) + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** E X C E P T I O N H A N D L I N G *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + + /************************************************************************ + * + * @struct: FT_XHandlerRec + * + * @description: + * exception handler structure + * + * @fields: + * previous :: previous handler in chain. + * jum_buffer :: processor state used by setjmp/longjmp to implement + * exception control transfer + * error :: exception error code + * mark :: top of cleanup stack when @FT_XTRY is used + */ + typedef struct FT_XHandlerRec_ + { + FT_XHandler previous; + ft_jmp_buf jump_buffer; + volatile FT_Error error; + FT_Pointer mark; + + } FT_XHandlerRec; + + FT_BASE( void ) + ft_xhandler_enter( FT_XHandler xhandler, + FT_Memory memory ); + + FT_BASE( void ) + ft_xhandler_exit( FT_XHandler xhandler ); + + + + + + + + +FT_END_HEADER + +#endif /* __FT_CORE_H__ */ diff --git a/include/freetype/internal/ftexcept.h b/include/freetype/internal/ftexcept.h index f3c770e5e..f5495e56c 100644 --- a/include/freetype/internal/ftexcept.h +++ b/include/freetype/internal/ftexcept.h @@ -6,21 +6,12 @@ FT_BEGIN_HEADER - typedef struct FT_XHandlerRec_* FT_XHandler; - - typedef struct FT_XHandlerRec_ - { - FT_XHandler previous; - ft_jmp_buf jump_buffer; - volatile FT_Error error; - FT_Pointer cleanup; - - } FT_XHandlerRec; - typedef void (*FT_CleanupFunc)( FT_Pointer item, - FT_Pointer item_data ); + /* I can't find a better place for this for now */ +<<<<<<< ftexcept.h +======= /* the size of a cleanup chunk in bytes is FT_CLEANUP_CHUNK_SIZE*12 + 4 */ /* this must be a small power of 2 whenever possible.. */ @@ -85,6 +76,7 @@ FT_BEGIN_HEADER ft_cleanup_throw( FT_CleanupStack stack, FT_Error error ); +>>>>>>> 1.2 FT_END_HEADER #endif /* __FT_EXCEPT_H__ */ diff --git a/include/freetype/internal/fthash.h b/include/freetype/internal/fthash.h new file mode 100644 index 000000000..551f9f5dc --- /dev/null +++ b/include/freetype/internal/fthash.h @@ -0,0 +1,484 @@ +/****************************************************************** + * + * fthash.h - fast dynamic hash tables + * + * Copyright 2002 by + * David Turner, Robert Wilhelm, and Werner Lemberg + * + * This file is part of the FreeType project, and may only be used, + * modified, and distributed under the terms of the FreeType project + * license, LICENSE.TXT. By continuing to use, modify, or distribute + * this file you indicate that you have read the license and + * understand and accept it fully. + * + * + * This header is used to define dynamic hash tables as described + * by the article "Main-Memory Linear Hashing - Some Enhancements + * of Larson's Algorithm" by Mikael Petterson. + * + * Basically, linear hashing prevents big "stalls" during + * resizes of the buckets array by only splitting one bucket + * at a time. This ensures excellent response time even when + * the table is frequently resized.. + * + * + * Note that the use of the FT_Hash type is rather unusual in order + * to be as generic and efficient as possible. See the comments in the + * following definitions for more details. + */ + +#ifndef __FT_HASH_H__ +#define __FT_HASH_H__ + +#include +#include FT_TYPES_H + +FT_BEGIN_HEADER + + /*********************************************************** + * + * @type: FT_Hash + * + * @description: + * handle to a @FT_HashRec structure used to model a + * dynamic hash table + */ + typedef struct FT_HashRec_* FT_Hash; + + + /*********************************************************** + * + * @type: FT_HashNode + * + * @description: + * handle to a @FT_HashNodeRec structure used to model a + * single node of a hash table + */ + typedef struct FT_HashNodeRec_* FT_HashNode; + + + /*********************************************************** + * + * @type: FT_Hash_CompareFunc + * + * @description: + * a function used to compare two nodes of the hash table + * + * @input: + * node1 :: handle to first node + * node2 :: handle to second node + * + * @return: + * 1 iff the 'keys' in 'node1' and 'node2' are identical. + * 0 otherwise. + */ + typedef FT_Int (*FT_Hash_CompareFunc)( const FT_HashNode node1, + const FT_HashNode node2 ); + + + /*********************************************************** + * + * @struct: FT_HashRec + * + * @description: + * a structure used to model a dynamic hash table. + * + * @fields: + * memory :: memory manager used to allocate + * the buckets array and the hash nodes + * + * buckets :: array of hash buckets + * + * node_size :: size of node in bytes + * node_compare :: a function used to compare two nodes + * node_hash :: a function used to compute the hash + * value of a given node + * p :: + * mask :: + * slack :: + * + * @note: + * 'p', 'mask' and 'slack' are control values managed by + * the hash table. Do not try to interpret them directly. + * + * You can grab the hash table size by calling + * '@ft_hash_get_size'. + */ + typedef struct FT_HashRec_ + { + FT_Memory memory; + FT_HashNode* buckets; + FT_UInt p; + FT_UInt mask; /* really maxp-1 */ + FT_UInt slack; + + + FT_UInt node_size; + FT_Hash_CompareFunc node_compare; + FT_Hash_ComputeFunc node_hash; + + } FT_HashRec, *FT_Hash; + + + /*********************************************************** + * + * @struct: FT_HashNodeRec + * + * @description: + * a structure used to model the root fields of a dynamic + * hash table node. + * + * it's up to client applications to "sub-class" this + * structure to add relevant (key,value) definitions + * + * @fields: + * link :: pointer to next node in bucket's collision list + * hash :: 32-bit hash value for this node + * + * @note: + * it's up to client applications to "sub-class" this structure + * to add relevant (key,value) type definitions. For example, + * if we want to build a "string -> int" mapping, we could use + * something like: + * + * { + * typedef struct MyNodeRec_ + * { + * FT_HashNodeRec hnode; + * const char* key; + * int value; + * + * } MyNodeRec, *MyNode; + * } + * + */ + typedef struct FT_HashNodeRec_ + { + FT_HashNode link; + FT_UInt32 hash; + + } FT_HashNodeRec; + + + /**************************************************************** + * + * @function: ft_hash_init + * + * @description: + * initialize a dynamic hash table + * + * @input: + * table :: handle to target hash table structure + * compare :: node comparison function + * memory :: memory manager handle used to allocate the + * buckets array within the hash table + * + * @note: + * the node comparison function should only compare node _keys_ + * and ignore values !! with good hashing computation (which the + * user must perform itself), the comparison function should be + * pretty selfom called. + * + * here is a simple example: + * + * { + * static int my_compare( const MyNode node1, + * const MyNode node2 ) + * { + * // compare keys of 'node1' and 'node2' + * return strcmp( node1->key, node2->key ); + * } + * + * .... + * + * ft_hash_init( &hash, (FT_Hash_CompareFunc) my_compare, memory ); + * .... + * } + */ + FT_BASE( void ) + ft_hash_init( FT_Hash table, + FT_Hash_CompareFunc compare, + FT_Memory memory ); + + + /**************************************************************** + * + * @function: ft_hash_lookup + * + * @description: + * search a hash table to find a node corresponding to a given + * key. + * + * @input: + * table :: handle to target hash table structure + * keynode :: handle to a reference hash node that will be + * only used for key comparisons with the table's + * elements + * + * @return: + * a pointer-to-hash-node value, which must be used as followed: + * + * - if '*result' is NULL, the key wasn't found in the hash + * table. The value of 'result' can be used to add new elements + * through @ft_hash_add however.. + * + * - if '*result' is not NULL, it's a handle to the first table + * node that corresponds to the search key. The value of 'result' + * can be used to remove this element through @ft_hash_remove + * + * @note: + * here is an example: + * + * { + * // maps a string to an integer with a hash table + * // returns -1 in case of failure + * // + * int my_lookup( FT_Hash table, + * const char* key ) + * { + * MyNode* pnode; + * MyNodeRec noderec; + * + * // set-up key node. It's 'hash' and 'key' fields must + * // be set correctly.. we ignore 'link' and 'value' + * // + * noderec.hnode.hash = strhash( key ); + * noderec.key = key; + * + * // perform search - return value + * // + * pnode = (MyNode) ft_hash_lookup( table, &noderec ); + * if ( *pnode ) + * { + * // we found it + * return (*pnode)->value; + * } + * return -1; + * } + * } + */ + FT_BASE_DEF( FT_HashNode* ) + ft_hash_lookup( FT_Hash table, + FT_HashNode keynode ) + + + /**************************************************************** + * + * @function: ft_hash_add + * + * @description: + * add a new node to a dynamic hash table. the user must + * call @ft_hash_lookup and allocate a new node before calling + * this function. + * + * @input: + * table :: hash table handle + * pnode :: pointer-to-hash-node value returned by @ft_hash_lookup + * new_node :: handle to new hash node. All its fields must be correctly + * set, including 'hash'. + * + * @note: + * this function should always be used _after_ a call to @ft_hash_lookup + * that returns a pointer to a NULL handle. Here's an example: + * + * { + * // sets the value corresponding to a given string key + * // + * void my_set( FT_Hash table, + * const char* key, + * int value ) + * { + * MyNode* pnode; + * MyNodeRec noderec; + * MyNode node; + * + * // set-up key node. It's 'hash' and 'key' fields must + * // be set correctly.. + * noderec.hnode.hash = strhash( key ); + * noderec.key = key; + * + * // perform search - return value + * pnode = (MyNode) ft_hash_lookup( table, &noderec ); + * if ( *pnode ) + * { + * // we found it, simply replace the value in the node + * (*pnode)->value = value; + * return; + * } + * + * // allocate a new node - and set it up + * node = (MyNode) malloc( sizeof(*node) ); + * node->hnode.hash = noderec.hnode.hash; + * node->key = key; + * node->value = value; + * + * // add it to the hash table + * ft_hash_add( table, pnode, node ); + * } + */ + FT_BASE( void ) + ft_hash_add( FT_Hash table, + FT_HashNode* pnode, + FT_HashNode new_node ); + + + /**************************************************************** + * + * @function: ft_hash_remove + * + * @description: + * try to remove the node corresponding to a given key from + * a hash table. This must be called after @ft_hash_lookup + * + * @input: + * table :: hash table handle + * pnode :: pointer-to-hash-node value returned by @ft_hash_lookup + * + * @note: + * this function doesn't free the node itself !! Here's an example: + * + * { + * // sets the value corresponding to a given string key + * // + * void my_remove( FT_Hash table, + * const char* key ) + * { + * MyNodeRec noderec; + * MyNode node; + * + * noderec.hnode.hash = strhash(key); + * noderec.key = key; + * node = &noderec; + * + * pnode = ft_hash_lookup( table, &noderec ); + * node = *pnode; + * if ( node != NULL ) + * { + * ft_hash_remove( table, pnode ); + * free( node ); + * } + * } + * } + */ + FT_BASE( void ) + ft_hash_remove( FT_Hash table, + FT_HashNode* pnode ); + + + + /**************************************************************** + * + * @function: ft_hash_get_size + * + * @description: + * return the number of elements in a given hash table + * + * @input: + * table :: handle to target hash table structure + * + * @return: + * number of elements. 0 if empty + */ + FT_BASE( FT_UInt ) + ft_hash_get_size( FT_Hash table ); + + + + /**************************************************************** + * + * @functype: FT_Hash_ForeachFunc + * + * @description: + * a function used to iterate over all elements of a given + * hash table + * + * @input: + * node :: handle to target @FT_HashNodeRec node structure + * data :: optional argument to iteration routine + * + * @also: @ft_hash_foreach + */ + typedef void (*FT_Hash_ForeachFunc)( const FT_HashNode node, + const FT_Pointer data ); + + + /**************************************************************** + * + * @function: ft_hash_foreach + * + * @description: + * parse over all elements in a hash table + * + * @input: + * table :: handle to target hash table structure + * foreach_func :: iteration routine called for each element + * foreach_data :: optional argument to the iteration routine + * + * @note: + * this function is often used to release all elements from a + * hash table. See the example given for @ft_hash_done + */ + FT_BASE( void ) + ft_hash_foreach( FT_Hash table, + FT_Hash_ForeachFunc foreach_func, + const FT_Pointer foreach_data ); + + + + /**************************************************************** + * + * @function: ft_hash_done + * + * @description: + * finalize a given hash table + * + * @input: + * table :: handle to target hash table structure + * node_func :: optional iteration function pointer. this + * can be used to destroy all nodes explicitely + * node_data :: optional argument to the node iterator + * + * @note: + * this function simply frees the hash table's buckets. + * you probably will need to call @ft_hash_foreach to + * destroy all its elements before @ft_hash_done, as in + * the following example: + * + * { + * static void my_node_clear( const MyNode node ) + * { + * free( node ); + * } + * + * static void my_done( FT_Hash table ) + * { + * ft_hash_done( table, (FT_Hash_ForeachFunc) my_node_clear, NULL ); + * } + * } + */ + FT_BASE( void ) + ft_hash_done( FT_Hash table, + FT_Hash_ForeachFunc item_func, + const FT_Pointer item_data ); + + /* */ + + /* compute bucket index from hash value in a dynamic hash table */ + /* this is only used to break encapsulation to speed lookups in */ + /* the FreeType cache manager !! */ + /* */ + +#define FT_HASH_COMPUTE_INDEX(_table,_hash,_index) \ + { \ + FT_UInt _mask = (_table)->mask; \ + FT_UInt _hash0 = (_hash); \ + \ + (_index) = (FT_UInt)( (_hash0) & _mask ) ); \ + if ( (_index) < (_table)->p ) \ + (_index) = (FT_uInt)( (_hash0) & ( 2*_mask+1 ) ); \ + } + + +FT_END_HEADER + +#endif /* __FT_HASH_H__ */ diff --git a/include/freetype/internal/ftobject.h b/include/freetype/internal/ftobject.h index d830ccdb1..e1faf545a 100644 --- a/include/freetype/internal/ftobject.h +++ b/include/freetype/internal/ftobject.h @@ -6,35 +6,189 @@ FT_BEGIN_HEADER + /************************************************************** + * + * @type: FT_Object + * + * @description: + * handle to a FreeType Object. See @FT_ObjectRec + */ typedef struct FT_ObjectRec_* FT_Object; - + + + /************************************************************** + * + * @type: FT_Class + * + * @description: + * handle to a constant class handle to a FreeType Object. + * + * Note that a class is itself a @FT_Object and are dynamically + * allocated on the heap. + * + * @also: + * @FT_ClassRec, @FT_Object, @FT_ObjectRec, @FT_Type, @FT_TypeRec + */ typedef const struct FT_ClassRec_* FT_Class; - + + + /************************************************************** + * + * @type: FT_Type + * + * @description: + * handle to a constant structure (of type @FT_TypeRec) used + * to describe a given @FT_Class type to the FreeType object + * sub-system. + */ typedef const struct FT_TypeRec_* FT_Type; + + + /************************************************************** + * + * @struct: FT_ObjectRec + * + * @description: + * a structure describing the root fields of all @FT_Object + * class instances in FreeType + * + * @fields: + * clazz :: handle to the object's class + * ref_count :: object's reference count. Starts at 1 + */ typedef struct FT_ObjectRec_ { FT_Class clazz; FT_Int ref_count; - + } FT_ObjectRec; + + /************************************************************** + * + * @macro: FT_OBJECT (x) + * + * @description: + * a useful macro to type-cast anything to a @FT_Object + * handle. No check performed.. + */ #define FT_OBJECT(x) ((FT_Object)(x)) + + + /************************************************************** + * + * @macro: FT_OBJECT_P (x) + * + * @description: + * a useful macro to type-cast anything to a pointer to + * @FT_Object handle. + */ #define FT_OBJECT_P(x) ((FT_Object*)(x)) + + /************************************************************** + * + * @macro: FT_OBJECT__CLASS (obj) + * + * @description: + * a useful macro to return the class of any object + */ #define FT_OBJECT__CLASS(x) FT_OBJECT(x)->clazz + + + /************************************************************** + * + * @macro: FT_OBJECT__REF_COUNT (obj) + * + * @description: + * a useful macro to return the reference count of any object + */ #define FT_OBJECT__REF_COUNT(x) FT_OBJECT(x)->ref_count + + + /************************************************************** + * + * @macro: FT_OBJECT__MEMORY (obj) + * + * @description: + * a useful macro to return a handle to the memory manager + * used to allocate a given object + */ #define FT_OBJECT__MEMORY(x) FT_CLASS__MEMORY(FT_OBJECT(x)->clazz) + + + /************************************************************** + * + * @macro: FT_OBJECT__LIBRARY (obj) + * + * @description: + * a useful macro to return a handle to the library handle + * that owns the object + */ #define FT_OBJECT__LIBRARY(x) FT_CLASS__LIBRARY(FT_OBJECT(x)->clazz) + + /************************************************************** + * + * @functype: FT_Object_InitFunc + * + * @description: + * a function used to initialize a new object + * + * @input: + * object :: target object handle + * init_data :: optional pointer to initialization data + * + * @throws: any + * + * the object is _assumed_ to be reachable from the cleanup + * stack when the constructor is called. This means that + * any exception can be thrown safely in it. + */ typedef void (*FT_Object_InitFunc)( FT_Object object, FT_Pointer init_data ); + /************************************************************** + * + * @functype: FT_Object_DoneFunc + * + * @description: + * a function used to finalize a given object + * + * @input: + * object :: handle to target object + * + * @throws: *never* !! + */ typedef void (*FT_Object_DoneFunc)( FT_Object object ); - typedef void (*FT_Object_CopyFunc)( FT_Object target, - FT_Object source ); + /************************************************************** + * + * @struct: FT_ClassRec + * + * @description: + * a structure used to describe a given object class within + * FreeType + * + * @fields: + * object :: root @FT_ObjectRec fields, since each class is + * itself an object. (it's an instance of the + * "metaclass", a special object of the FreeType + * object sub-system.) + * + * magic :: a 32-bit magic number used for decoding + * type :: the @FT_Type descriptor of this class + * memory :: the current memory manager handle + * library :: the current library handle + * info :: an opaque pointer to class-specific information + * managed by the FreeType object sub-system + * + * obj_size :: size of class instances in bytes + * obj_init :: class instance constructor + * obj_done :: class instance destructor + */ typedef struct FT_ClassRec_ { FT_ObjectRec object; @@ -43,60 +197,247 @@ FT_BEGIN_HEADER FT_Memory memory; FT_Library library; FT_Pointer info; - FT_Pointer data; FT_UInt obj_size; FT_Object_InitFunc obj_init; FT_Object_DoneFunc obj_done; - FT_Object_CopyFunc obj_copy; - + } FT_ClassRec; + + /************************************************************** + * + * @macro: FT_CLASS (x) + * + * @description: + * a useful macro to convert anything to a class handle + * without checks + */ #define FT_CLASS(x) ((FT_Class)(x)) + + + /************************************************************** + * + * @macro: FT_CLASS_P (x) + * + * @description: + * a useful macro to convert anything to a pointer to a class + * handle without checks + */ #define FT_CLASS_P(x) ((FT_Class*)(x)) + + /************************************************************** + * + * @macro: FT_CLASS__MEMORY (clazz) + * + * @description: + * a useful macro to return the memory manager handle of a + * given class + */ #define FT_CLASS__MEMORY(x) FT_CLASS(x)->memory + + + /************************************************************** + * + * @macro: FT_CLASS__LIBRARY (clazz) + * + * @description: + * a useful macro to return the library handle of a + * given class + */ #define FT_CLASS__LIBRARY(x) FT_CLASS(x)->library -#define FT_CLASS__MAGIC(x) FT_CLASS(x)->magic + + + + /************************************************************** + * + * @macro: FT_CLASS__TYPE (clazz) + * + * @description: + * a useful macro to return the type of a given class + * given class + */ #define FT_CLASS__TYPE(x) FT_CLASS(x)->type -#define FT_CLASS__DATA(x) FT_CLASS(x)->data + + /* */ #define FT_CLASS__INFO(x) FT_CLASS(x)->info +#define FT_CLASS__MAGIC(x) FT_CLASS(x)->magic + + /************************************************************** + * + * @struct: FT_TypeRec + * + * @description: + * a structure used to describe a given class to the FreeType + * object sub-system. + * + * @fields: + * name :: class name. only used for debugging + * super :: type of super-class. NULL if none + * + * class_size :: size of class structure in bytes + * class_init :: class constructor + * class_done :: class finalizer + * + * obj_size :: instance size in bytes + * obj_init :: instance constructor. can be NULL + * obj_done :: instance destructor. can be NULL + * + * @note: + * if 'obj_init' is NULL, the class will use it's parent + * constructor. + * + * if 'obj_done' is NULL, the class will use it's parent + * finalizer. + * + * the object sub-system allocates a new class, copies + * the content of its super-class into the new structure, + * _then_ calls 'clazz_init'. + */ typedef struct FT_TypeRec_ { const char* name; FT_Type super; - + FT_UInt class_size; FT_Object_InitFunc class_init; FT_Object_DoneFunc class_done; - + FT_UInt obj_size; FT_Object_InitFunc obj_init; FT_Object_DoneFunc obj_done; - FT_Object_CopyFunc obj_copy; - + } FT_TypeRec; + + /************************************************************** + * + * @macro: FT_TYPE (x) + * + * @description: + * a useful macro to convert anything to a class type handle + * without checks + */ #define FT_TYPE(x) ((FT_Type)(x)) - /* check that a handle points to a valid object */ + /************************************************************** + * + * @function: ft_object_check + * + * @description: + * checks that a handle points to a valid @FT_Object + * + * @input: + * obj :: handle/pointer + * + * @return: + * 1 iff the handle points to a valid object. 0 otherwise + */ FT_BASE_DEF( FT_Int ) ft_object_check( FT_Pointer obj ); - /* check that an object is of a given class */ + /************************************************************** + * + * @function: ft_object_is_a + * + * @description: + * checks that a handle points to a valid @FT_Object that + * is an instance of a given class (or of any of its sub-classes) + * + * @input: + * obj :: handle/pointer + * clazz :: class handle to check + * + * @return: + * 1 iff the handle points to a valid 'clazz' instance. 0 + * otherwise. + */ FT_BASE_DEF( FT_Int ) ft_object_is_a( FT_Pointer obj, FT_Class clazz ); + /************************************************************** + * + * @function: ft_object_new + * + * @description: + * create a new object (class instance) + * + * @input: + * clazz :: object's class pointer + * init_data :: optional pointer to initialization data + * + * @return: + * handle to new object. Cannot be NULL ! + */ FT_BASE_DEF( FT_Object ) ft_object_new( FT_Class clazz, FT_Pointer init_data ); + /************************************************************** + * + * @function: ft_object_create + * + * @description: + * a variation of @ft_object_new that should be used when + * creating a new object that is owned by another object + * which is reachable from the cleanup stack. + * + * this function is a bit more akward to use but completely + * avoids push/pop pairs during object construction and is + * therefore faster. + * + * @output: + * pobject :: new object handle + * + * @input: + * clazz :: object's class pointer + * init_data :: optional pointer to initialization data + * push :: boolean. If true, the new object is pushed + * on top of the cleanup stack. + */ + FT_BASE_DEF( void ) + ft_object_create( FT_Object *pobject, + FT_Class clazz, + FT_Pointer init_data ); + + /* */ + + FT_BASE_DEF( FT_Class ) + ft_class_find_by_type( FT_Type type, + FT_Memory memory ); + + FT_BASE_DEF( FT_Class ) + ft_class_find_by_name( FT_CString class_name, + FT_Memory memory ); + + FT_BASE_DEF( FT_Object ) + ft_object_new_from_type( FT_Type type, + FT_Pointer data, + FT_Memory memory ); + + FT_BASE_DEF( void ) + ft_object_create_from_type( FT_Object *pobject, + FT_Type type, + FT_Pointer init_data, + FT_Memory memory ); + + FT_BASE_DEF( void ) + ft_object_push( FT_Object object ); + + FT_BASE_DEF( void ) + ft_object_pop( FT_Object object ); + + FT_BASE_DEF( void ) + ft_object_pop_destroy( FT_Object object ); + + FT_END_HEADER #endif /* __FT_OBJECT_H__ */ diff --git a/src/base/ftexcept.c b/src/base/ftexcept.c index 96391b6b2..1b93a9970 100644 --- a/src/base/ftexcept.c +++ b/src/base/ftexcept.c @@ -10,9 +10,9 @@ stack->top = stack->chunk->items; stack->limit = stack->top + FT_CLEANUP_CHUNK_SIZE; stack->chunk_0.link = NULL; - + stack->memory = memory; - } + } @@ -21,7 +21,7 @@ { FT_Memory memory = stack->memory; FT_CleanupChunk chunk, next; - + for (;;) { chunk = stack->chunk; @@ -29,10 +29,10 @@ break; stack->chunk = chunk->link; - - FT_FREE( chunk ); + + FT_Free( chunk, memory ); } - + stack->memory = NULL; } @@ -49,22 +49,20 @@ FT_ASSERT( stack && stack->chunk && stack->top ); FT_ASSERT( item && item_func ); - + top = stack->top; - + top->item = item; top->item_func = item_func; top->item_data = item_data; - + top ++; - + if ( top == stack->limit ) { FT_CleanupChunk chunk; - FT_Error error; - - if ( FT_ALLOC( chunk, stack->memory ) ) - ft_cleanup_stack_throw( stack, error ); + + chunk = FT_QAlloc( sizeof(*chunk), stack->memory ); chunk->link = stack->chunk; stack->chunk = chunk; @@ -73,7 +71,7 @@ } stack->top = top; - } + } @@ -82,17 +80,17 @@ FT_Int destroy ) { FT_CleanupItem top; - - + + FT_ASSERT( stack && stack->chunk && stack->top ); top = stack->top; - + if ( top == stack->chunk->items ) { FT_CleanupChunk chunk; - + chunk = stack->chunk; - + if ( chunk == &stack->chunk_0 ) { FT_ERROR(( "cleanup.pop: empty cleanup stack !!\n" )); @@ -101,21 +99,21 @@ chunk = chunk->link; FT_QFree( stack->chunk, stack->memory ); - + stack->chunk = chunk; stack->limit = chunk->items + FT_CLEANUP_CHUNK_SIZE; top = stack->limit; } - + top --; - + if ( destroy ) top->item_func( top->item, top->item_data ); - + top->item = NULL; top->item_func = NULL; top->item_data = NULL; - + stack->top = top; } @@ -129,10 +127,10 @@ FT_ASSERT( stack && stack->chunk && stack->top ); - + top = stack->top; chunk = stack->chunk; - + if ( top > chunk->items ) top--; else @@ -146,17 +144,17 @@ } - FT_BASE_DEF( void ) - ft_cleanup_stack_throw( FT_CleanupStack stack, FT_Error error ) - { - } - FT_BASE_DEF( void ) ft_xhandler_enter( FT_XHandler xhandler, FT_Memory memory ) { - + FT_CleanupStack stack = FT_MEMORY__CLEANUP(memory); + + xhandler->previous = stack->xhandler; + xhandler->cleanup = stack->top; + xhandler->error = 0; + stack->xhandler = xhandler; } @@ -164,5 +162,36 @@ FT_BASE_DEF( void ) ft_xhandler_exit( FT_XHandler xhandler ) { + FT_CleanupStack stack = FT_MEMORY__CLEANUP(memory); + + stack->xhandler = xhandler->previous; + xhandler->previous = NULL; + xhandler->error = error; + xhandler->cleanup = NULL; } + + + FT_BASE_DEF( void ) + ft_cleanup_throw( FT_CleanupStack stack, + FT_Error error ) + { + FT_XHandler xhandler = stack->xhandler; + + if ( xhandler == NULL ) + { + /* no exception handler was registered. this */ + /* means that we have an un-handled exception */ + /* the only thing we can do is _PANIC_ and */ + /* halt the current program.. */ + /* */ + FT_ERROR(( "FREETYPE PANIC: An un-handled exception occured. Program aborted" )); + ft_exit(1); + } + + /* cleanup the stack until we reach the handler's */ + /* starting stack location.. */ + + xhandler->error = error; + longmp( xhandler->jump_buffer, 1 ); + } \ No newline at end of file diff --git a/src/base/fthash.c b/src/base/fthash.c new file mode 100644 index 000000000..bd3cb21ff --- /dev/null +++ b/src/base/fthash.c @@ -0,0 +1,215 @@ +#include "fthash.h" +#include FT_INTERNAL_MEMORY_H +#include FT_INTERNAL_DEBUG_H + +#define FT_HASH_MAX_LOAD 2 +#define FT_HASH_MIN_LOAD 1 +#define FT_HASH_SUB_LOAD (FT_HASH_MAX_LOAD-FT_HASH_MIN_LOAD) + +/* this one _must_ be a power of 2 !! */ +#define FT_HASH_INITIAL_SIZE 8 + + + FT_BASE_DEF( void ) + ft_hash_done( FT_Hash table, + FT_Hash_ForeachFunc node_func, + const FT_Pointer node_data ) + { + if ( table ) + { + FT_Memory memory = table->memory; + + if ( node_func ) + ft_hash_foreach( table, node_func, node_data ); + + FT_FREE( table->buckets ); + table->p = 0; + table->mask = 0; + table->slack = 0; + + table->compare = NULL; + } + } + + + FT_BASE_DEF( FT_UInt ) + ft_hash_get_size( FT_Hash table ) + { + FT_UInt result = 0; + + if ( table ) + result = (table->p + table->mask + 1)*FT_HASH_MAX_LOAD - table->slack; + + return result; + } + + + + FT_BASE_DEF( void ) + ft_hash_init( FT_Hash table, + FT_Hash_CompareFunc compare, + FT_Memory memory ) + { + hash->memory = memory; + hash->compare = node_compare; + hash->p = 0; + hash->mask = FT_HASH_INITIAL_SIZE-1; + hash->slack = FT_HASH_INITIAL_SIZE*FT_HASH_MAX_LOAD; + + FT_NEW_ARRAY( hash->buckets, FT_HASH_INITIAL_SIZE*2 ); + } + + + + FT_BASE_DEF( void ) + ft_hash_foreach( FT_Hash table, + FT_Hash_ForeachFunc foreach_func, + const FT_Pointer foreach_data ) + { + FT_UInt count = table->p + table->mask + 1; + FT_HashNode* pnode = table->buckets; + FT_HashNode node, next; + + for ( ; count > 0; count--, pnode++ ) + { + node = *pnode; + while ( node ) + { + next = node->link; + foreach_func( node, foreach_data ); + node = next; + } + } + } + + + + FT_BASE_DEF( FT_HashNode* ) + ft_hash_lookup( FT_Hash table, + FT_HashNode keynode ) + { + FT_UInt index; + FT_UInt23 hash = keynode->hash; + + index = (FT_UInt)(hash & table->mask); + if ( index < table->p ) + index = (FT_UInt)(hash & (2*table->mask+1)); + + pnode = &table->buckets[index]; + for (;;) + { + node = *pnode; + if ( node == NULL ) + break; + + if ( node->hash == hash && table->compare( node, keynode ) ) + break; + + pnode = &node->link; + } + + return pnode; + } + + + + + FT_BASE_DEF( void ) + ft_hash_add( FT_Hash table, + FT_HashNode* pnode, + FT_HashNode new_node ) + { + /* add it to the hash table */ + new_node->link = *pnode; + *pnode = new_node; + + if ( --table->slack < 0 ) + { + FT_UInt p = table->p; + FT_UInt mask = table->mask; + FT_HashNode new_list; + + /* split a single bucket */ + new_list = NULL; + pnode = table->buckets + p; + for (;;) + { + node = *pnode; + if ( node == NULL ) + break; + + if ( node->hash & mask ) + { + *pnode = node->link; + node->link = new_list; + new_list = node; + } + else + pnode = &node->link; + } + + table->buckets[ p + mask + 1 ] = new_list; + + table->slack += FT_HASH_MAX_LOAD; + + if ( p >= mask ) + { + FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask+1)*4 ); + table->mask = 2*mask + 1; + table->p = 0; + } + else + table->p = p + 1; + } + } + + + + FT_BASE_DEF( FT_Int ) + ft_hash_remove( FT_Hash table, + FT_HashNode* pnode ) + { + FT_HashNode node; + FT_UInt num_buckets; + + FT_ASSERT( pnode != NULL && node != NULL ); + + node = *pnode; + *pnode->link = node->link; + node->link = NULL; + + num_buckets = ( table->p + table->mask + 1) ; + + if ( ++ table->slack > num_buckets*FT_HASH_SUB_LOAD ) + { + FT_UInt p = table->p; + FT_UInt mask = table->mask; + FT_UInt old_index = p + mask; + FT_HashNode* pnode; + FT_HashNode* pold; + + if ( old_index < FT_HASH_INITIAL_SIZE ) + return; + + if ( p == 0 ) + { + table->mask >>= 1; + p = table->mask; + + FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask) ); + } + else + p--; + + pnode = table->buckets + p; + while ( *pnode ) + pnode = &(*pnode)->link; + + pold = table->buckets + old_index; + *pnode = *pold; + *pold = NULL; + + table->slack -= FT_HASH_MAX_LOAD; + table->p = p; + } + } diff --git a/src/base/ftobject.c b/src/base/ftobject.c new file mode 100644 index 000000000..db8565ba0 --- /dev/null +++ b/src/base/ftobject.c @@ -0,0 +1,302 @@ +#include "ftobject.c" + +#define FT_MAGIC_DEATH 0xDEADdead +#define FT_MAGIC_CLASS 0x12345678 + +#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 ) + + + /*******************************************************************/ + /*******************************************************************/ + /***** *****/ + /***** *****/ + /***** M E T A - C L A S S *****/ + /***** *****/ + /***** *****/ + /*******************************************************************/ + /*******************************************************************/ + + /* 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; + + + /* destroy a given class */ + static void + ft_class_hnode_destroy( FT_ClassHNode node ) + { + FT_Clazz clazz = node->clazz; + FT_Memory memory = clazz->memory; + FT_Type ctype = clazz->type; + + if ( ctype->class_done ) + ctype->class_done( clazz ); + + FT_FREE( clazz ); + + node->clazz = NULL; + node->type = NULL; + + FT_FREE( node ); + } + + + static FT_Int + ft_class_hnode_compare( const FT_ClassHNode node1, + const FT_ClassHNode node2 ) + { + return ( node1->type == node2->type ); + } + + + static void + ft_metaclass_done( FT_MetaClass meta ) + { + /* clear all objects */ + ft_hash_done( &meta->type_to_class, + (FT_Hash_ForeachFunc) ft_class_destroy, + NULL ); + + meta->clazz->object.clazz = NULL; + meta->clazz->object.ref_count = 0; + meta->clazz->magic = FT_MAGIC_DEATH; + } + + + static void + ft_metaclass_init( FT_MetaClass meta, + FT_Library library ) + { + FT_ClassRec* clazz = 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_metaclass_type; + clazz->info = NULL; + + 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 ); + } + + + /* find or create the class corresponding to a given type */ + static FT_Class + ft_metaclass_get_class( FT_MetaClass meta, + FT_Type ctype ) + { + FT_ClassHNodeRec keynode, *node, **pnode; + FT_Memory memory; + + keynode.hnode.hash = (FT_UInt32)( ctype >> 2 ); + keynode.type = type; + + pnode = (FT_ClassHNode) ft_hash_lookup( &meta->type_to_class, + &noderec ); + node = *pnode; + if ( node != NULL ) + return node->clazz; + + memory = FT_CLASS__MEMORY(meta); + node = FT_MEM_SAFE_ALLOC( sizeof(*node) ); + if ( node != NULL ) + { + FT_ClassRec* clazz; + + clazz = FT_MEM_SAFE_ALLOC( ctype->class_size ); + if ( clazz == NULL ) + { + FT_FREE( node ); + FT_XTHROW( FT_Err_Out_Of_Memory ); + } + } + } + + + 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) ft_class_init, + (FT_Object_DoneFunc) ft_class_done + }; + + + + + + + + + + + + + + + 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_Object o = FT_OBJECT(obj); + FT_Class c = FT_OBJECT__CLASS(obj); + + do + { + if ( c == clazz ) + return 1; + + c = c->super; + } + while ( c == NULL ); + + return (clazz == NULL); + } + } + + + /* 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_object_create( FT_Object *pobject, + FT_Class clazz, + FT_Pointer init_data ) + { + FT_Memory memory; + 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; + + if ( clazz->obj_init ) + clazz->obj_init( obj, init_data ); + } + + + FT_BASE_DEF( FT_Class ) + ft_class_find_by_type( FT_Type type, + FT_Memory memory ) + { + } + + + FT_BASE_DEF( FT_Class ) + ft_class_find_by_name( FT_CString class_name, + FT_Memory memory ); + + FT_BASE_DEF( FT_Object ) + ft_object_new_from_type( FT_Type type, + FT_Pointer data, + FT_Memory memory ); + + FT_BASE_DEF( void ) + ft_object_create_from_type( FT_Object *pobject, + FT_Type type, + FT_Pointer init_data, + FT_Memory memory ); + + FT_BASE_DEF( void ) + ft_object_push( FT_Object object ); + + FT_BASE_DEF( void ) + ft_object_pop( FT_Object object ); + + FT_BASE_DEF( void ) + ft_object_pop_destroy( FT_Object object ); +