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.BRANCH-2-1-5
parent
f031ffd3bc
commit
8ff18d7f74
10 changed files with 1919 additions and 102 deletions
@ -0,0 +1,185 @@ |
||||
#ifndef __FT_CORE_H__ |
||||
#define __FT_CORE_H__ |
||||
|
||||
#include <ft2build.h> |
||||
#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__ */ |
@ -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 <ft2build.h> |
||||
#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__ */ |
@ -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; |
||||
} |
||||
} |
@ -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 ); |
||||
|
Loading…
Reference in new issue