2 changed files with 1454 additions and 0 deletions
-
491src/include/hash.h
-
963src/misc/hash.c
@ -0,0 +1,491 @@ |
|||||
|
/* ----------------------------------------------------------------- |
||||
|
FILE: nghash.h |
||||
|
DESCRIPTION:Insert file for threaded hash routines. |
||||
|
This code was donated from TimberWolf. |
||||
|
CONTENTS: |
||||
|
DATE: Jul 17, 1988 - original coding |
||||
|
REVISIONS: Aug 21, 2009 - adapted for ngspice |
||||
|
----------------------------------------------------------------- */ |
||||
|
#ifndef NGHASH_H |
||||
|
#define NGHASH_H |
||||
|
|
||||
|
#include <bool.h> |
||||
|
|
||||
|
#define _NGMALLOC(size_xz) tmalloc((size_xz)) |
||||
|
#define NGMALLOC(n, els) (els *) tmalloc((n)*sizeof(els)) |
||||
|
#define NGREALLOC(ar,n,els) (els *) trealloc(ar,(n)*sizeof(els)) |
||||
|
#define NGFREE(els) txfree(els) |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* ********************** TYPE DEFINITIONS ************************* */ |
||||
|
typedef void (*ngdelete)(void *) ; |
||||
|
|
||||
|
|
||||
|
|
||||
|
typedef struct ngtable_rec { |
||||
|
void *key ; |
||||
|
void *data ; |
||||
|
struct ngtable_rec *next ; /* collision list */ |
||||
|
struct ngtable_rec *thread_next ; /* thread thru entire table */ |
||||
|
struct ngtable_rec *thread_prev ; /* thread thru entire table */ |
||||
|
} NGTABLEBOX, *NGTABLEPTR ; |
||||
|
|
||||
|
typedef struct nghashbox { |
||||
|
NGTABLEPTR *hash_table ; |
||||
|
NGTABLEPTR thread ; /* thread of hash table */ |
||||
|
NGTABLEPTR last_entry ; /* last entry into hash table */ |
||||
|
NGTABLEPTR enumeratePtr ; /* used to enumerate hash table */ |
||||
|
NGTABLEPTR searchPtr ; /* used for find again mechanism */ |
||||
|
void *compare_func ; /* the comparison function */ |
||||
|
void *hash_func ; /* the hash function */ |
||||
|
int size ; /* the size of the table */ |
||||
|
int max_density ; /* maximum number of entries before growth */ |
||||
|
int num_entries ; /* current number of entries in table */ |
||||
|
int need_resize ; /* amount before we need a resize */ |
||||
|
float growth_factor ; /* how much to grow table by */ |
||||
|
long access ; /* used for statistics */ |
||||
|
long collision ; /* collision times */ |
||||
|
unsigned int power_of_two : 8 ; /* build table as a power of two */ |
||||
|
unsigned int call_from_free : 8 ;/* true if in a free calling sequence */ |
||||
|
unsigned int unique : 16 ; /* true if only one unique item in col. list */ |
||||
|
} NGHASHBOX, *NGHASHPTR ; |
||||
|
|
||||
|
typedef enum { |
||||
|
NGHASH_NONUNIQUE = 0, |
||||
|
NGHASH_UNIQUE = 1L, |
||||
|
NGHASH_POWER_OF_TWO = (1L<<1) |
||||
|
} NGHASHFLAGS_T ; |
||||
|
|
||||
|
typedef unsigned int (*nghash_func)(NGHASHPTR,void *) ; |
||||
|
|
||||
|
/* the default hashing functions */ |
||||
|
typedef enum { |
||||
|
NGHASH_FUNC_STR = 0, |
||||
|
NGHASH_FUNC_PTR = -1, |
||||
|
NGHASH_FUNC_NUM = -2 |
||||
|
} NGHASH_FUNC_T ; |
||||
|
|
||||
|
typedef struct nghash_iter_rec { |
||||
|
struct ngtable_rec *position ; |
||||
|
} NGHASHITER, *NGHASHITERPTR ; |
||||
|
/* ----------------------------------------------------------------- |
||||
|
* macro definition for enumeration. Strange looking but compiler |
||||
|
* will optimize. x_yz->position = 0 and x_yz will be returned. |
||||
|
----------------------------------------------------------------- */ |
||||
|
#define NGHASH_FIRST(x_yz) ( ((x_yz)->position = NULL) ? (x_yz) : (x_yz) ) |
||||
|
#define NGHASH_ITER_EQUAL(x_yz,y_yz) ( (x_yz)->position == (y_yz)->position ) |
||||
|
|
||||
|
#define NGHASH_DEF_HASH_STR NGHASH_FUNC_STR |
||||
|
#define NGHASH_DEF_HASH_PTR (void *) NGHASH_FUNC_PTR |
||||
|
#define NGHASH_DEF_HASH_NUM (void *) NGHASH_FUNC_NUM |
||||
|
|
||||
|
/* the default comparison functions */ |
||||
|
#define NGHASH_DEF_CMP_STR NGHASH_FUNC_STR |
||||
|
#define NGHASH_DEF_CMP_PTR (void *) NGHASH_FUNC_PTR |
||||
|
#define NGHASH_DEF_CMP_NUM (void *) NGHASH_FUNC_PTR |
||||
|
|
||||
|
/* defines for unique flag */ |
||||
|
|
||||
|
/* misc definitions */ |
||||
|
#define NGHASH_DEF_GROW_FACTOR 2.0 |
||||
|
#define NGHASH_DEF_MAX_DENSITY 4 |
||||
|
#define NGHASH_MIN_SIZE 4 |
||||
|
#define nghash_tablesize( htable_xz ) ( (htable_xz)->size ) |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Here are three hash functions in order of increasingly better |
||||
|
* behaviour. Remember hsum must be unsigned int. The best one |
||||
|
* is taken from tcl. |
||||
|
----------------------------------------------------------------- */ |
||||
|
|
||||
|
#define NGHASH_STR_TO_HASH( str, hsum, size ) \ |
||||
|
do { \ |
||||
|
char *name ; \ |
||||
|
int shift ; \ |
||||
|
for( name=str,shift=1,hsum=0 ;*name; name++){ \ |
||||
|
hsum = hsum + ((*name)<<shift); \ |
||||
|
shift = (shift + 1) & 0x0007; \ |
||||
|
} \ |
||||
|
hsum %= (size) ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#undef NGHASH_STR_TO_HASH |
||||
|
#define NGHASH_STR_TO_HASH( str, hsum, size ) \ |
||||
|
do { \ |
||||
|
char *name ; \ |
||||
|
unsigned long g ; \ |
||||
|
for( name=str,hsum=0 ;*name; name++){ \ |
||||
|
hsum = (hsum << 4) + (*name); \ |
||||
|
g = hsum & 0xF0000000 ; \ |
||||
|
if (g) { \ |
||||
|
hsum = hsum^(g >> 24); \ |
||||
|
hsum = hsum^g; \ |
||||
|
} \ |
||||
|
} \ |
||||
|
hsum = hsum & (size-1); \ |
||||
|
} while(0); |
||||
|
|
||||
|
#undef NGHASH_STR_TO_HASH |
||||
|
#define NGHASH_STR_TO_HASH( str, hsum, size ) \ |
||||
|
do { \ |
||||
|
int c ; \ |
||||
|
char *string ; \ |
||||
|
hsum = 0 ; \ |
||||
|
string = (char *) str ; \ |
||||
|
while(1) { \ |
||||
|
c = *string ; \ |
||||
|
string++ ; \ |
||||
|
if( c == 0) { \ |
||||
|
break ; \ |
||||
|
} \ |
||||
|
hsum += (hsum<<3) + c; \ |
||||
|
} \ |
||||
|
hsum %= (size) ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#define NGHASH_NUM_TO_HASH( num, hsum, size ) \ |
||||
|
do { \ |
||||
|
int c, len ; \ |
||||
|
unsigned long temp; \ |
||||
|
char cptr[80] ; \ |
||||
|
sprintf( cptr, "%lx", (UNSIGNED_LONG) num) ; \ |
||||
|
len = strlen(cptr) ; \ |
||||
|
temp = (unsigned long) cptr[0] ; \ |
||||
|
for( c = 1 ; c < len ; c++ ){ \ |
||||
|
temp += (temp<<3) + (unsigned long) cptr[c] ; \ |
||||
|
} \ |
||||
|
temp %= (size) ; \ |
||||
|
hsum = temp ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#undef NGHASH_NUM_TO_HASH |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Replace the old num to hash with a new algorithm which is simple |
||||
|
* and is 5 times faster. Variance was even less on generated data. |
||||
|
* To use this hash table power of 2 table size must be enforced. |
||||
|
----------------------------------------------------------------- */ |
||||
|
#define NGHASH_NUM_TO_HASH( ptr, hsum, size ) \ |
||||
|
do { \ |
||||
|
unsigned int temp ; \ |
||||
|
long value = (long) ptr ; \ |
||||
|
temp = value ; \ |
||||
|
hsum = temp & (size - 1) ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
|
||||
|
#define NGHASH_PTR_TO_HASH( ptr, hsum, size ) \ |
||||
|
do { \ |
||||
|
unsigned long temp; \ |
||||
|
temp = (unsigned long) (ptr); \ |
||||
|
temp %= (size) ; \ |
||||
|
hsum = temp ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#undef NGHASH_PTR_TO_HASH |
||||
|
/* ----------------------------------------------------------------- |
||||
|
* We use strlen instead of looking at the return value of sprintf |
||||
|
* because it is not standard. Actually no machine have 80 byte pointers |
||||
|
* but just being cautious. |
||||
|
----------------------------------------------------------------- */ |
||||
|
|
||||
|
#define NGHASH_PTR_TO_HASH( ptr, hsum, size ) \ |
||||
|
do { \ |
||||
|
int c, len ; \ |
||||
|
unsigned long temp; \ |
||||
|
char cptr[80] ; \ |
||||
|
sprintf( cptr, "%lx", (UNSIGNED_LONG) ptr) ; \ |
||||
|
len = strlen(cptr) ; \ |
||||
|
temp = (UNSIGNED_LONG) cptr[0] ; \ |
||||
|
for( c = 1 ; c < len ; c++ ){ \ |
||||
|
temp += (temp<<3) + (UNSIGNED_LONG) cptr[c] ; \ |
||||
|
} \ |
||||
|
temp %= (size) ; \ |
||||
|
hsum = temp ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#undef NGHASH_PTR_TO_HASH |
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Replace the old ptr to hash with a new algorithm which is simple |
||||
|
* and is 5 times faster. Variance was even less on generated data. |
||||
|
* To use this hash table power of 2 table size must be enforced. |
||||
|
----------------------------------------------------------------- */ |
||||
|
#define NGHASH_PTR_TO_HASH( ptr, hsum, size ) \ |
||||
|
do { \ |
||||
|
unsigned int temp ; \ |
||||
|
long value = (long) ptr ; \ |
||||
|
temp = value >> 4 ; \ |
||||
|
hsum = temp & (size - 1) ; \ |
||||
|
} while(0); |
||||
|
|
||||
|
#define NGHASH_PTR_COMPARE_FUNC( p1 , p2 ) ( (p1) != (p2) ) |
||||
|
|
||||
|
|
||||
|
#define nghash_unique( htable_xz, flag_xz ) ((htable_xz)->unique = flag_xz) |
||||
|
|
||||
|
extern NGHASHPTR nghash_init( int numentries ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns a hash table with the given number of entries. |
||||
|
More that one hash table can coexist at the same time. |
||||
|
The default key type is string. The default comparison function |
||||
|
is strcmp. |
||||
|
*/ |
||||
|
|
||||
|
extern NGHASHPTR nghash_init_integer( int numentries ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns a hash table with the given number of entries. |
||||
|
More that one hash table can coexist at the same time. |
||||
|
The default key type is an integer. The default comparison function |
||||
|
is integer comparison. |
||||
|
*/ |
||||
|
|
||||
|
extern NGHASHPTR nghash_init_pointer( int numentries ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns a hash table with the given number of entries. |
||||
|
More that one hash table can coexist at the same time. |
||||
|
The default key type is a pointer. The default comparison function |
||||
|
is pointer comparison. |
||||
|
*/ |
||||
|
|
||||
|
extern NGHASHPTR nghash_init_with_parms( void *comp_func, |
||||
|
nghash_func hash_func, int numentries, int max_density, |
||||
|
double growth, NGHASHFLAGS_T flags ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns a hash table with the given number of entries. |
||||
|
More that one hash table can coexist at the same time. |
||||
|
Tables may be given their own hash and compare functions. If your keys |
||||
|
are pointers, numbers or strings, it is recommended that you use the |
||||
|
functions: |
||||
|
* HASH_DEF_HASH_PTR and HASH_DEF_CMP_PTR for pointers, |
||||
|
* HASH_DEF_HASH_NUM and HASH_DEF_CMP_NUM for numbers, and |
||||
|
* HASH_DEF_HASH_STR and HASH_DEF_CMP_STR for strings. |
||||
|
The hash package will recognize these and run faster as a result. |
||||
|
|
||||
|
You may use your own hash and compare functions provided they look like |
||||
|
* INT hash(void * key) and |
||||
|
* UNSIGNED_INT compare(void * key1, void * key2). |
||||
|
The hash function's return value should be in the interval [0, UINT_MAX]. |
||||
|
The compare should return zero if the two keys are equal and a non-zero |
||||
|
value otherwise. |
||||
|
|
||||
|
Whenever |
||||
|
number of entries in hash table >= size of table * max_density, |
||||
|
the table is grown at a the specified by growth. Unique if TRUE only allows |
||||
|
one entry which matches comparison function. Otherwise, multiple items |
||||
|
which are not unique relative to the comparison function can exist in |
||||
|
collision list. |
||||
|
*/ |
||||
|
|
||||
|
extern int nghash_max_density(NGHASHPTR hashtable,int max_density) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Changes the max_density limit in the hash table if max_density > 1. |
||||
|
This function returns the current value of max_density. |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
extern int nghash_table_get( NGHASHPTR hashtable ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns the current size of hash table set by nghash_table_create |
||||
|
*/ |
||||
|
|
||||
|
extern int nghash_table_size( int num ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns the closest prime number to given size. |
||||
|
*/ |
||||
|
|
||||
|
extern int nghash_table_size2( int num ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns the table size to the closest power of 2. |
||||
|
*/ |
||||
|
|
||||
|
extern void *_nghash_find(NGHASHPTR hashtable, void * user_key,BOOL *status) ; |
||||
|
extern void *nghash_find(NGHASHPTR hashtable, void * user_key) ; |
||||
|
extern void *nghash_find_again(NGHASHPTR hashtable, void * user_key) ; |
||||
|
extern void *_nghash_find_again(NGHASHPTR hashtable, void * user_key,BOOL *status) ; |
||||
|
extern void *nghash_delete(NGHASHPTR hashtable, void * user_key) ; |
||||
|
extern void *nghash_insert(NGHASHPTR hashtable, void * user_key, void * data) ; |
||||
|
/* |
||||
|
The four functions above replace the old nghash_search function. The same |
||||
|
functionality but now four functions. |
||||
|
Function: |
||||
|
Hash table search routine. Given a hashtable and a key, perform |
||||
|
the following operations: |
||||
|
HASH_ENTER:if key is in table it, returns a pointer to the item. |
||||
|
if key is not in table, add it to the table. returns NULL. |
||||
|
HASH_FIND:if key is in table, it returns data pointer. |
||||
|
if key is not in the table, it returns NULL. |
||||
|
HASH_FIND_AGAIN:if additional keys are in table, returns data pointer. |
||||
|
if no more keys are in the table, it returns NULL. |
||||
|
HASH_DELETE:if key is in table, it returns -1 |
||||
|
if key is not in table, it return NULL. |
||||
|
Memory is not freed in the delete case, but marked dirty. |
||||
|
Data is a pointer to the information to be store with the given key. |
||||
|
A new operation is available for using pointers are arguments. |
||||
|
Just pass the pointer in as a key. Make sure to call the |
||||
|
nghash_init_pointer function first. |
||||
|
*/ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Convenience functions. |
||||
|
----------------------------------------------------------------- */ |
||||
|
typedef void * (*nghash)(void *) ; |
||||
|
|
||||
|
extern void nghash_free(NGHASHPTR htabl,void (*del_data)(void *),void (*del_key)(void *) ); |
||||
|
/* |
||||
|
Function: |
||||
|
Frees the memory associated with a hash table. The user make supply a |
||||
|
function which deletes the memory associated with the data field. |
||||
|
In addition, the user may free the memory stored at the key. |
||||
|
This function must have the data pointer supplied by the hash add routines |
||||
|
as an argument,ie. |
||||
|
nghash_table_delete( my_hash_table, my_free_func, my_key_free ) ; |
||||
|
my_free_func( data ) |
||||
|
void * data ; |
||||
|
{ |
||||
|
} |
||||
|
my_key_free( key ) |
||||
|
void * key ; |
||||
|
{ |
||||
|
} |
||||
|
Note: for the default hash types: STR, PTR, and NUM, the free key |
||||
|
operation is ignored. |
||||
|
*/ |
||||
|
|
||||
|
extern void nghash_free_string_func(char *str) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Just a wrapper for YFREE(string) |
||||
|
*/ |
||||
|
|
||||
|
extern void nghash_free_string_hashtable(NGHASHPTR htable) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Frees the memory associated with a hash table. This version is |
||||
|
a convenience function as it is equivalent to the function: |
||||
|
nghash_free( htable, (ngdelete) nghash_free_string_func, NULL ) ; |
||||
|
*/ |
||||
|
|
||||
|
extern void nghash_empty(NGHASHPTR htabl,void (*del_data)(void *),void (*del_key)(void *) ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Similar to nghash_free except table structure is not delete. However, |
||||
|
all entries have been deleted. |
||||
|
*/ |
||||
|
|
||||
|
extern NGHASHPTR nghash_merge( NGHASHPTR master_htable, NGHASHPTR merge_htable ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Merge items in merge_htable into master_htable. Create master_htable |
||||
|
if master_htable is NULL. Returns the merged hash table. |
||||
|
*/ |
||||
|
|
||||
|
extern int nghash_get_size( NGHASHPTR hashtable ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Since this is a threaded hash table we can find the size of |
||||
|
all the valid entries in the table in O(n) where n is the |
||||
|
number of valid entries. Returns the number of valid entries. |
||||
|
One may enumerate the hash table by writing the following loop. |
||||
|
TABLEPTR ptr ; |
||||
|
for( ptr = mytable->thread; ptr ; ptr= ptr->threadNext ){ |
||||
|
... |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
extern void *nghash_enumeratek(NGHASHPTR hashtable,void **key_ret,BOOL flag) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Since this is a threaded hash table, we can enumerate the elements of |
||||
|
the hash table in O(n) time where n is the number of valid entries. |
||||
|
Returns the data and key associated with each entry. The flag is similar |
||||
|
to the rbtree enumerate function. This eliminates the need for writing |
||||
|
the following: |
||||
|
TABLEPTR ptr ; |
||||
|
for( ptr = mytable->thread; ptr ; ptr= ptr->threadNext ){ |
||||
|
... |
||||
|
} |
||||
|
Instead write: |
||||
|
for( data = nghash_enumerate(hashtable,&key,TRUE) ; data ; |
||||
|
data = nghash_enumerate(hashtable,&key,TRUE) ){ |
||||
|
... |
||||
|
} |
||||
|
The order returned is guaranteed to be the same as the order entered. |
||||
|
*/ |
||||
|
|
||||
|
extern void *nghash_enumeratekRE(NGHASHPTR hashtable,void **key_ret,NGHASHITERPTR iter_p) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Same as nghash_enumeratekRE but this is a reentrant version. Use as |
||||
|
void * key_ret ; |
||||
|
for( i_p= nghash_enumeratekRE(htable_p,&key_ret,YHASH_FIRST(&iter) ) ; i_p ; |
||||
|
i_p= nghash_enumeratekRE(htable_p,&key_ret,&iter) ) ){ |
||||
|
data_p = (data_cast) key_ret ; |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
extern void *nghash_enumerate(NGHASHPTR hashtable,BOOL flag) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Like above but we don't return the key. |
||||
|
*/ |
||||
|
|
||||
|
extern void *nghash_enumerateRE(NGHASHPTR hashtable,NGHASHITERPTR iter_p) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Like nghash_enumerate but this is a reentrant version. |
||||
|
for( i_p= nghash_enumerateRE(htable_p,YHASH_FIRST(&iter) ) ; i_p ; |
||||
|
i_p= nghash_enumerateRE(htable_p,&iter) ) ){ |
||||
|
data_p = (data_cast) key_ret ; |
||||
|
} |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
extern BOOL nghash_deleteItem( NGHASHPTR hashtable, void *key, void *data ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Delete a specific item in the hash table. Returns true if the item was |
||||
|
found and deleted. |
||||
|
*/ |
||||
|
|
||||
|
/* |
||||
|
* This function has been removed because we now support reentrant versions. |
||||
|
extern VOID nghash_enumeratePush( P1(YHASHPTR hashtable)); |
||||
|
Function: |
||||
|
Push the current enumeration pointer onto a stack. This is useful |
||||
|
for recursive enumeration. |
||||
|
*/ |
||||
|
|
||||
|
/* |
||||
|
* This function has been removed because we now support reentrant versions. |
||||
|
extern VOID nghash_enumeratePop( P1(YHASHPTR hashtable)); |
||||
|
Function: |
||||
|
Pops the current enumeration pointer from the stack. This is useful |
||||
|
for recursive enumeration. |
||||
|
*/ |
||||
|
|
||||
|
extern void nghash_dump( NGHASHPTR hashtable,void (*print_func)(void *) ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Prints the contents of the hash table. |
||||
|
*/ |
||||
|
|
||||
|
extern void nghash_reset_stat( NGHASHPTR hashtable ) ; |
||||
|
extern void nghash_resize( NGHASHPTR hashtable, int num ) ; |
||||
|
|
||||
|
extern void nghash_distribution( NGHASHPTR hashtable ) ; |
||||
|
/* |
||||
|
Function: |
||||
|
Returns information about hash table distribution to message system. |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
#endif /* NGHASH_H */ |
||||
@ -0,0 +1,963 @@ |
|||||
|
/* ----------------------------------------------------------------- |
||||
|
FILE: hash.c |
||||
|
DESCRIPTION:This file contains the routines for building and |
||||
|
maintaining a hash table. |
||||
|
Abstract : Contains routines for managing hash tables. Tables |
||||
|
may be given their own hash and compare functions. If your keys |
||||
|
are pointers, numbers or strings, it is recommended that you |
||||
|
use the functions |
||||
|
* HASH_DEF_HASH_PTR and HASH_DEF_CMP_PTR for pointers, |
||||
|
* HASH_DEF_HASH_NUM and HASH_DEF_CMP_NUM for numbers, and |
||||
|
* HASH_DEF_HASH_STR and HASH_DEF_CMP_STR for strings. |
||||
|
The hash package will recognize these and run faster as |
||||
|
a result. |
||||
|
|
||||
|
You may use your own hash and compare functions provided they |
||||
|
look like |
||||
|
* int hash(void * key) and |
||||
|
* int compare(void * key1, void * key2). |
||||
|
The hash function's return value should be in the interval |
||||
|
[0, Uint_MAX]. The compare should return zero if the two |
||||
|
keys are equal and a non-zero value otherwise. |
||||
|
CONTENTS: |
||||
|
DATE: Jul 7, 1988 - original coding. |
||||
|
1988 - 2009 many updates... |
||||
|
REVISIONS: |
||||
|
----------------------------------------------------------------- */ |
||||
|
#include "ngspice.h" |
||||
|
#include "hash.h" |
||||
|
|
||||
|
/* definitions local to this file only */ |
||||
|
|
||||
|
/* ********************** TYPE DEFINITIONS ************************* */ |
||||
|
#define PRIMECOUNT 200 |
||||
|
#define MINPRIMESIZE 7 |
||||
|
typedef int (*COMPARE_FUNC)(void *,void *) ; |
||||
|
|
||||
|
/* ********************** STATIC DEFINITIONS ************************* */ |
||||
|
static NGTABLEPTR _nghash_find_item(NGHASHPTR hhtable,void *user_key,void *data) ; |
||||
|
|
||||
|
|
||||
|
NGHASHPTR nghash_init_with_parms(void *comp_func, nghash_func hash_func, int num, |
||||
|
int max, double growth, NGHASHFLAGS_T flags) |
||||
|
{ |
||||
|
BOOL unique ; /* entries are to be unique */ |
||||
|
BOOL power_of_two ; /* want hash table power of 2 */ |
||||
|
NGHASHPTR hashtable ; |
||||
|
|
||||
|
unique = flags & NGHASH_UNIQUE ; |
||||
|
power_of_two = flags & NGHASH_POWER_OF_TWO ; |
||||
|
|
||||
|
hashtable = NGMALLOC( 1, NGHASHBOX ) ; |
||||
|
if( power_of_two ){ |
||||
|
hashtable->size = nghash_table_size2( num ) ; |
||||
|
} else { |
||||
|
/* prime size */ |
||||
|
hashtable->size = nghash_table_size( num ) ; |
||||
|
} |
||||
|
hashtable->compare_func = (void *) comp_func ; |
||||
|
hashtable->hash_func = (void *) hash_func ; |
||||
|
|
||||
|
hashtable->hash_table = NGMALLOC( hashtable->size, NGTABLEPTR ) ; |
||||
|
hashtable->max_density = max ; |
||||
|
hashtable->need_resize = hashtable->size * hashtable->max_density ; |
||||
|
hashtable->growth_factor = growth ; |
||||
|
hashtable->unique = unique ; |
||||
|
hashtable->power_of_two = power_of_two ; |
||||
|
hashtable->thread = NULL ; /* initialize list */ |
||||
|
hashtable->last_entry = NULL ; /* end of list */ |
||||
|
hashtable->num_entries = 0 ; |
||||
|
hashtable->call_from_free = FALSE ; |
||||
|
hashtable->access = 0; |
||||
|
hashtable->collision = 0; |
||||
|
hashtable->enumeratePtr = NULL ; |
||||
|
return(hashtable) ; |
||||
|
} /* end nghash_init_with_parms() */ |
||||
|
|
||||
|
|
||||
|
void nghash_resize(NGHASHPTR hashtable, int num) |
||||
|
{ |
||||
|
NGTABLEPTR *oldtable, hptr, zapptr ; |
||||
|
NGTABLEPTR new_hptr ; /* new hash table entry */ |
||||
|
int i, oldsize ; |
||||
|
|
||||
|
oldsize = hashtable->size ; |
||||
|
oldtable = hashtable->hash_table; |
||||
|
|
||||
|
if( hashtable->power_of_two ){ |
||||
|
hashtable->size = nghash_table_size2( num - 1 ) ; |
||||
|
} else { |
||||
|
hashtable->size = nghash_table_size( num ) ; |
||||
|
} |
||||
|
hashtable->num_entries = 0 ; |
||||
|
hashtable->thread = NULL ; |
||||
|
hashtable->last_entry = NULL ; /* end of list */ |
||||
|
hashtable->need_resize = hashtable->size * hashtable->max_density ; |
||||
|
|
||||
|
hashtable->hash_table = NGMALLOC( hashtable->size, NGTABLEPTR); |
||||
|
for( i = 0 ; i < oldsize ; i++ ) { |
||||
|
for( hptr = oldtable[i]; hptr; ) { |
||||
|
zapptr = hptr ; |
||||
|
nghash_insert( hashtable, hptr->key, hptr->data ) ; |
||||
|
if( hashtable->searchPtr && hashtable->searchPtr == hptr ){ |
||||
|
new_hptr = _nghash_find_item(hashtable, hptr->key, hptr->data ) ; |
||||
|
hashtable->searchPtr = new_hptr ; |
||||
|
} |
||||
|
if( hashtable->enumeratePtr && hashtable->enumeratePtr == hptr ){ |
||||
|
new_hptr = _nghash_find_item(hashtable, hptr->key, hptr->data ) ; |
||||
|
hashtable->enumeratePtr = new_hptr ; |
||||
|
} |
||||
|
/* Now safe to free */ |
||||
|
if( hashtable->hash_func == (void *) NGHASH_DEF_HASH_STR){ |
||||
|
NGFREE( hptr->key); |
||||
|
} |
||||
|
hptr = hptr->next ; |
||||
|
NGFREE( zapptr ) ; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
NGFREE( oldtable ); |
||||
|
} /* end nghash_resize() */ |
||||
|
|
||||
|
|
||||
|
void nghash_reset_stat(NGHASHPTR hashtable) |
||||
|
{ |
||||
|
hashtable->collision = 0 ; |
||||
|
hashtable->access = 0 ; |
||||
|
} |
||||
|
|
||||
|
NGHASHPTR nghash_init(int num_entries) |
||||
|
{ |
||||
|
return( nghash_init_with_parms( NGHASH_DEF_CMP_STR, NGHASH_DEF_HASH_STR, |
||||
|
num_entries, NGHASH_DEF_MAX_DENSITY, |
||||
|
NGHASH_DEF_GROW_FACTOR, NGHASH_UNIQUE) ) ; |
||||
|
} /* end nghash_init() */ |
||||
|
|
||||
|
NGHASHPTR nghash_init_pointer(int num_entries) |
||||
|
{ |
||||
|
return( nghash_init_with_parms( NGHASH_DEF_CMP_PTR, NGHASH_DEF_HASH_PTR, |
||||
|
num_entries, NGHASH_DEF_MAX_DENSITY, |
||||
|
NGHASH_DEF_GROW_FACTOR, |
||||
|
NGHASH_UNIQUE|NGHASH_POWER_OF_TWO) ) ; |
||||
|
} /* end nghash_init_pointer() */ |
||||
|
|
||||
|
NGHASHPTR nghash_init_integer(int num_entries) |
||||
|
{ |
||||
|
return( nghash_init_with_parms( NGHASH_DEF_CMP_NUM, NGHASH_DEF_HASH_NUM, |
||||
|
num_entries, NGHASH_DEF_MAX_DENSITY, |
||||
|
NGHASH_DEF_GROW_FACTOR, |
||||
|
NGHASH_UNIQUE|NGHASH_POWER_OF_TWO) ) ; |
||||
|
} /* end nghash_init_integer() */ |
||||
|
|
||||
|
int nghash_table_get(NGHASHPTR hashtable) |
||||
|
{ |
||||
|
return(hashtable->size) ; |
||||
|
} |
||||
|
|
||||
|
int nghash_max_density(NGHASHPTR hashtable,int max_density) |
||||
|
{ |
||||
|
if( max_density > 0 ){ |
||||
|
hashtable->max_density = max_density ; |
||||
|
hashtable->need_resize = hashtable->size * hashtable->max_density ; |
||||
|
} |
||||
|
return(hashtable->max_density) ; |
||||
|
} |
||||
|
|
||||
|
void nghash_empty(NGHASHPTR hashtable, void (*delete_data) (void *), |
||||
|
void (*delete_key) (void *)) |
||||
|
{ |
||||
|
long old_size ; /* old size of hash table */ |
||||
|
long new_size ; /* new size of hash table */ |
||||
|
NGTABLEPTR *table, hptr , zapptr ; |
||||
|
|
||||
|
nghash_reset_stat(hashtable); |
||||
|
|
||||
|
old_size = MAX( NGHASH_MIN_SIZE, hashtable->num_entries ) ; |
||||
|
if( hashtable->power_of_two ){ |
||||
|
new_size = nghash_table_size2( old_size ) ; |
||||
|
} else { |
||||
|
/* prime size */ |
||||
|
new_size = nghash_table_size( old_size ) ; |
||||
|
} |
||||
|
|
||||
|
table = hashtable->hash_table ; |
||||
|
if( table ){ |
||||
|
for( hptr = hashtable->thread ; hptr ; ){ |
||||
|
zapptr = hptr ; |
||||
|
hptr = hptr->thread_next ; |
||||
|
|
||||
|
/* execute user define delete function if requested */ |
||||
|
if( delete_data ){ |
||||
|
(*delete_data)(zapptr->data) ; |
||||
|
} |
||||
|
if( hashtable->hash_func == (void *) NGHASH_DEF_HASH_STR ) { |
||||
|
/* we allocated this ourselves we can delete it */ |
||||
|
NGFREE( zapptr->key ) ; |
||||
|
} else if( delete_key ){ |
||||
|
(*delete_key)(zapptr->key) ; |
||||
|
} |
||||
|
NGFREE( zapptr ) ; |
||||
|
} |
||||
|
memset( (char *)table, 0, (size_t) hashtable->size*sizeof(NGTABLEPTR)) ; |
||||
|
} |
||||
|
/* free decks associated with tree if they exist */ |
||||
|
hashtable->thread = NULL ; /* initialize list */ |
||||
|
hashtable->last_entry = NULL ; /* initialize list */ |
||||
|
hashtable->num_entries = 0 ; |
||||
|
} /* end nghash_empty() */ |
||||
|
|
||||
|
|
||||
|
void nghash_free(NGHASHPTR hashtable, void (*delete_data) (void *), |
||||
|
void (*delete_key) (void *)) |
||||
|
{ |
||||
|
hashtable->call_from_free = TRUE; |
||||
|
nghash_empty(hashtable, delete_data, delete_key ) ; |
||||
|
hashtable->call_from_free = FALSE ; |
||||
|
NGFREE( hashtable->hash_table ) ; |
||||
|
NGFREE( hashtable ) ; |
||||
|
} /* end nghash_free() */ |
||||
|
|
||||
|
void nghash_free_string_func( char *str ) |
||||
|
{ |
||||
|
NGFREE( str ) ; |
||||
|
} /* end nghash_free_string_func() */ |
||||
|
|
||||
|
void nghash_free_string_hashtable(NGHASHPTR hashtable) |
||||
|
{ |
||||
|
hashtable->call_from_free = TRUE; |
||||
|
nghash_empty(hashtable, (ngdelete) nghash_free_string_func, NULL ) ; |
||||
|
hashtable->call_from_free = FALSE ; |
||||
|
NGFREE( hashtable->hash_table ) ; |
||||
|
NGFREE( hashtable ) ; |
||||
|
} /* end nghash_free_string_hashtable() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Now nghash_search is broken into four routines: nghash_find, |
||||
|
* nghash_find_again, nghash_delete, and nghash_insert. |
||||
|
----------------------------------------------------------------- */ |
||||
|
void * _nghash_find(NGHASHPTR hashtable, void * user_key,BOOL *status) |
||||
|
{ |
||||
|
int ret_code ; |
||||
|
long hfunc ; |
||||
|
unsigned int hsum ; |
||||
|
COMPARE_FUNC compare_func ; |
||||
|
NGTABLEPTR curPtr ; |
||||
|
NGTABLEPTR *table ; |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = hashtable->hash_table ; |
||||
|
DS(hashtable->access++;) ; |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Process the hash function. |
||||
|
----------------------------------------------------------------- */ |
||||
|
hfunc = (long) hashtable->hash_func ; |
||||
|
switch( hfunc ){ |
||||
|
case NGHASH_FUNC_STR: |
||||
|
NGHASH_STR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_PTR: |
||||
|
NGHASH_PTR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_NUM: |
||||
|
NGHASH_NUM_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
default: |
||||
|
hsum = (*((nghash_func)hashtable->hash_func))(hashtable,user_key) ; |
||||
|
} |
||||
|
|
||||
|
curPtr = table[hsum] ; |
||||
|
if( curPtr ){ |
||||
|
/* list started at this hash */ |
||||
|
for( ; curPtr ; curPtr=curPtr->next ) { |
||||
|
if( hashtable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( hashtable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
hashtable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) hashtable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
/* ---------------------------------------------------- |
||||
|
* Operation find or enter with unique items, we |
||||
|
* return item. On a nonunique enter, add item below. |
||||
|
------------------------------------------------------- */ |
||||
|
hashtable->searchPtr = curPtr ; |
||||
|
if( status ){ |
||||
|
*status = TRUE ; |
||||
|
} |
||||
|
return( curPtr->data ) ; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
/* cant find anything on a find operation */ |
||||
|
hashtable->searchPtr = NULL ; |
||||
|
if( status ){ |
||||
|
*status = FALSE ; |
||||
|
} |
||||
|
return( NULL ) ; |
||||
|
|
||||
|
} /* end nghash_find() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* This find does not understand 0 or NULL data items. Use _nghash_find |
||||
|
* instead. |
||||
|
* ----------------------------------------------------------------- */ |
||||
|
void * nghash_find(NGHASHPTR hashtable, void * user_key) |
||||
|
{ |
||||
|
return( _nghash_find( hashtable, user_key, NULL ) ) ; |
||||
|
} /* end nghash_find() */ |
||||
|
|
||||
|
|
||||
|
void * _nghash_find_again(NGHASHPTR hashtable, void * user_key,BOOL *status) |
||||
|
{ |
||||
|
int ret_code ; /* comparison return code */ |
||||
|
NGTABLEPTR curPtr ; /* current hashtable entry */ |
||||
|
COMPARE_FUNC compare_func ; /* user defined comparison function */ |
||||
|
NGTABLEPTR *table ; /* hash table array */ |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = hashtable->hash_table ; |
||||
|
DS(hashtable->access++;) ; |
||||
|
|
||||
|
if( hashtable->searchPtr ){ |
||||
|
for(curPtr=hashtable->searchPtr->next; curPtr ; curPtr=curPtr->next ) { |
||||
|
if( hashtable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( hashtable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
hashtable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) hashtable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
hashtable->searchPtr = curPtr ; |
||||
|
if( status ){ |
||||
|
*status = TRUE ; |
||||
|
} |
||||
|
return( curPtr->data ) ; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if( status ){ |
||||
|
*status = FALSE ; |
||||
|
} |
||||
|
return(NULL) ; |
||||
|
} /* end _nghash_find_again() */ |
||||
|
|
||||
|
void * nghash_find_again(NGHASHPTR hashtable, void * user_key) |
||||
|
{ |
||||
|
return( _nghash_find_again( hashtable, user_key, NULL ) ) ; |
||||
|
} /* end nghash_find_again() */ |
||||
|
|
||||
|
|
||||
|
void * nghash_delete(NGHASHPTR hashtable, void * user_key) |
||||
|
{ |
||||
|
int ret_code ; |
||||
|
long hfunc ; |
||||
|
unsigned int hsum ; |
||||
|
void * user_data_p ; |
||||
|
COMPARE_FUNC compare_func ; |
||||
|
NGTABLEPTR curPtr, *prevPtr ; |
||||
|
NGTABLEPTR *table ; |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = hashtable->hash_table ; |
||||
|
DS(hashtable->access++;) ; |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Process the hash function. |
||||
|
----------------------------------------------------------------- */ |
||||
|
hfunc = (long) hashtable->hash_func ; |
||||
|
switch( hfunc ){ |
||||
|
case NGHASH_FUNC_STR: |
||||
|
NGHASH_STR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_PTR: |
||||
|
NGHASH_PTR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_NUM: |
||||
|
NGHASH_NUM_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
default: |
||||
|
hsum = (*((nghash_func)hashtable->hash_func))(hashtable,user_key) ; |
||||
|
} |
||||
|
|
||||
|
/* insert into table only if distinct number */ |
||||
|
curPtr = table[hsum] ; |
||||
|
if( curPtr ){ |
||||
|
/* list started at this hash */ |
||||
|
prevPtr = table + hsum ; |
||||
|
for( ; curPtr ; prevPtr = &(curPtr->next), curPtr=curPtr->next ) { |
||||
|
if( hashtable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( hashtable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
hashtable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) hashtable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
if( curPtr->thread_prev ){ /* no sentinel */ |
||||
|
curPtr->thread_prev->thread_next = curPtr->thread_next; |
||||
|
} else { |
||||
|
hashtable->thread = curPtr->thread_next ; |
||||
|
} |
||||
|
if( curPtr->thread_next ){ /* no sentinel */ |
||||
|
curPtr->thread_next->thread_prev = curPtr->thread_prev ; |
||||
|
} else { |
||||
|
hashtable->last_entry = curPtr->thread_prev ; |
||||
|
} |
||||
|
*prevPtr = curPtr->next ; |
||||
|
if( hashtable->hash_func == (void *) NGHASH_DEF_HASH_STR ) { |
||||
|
/* we allocated this ourselves we can delete it */ |
||||
|
NGFREE( curPtr->key ) ; |
||||
|
} |
||||
|
user_data_p = curPtr->data ; |
||||
|
NGFREE(curPtr); |
||||
|
hashtable->num_entries-- ; |
||||
|
return( user_data_p ) ; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return( NULL ) ; /* didn't find anything to delete */ |
||||
|
|
||||
|
} /* end nghash_delete() */ |
||||
|
|
||||
|
void * nghash_insert(NGHASHPTR hashtable, void * user_key, void * data) |
||||
|
{ |
||||
|
int ret_code ; |
||||
|
long hfunc ; |
||||
|
unsigned int hsum ; |
||||
|
COMPARE_FUNC compare_func ; |
||||
|
NGTABLEPTR curPtr, temptr, curTable ; |
||||
|
NGTABLEPTR *table ; |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = hashtable->hash_table ; |
||||
|
DS(hashtable->access++;) |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Process the hash function. |
||||
|
----------------------------------------------------------------- */ |
||||
|
hfunc = (long) hashtable->hash_func ; |
||||
|
switch( hfunc ){ |
||||
|
case NGHASH_FUNC_STR: |
||||
|
NGHASH_STR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_PTR: |
||||
|
NGHASH_PTR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_NUM: |
||||
|
NGHASH_NUM_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
default: |
||||
|
hsum = (*((nghash_func)hashtable->hash_func))(hashtable,user_key) ; |
||||
|
} |
||||
|
|
||||
|
/* insert into table only if distinct number */ |
||||
|
temptr = table[hsum] ; |
||||
|
if( temptr ){ |
||||
|
/* list started at this hash */ |
||||
|
for( curPtr = temptr ; curPtr ; curPtr=curPtr->next ) { |
||||
|
DS(hashtable->collision++;) ; |
||||
|
if( hashtable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( hashtable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
hashtable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) hashtable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
if( hashtable->unique ){ |
||||
|
/* ---------------------------------------------------- |
||||
|
* Operation enter with unique items, we |
||||
|
* return item. On a nonunique enter, add item below. |
||||
|
------------------------------------------------------- */ |
||||
|
hashtable->searchPtr = curPtr ; |
||||
|
return( curPtr->data ) ; |
||||
|
} else { |
||||
|
break ; /* avoid some work for nonunique hash_enter */ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* now save data */ |
||||
|
hashtable->num_entries++ ; |
||||
|
table[hsum] = curTable = NGMALLOC(1,NGTABLEBOX); |
||||
|
curTable->data = data ; |
||||
|
if( hashtable->hash_func == (void *) NGHASH_DEF_HASH_STR ){ |
||||
|
curTable->key = copy( user_key); |
||||
|
} else { |
||||
|
curTable->key = user_key ; |
||||
|
} |
||||
|
curTable->next = temptr ; |
||||
|
/* now fix thread which goes through hash table */ |
||||
|
if( hashtable->last_entry ){ |
||||
|
hashtable->last_entry->thread_next = curTable ; |
||||
|
curTable->thread_prev = hashtable->last_entry ; |
||||
|
hashtable->last_entry = curTable ; |
||||
|
} else { |
||||
|
hashtable->thread = hashtable->last_entry = curTable ; |
||||
|
curTable->thread_prev = NULL ; |
||||
|
} |
||||
|
curTable->thread_next = NULL ; |
||||
|
|
||||
|
if( hashtable->num_entries >= hashtable->need_resize ){ |
||||
|
int newsize ; /* new size of table */ |
||||
|
newsize = hashtable->size * hashtable->growth_factor ; |
||||
|
nghash_resize(hashtable, newsize ) ; |
||||
|
} |
||||
|
|
||||
|
return( NULL ) ; /* no conflict on a enter */ |
||||
|
|
||||
|
} /* end nghash_insert() */ |
||||
|
|
||||
|
/* returns the pointer with the item */ |
||||
|
static NGTABLEPTR _nghash_find_item(NGHASHPTR htable,void * user_key,void * data) |
||||
|
{ |
||||
|
int ret_code ; |
||||
|
long hfunc ; |
||||
|
unsigned int hsum ; |
||||
|
COMPARE_FUNC compare_func ; |
||||
|
NGTABLEPTR curPtr, temptr ; |
||||
|
NGTABLEPTR *table ; |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = htable->hash_table ; |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Process the hash function. |
||||
|
----------------------------------------------------------------- */ |
||||
|
hfunc = (long) htable->hash_func ; |
||||
|
switch( hfunc ){ |
||||
|
case NGHASH_FUNC_STR: |
||||
|
NGHASH_STR_TO_HASH( user_key, hsum, htable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_PTR: |
||||
|
NGHASH_PTR_TO_HASH( user_key, hsum, htable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_NUM: |
||||
|
NGHASH_NUM_TO_HASH( user_key, hsum, htable->size); |
||||
|
break ; |
||||
|
default: |
||||
|
hsum = (*((nghash_func)htable->hash_func))(htable,user_key) ; |
||||
|
} |
||||
|
|
||||
|
/* insert into table only if distinct number */ |
||||
|
if( (temptr = table[hsum]) ){ |
||||
|
/* list started at this hash */ |
||||
|
for(curPtr=temptr ; curPtr ; curPtr=curPtr->next ) { |
||||
|
if( htable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( htable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
htable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) htable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
if( data ){ |
||||
|
if( curPtr->data == data ){ |
||||
|
return( curPtr ) ; |
||||
|
} |
||||
|
} else { |
||||
|
return( curPtr ) ; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return( NULL ) ; /* no matching item */ |
||||
|
|
||||
|
} /* end _nghash_find_item() */ |
||||
|
|
||||
|
|
||||
|
|
||||
|
void * nghash_enumeratek(NGHASHPTR htable, void * *key_return, BOOL start_flag) |
||||
|
{ |
||||
|
NGTABLEPTR current_spot ; /* current place in threaded list */ |
||||
|
|
||||
|
if( start_flag ){ |
||||
|
if( (htable->enumeratePtr = htable->thread) ){ |
||||
|
current_spot = htable->enumeratePtr ; |
||||
|
*key_return = current_spot->key ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} else { |
||||
|
if( htable->enumeratePtr && |
||||
|
(htable->enumeratePtr = htable->enumeratePtr->thread_next) ){ |
||||
|
current_spot = htable->enumeratePtr ; |
||||
|
*key_return = current_spot->key ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} |
||||
|
*key_return = NULL ; |
||||
|
return( NULL ) ; |
||||
|
|
||||
|
} /* end nghash_enumeratek() */ |
||||
|
|
||||
|
void * nghash_enumerate(NGHASHPTR htable,BOOL start_flag) |
||||
|
{ |
||||
|
NGTABLEPTR current_spot ; /* current place in threaded list */ |
||||
|
|
||||
|
if( start_flag ){ |
||||
|
if( (htable->enumeratePtr = htable->thread) ){ |
||||
|
current_spot = htable->enumeratePtr ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} else { |
||||
|
if( htable->enumeratePtr && |
||||
|
(htable->enumeratePtr = htable->enumeratePtr->thread_next) ){ |
||||
|
current_spot = htable->enumeratePtr ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} |
||||
|
return( NULL ) ; |
||||
|
|
||||
|
} /* end nghash_enumerate() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* This is the reentrant version which uses an iterator. |
||||
|
* ----------------------------------------------------------------- */ |
||||
|
void * nghash_enumeratekRE(NGHASHPTR htable, void * *key_return, NGHASHITERPTR iter_p) |
||||
|
{ |
||||
|
NGTABLEPTR current_spot ; /* current place in threaded list */ |
||||
|
FUNC_NAME("nghash_enumeratekRE") ; |
||||
|
|
||||
|
if(!(iter_p)){ |
||||
|
fprintf( stderr, "ERROR[%s]:Null iterator pointer.\n", routine ) ; |
||||
|
return(NULL) ; |
||||
|
} |
||||
|
if(!(iter_p->position)){ |
||||
|
if( (iter_p->position = htable->thread) ){ |
||||
|
current_spot = iter_p->position ; |
||||
|
*key_return = current_spot->key ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} else { |
||||
|
if( iter_p->position && |
||||
|
(iter_p->position = iter_p->position->thread_next) ){ |
||||
|
current_spot = iter_p->position ; |
||||
|
*key_return = current_spot->key ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} |
||||
|
*key_return = NULL ; |
||||
|
return( NULL ) ; |
||||
|
|
||||
|
} /* end nghash_enumeratekRE() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* This is the reentrant version which uses an iterator. |
||||
|
* ----------------------------------------------------------------- */ |
||||
|
void * nghash_enumerateRE(NGHASHPTR htable, NGHASHITERPTR iter_p) |
||||
|
{ |
||||
|
NGTABLEPTR current_spot ; /* current place in threaded list */ |
||||
|
FUNC_NAME("nghash_enumerateRE") ; |
||||
|
|
||||
|
if(!(iter_p)){ |
||||
|
fprintf( stderr, "ERROR[%s]:Null iterator pointer.\n", routine ) ; |
||||
|
return(NULL) ; |
||||
|
} |
||||
|
if(!(iter_p->position)){ |
||||
|
if( (iter_p->position = htable->thread) ){ |
||||
|
current_spot = iter_p->position ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} else { |
||||
|
if( iter_p->position && |
||||
|
(iter_p->position = iter_p->position->thread_next) ){ |
||||
|
current_spot = iter_p->position ; |
||||
|
return( current_spot->data ) ; |
||||
|
} |
||||
|
} |
||||
|
return( NULL ) ; |
||||
|
|
||||
|
} /* end nghash_enumerateRE() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Merge items in merge_htable into master_htable. Create master_htable |
||||
|
* if master_htable is NULL. Returns the merged hash table. |
||||
|
* ----------------------------------------------------------------- */ |
||||
|
NGHASHPTR nghash_merge( NGHASHPTR master_htable, NGHASHPTR merge_htable ) |
||||
|
{ |
||||
|
NGTABLEPTR ptr ; /* traverse hash table */ |
||||
|
|
||||
|
if(!(master_htable)){ |
||||
|
master_htable = NGMALLOC( 1, NGHASHBOX ) ; |
||||
|
*master_htable = *merge_htable ; |
||||
|
master_htable->hash_table = NGMALLOC( master_htable->size, NGTABLEPTR ) ; |
||||
|
master_htable->thread = NULL ; |
||||
|
master_htable->last_entry = NULL ; |
||||
|
master_htable->num_entries = 0 ; |
||||
|
master_htable->enumeratePtr = NULL ; |
||||
|
master_htable->searchPtr = NULL ; |
||||
|
master_htable->access = 0 ; |
||||
|
master_htable->collision = 0 ; |
||||
|
} |
||||
|
for( ptr = merge_htable->thread ; ptr ; ptr = ptr->thread_next ){ |
||||
|
nghash_insert( master_htable, ptr->key, ptr->data ) ; |
||||
|
} |
||||
|
return( master_htable ) ; |
||||
|
} /* end nghash_merge() */ |
||||
|
|
||||
|
|
||||
|
int nghash_get_size(NGHASHPTR hashtable) |
||||
|
{ |
||||
|
return( hashtable->num_entries ) ; |
||||
|
} |
||||
|
|
||||
|
void nghash_dump(NGHASHPTR htable, void (*print_key) (void *)) |
||||
|
{ |
||||
|
int i ; /* counter */ |
||||
|
int count ; /* counter */ |
||||
|
NGTABLEPTR *table ; /* hash table */ |
||||
|
NGTABLEPTR hptr ; /* hash table element */ |
||||
|
|
||||
|
table = htable->hash_table ; |
||||
|
fprintf( stderr, "Dump of hashtable containing %d entries...\n", |
||||
|
htable->num_entries ) ; |
||||
|
fprintf( stderr, "Table is %4.2f%% full\n", |
||||
|
100.0 * (double) htable->num_entries / (double) htable->size ) ; |
||||
|
for( i = 0 ; i < htable->size ; i++ ) { |
||||
|
if( (hptr = table[i]) ){ |
||||
|
fprintf( stderr, " [%5d]:", i ) ; |
||||
|
count = 0 ; |
||||
|
for( ; hptr ; hptr=hptr->next ){ |
||||
|
if( ++count == 3 ){ |
||||
|
fprintf( stderr, "\n\t" ) ; |
||||
|
count = 0 ; |
||||
|
} |
||||
|
if( htable->hash_func == (void *) NGHASH_DEF_HASH_STR ){ |
||||
|
fprintf( stderr, " key:%s ", (char *) hptr->key ) ; |
||||
|
} else { |
||||
|
fprintf( stderr, " key:%0lx ", (unsigned long) hptr->key ) ; |
||||
|
} |
||||
|
if( print_key) { |
||||
|
(*print_key)(hptr->data) ; |
||||
|
} else { |
||||
|
fprintf( stderr, " data:%0lx ", (unsigned long) hptr->data ) ; |
||||
|
} |
||||
|
} |
||||
|
fprintf( stderr, "\n" ) ; |
||||
|
} |
||||
|
} /* end for( i = 0... */ |
||||
|
|
||||
|
} /* end nghash_enumerate() */ |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* nghash_deleteItem - deletes a specified item out of the hash table. |
||||
|
* To be useful, unique flag should be off. Otherwise just use nghash_delete. |
||||
|
* Returns true if item was deleted. |
||||
|
----------------------------------------------------------------- */ |
||||
|
BOOL nghash_deleteItem(NGHASHPTR hashtable, void * user_key, void * data) |
||||
|
{ |
||||
|
int ret_code ; |
||||
|
long hfunc ; |
||||
|
unsigned long hsum ; |
||||
|
COMPARE_FUNC compare_func ; |
||||
|
NGTABLEPTR curPtr, temptr, *prevPtr ; |
||||
|
NGTABLEPTR *table ; |
||||
|
|
||||
|
/* initialization */ |
||||
|
table = hashtable->hash_table ; |
||||
|
|
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Process the hash function. |
||||
|
----------------------------------------------------------------- */ |
||||
|
hfunc = (long) hashtable->hash_func ; |
||||
|
switch( hfunc ){ |
||||
|
case NGHASH_FUNC_STR: |
||||
|
NGHASH_STR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_PTR: |
||||
|
NGHASH_PTR_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
case NGHASH_FUNC_NUM: |
||||
|
NGHASH_NUM_TO_HASH( user_key, hsum, hashtable->size); |
||||
|
break ; |
||||
|
default: |
||||
|
hsum = (*((nghash_func)hashtable->hash_func))(hashtable,user_key) ; |
||||
|
} |
||||
|
|
||||
|
/* insert into table only if distinct number */ |
||||
|
temptr = table[hsum] ; |
||||
|
if( temptr ){ |
||||
|
/* list started at this hash */ |
||||
|
prevPtr = table + hsum ; |
||||
|
for(curPtr=temptr;curPtr;prevPtr = &(curPtr->next), curPtr=curPtr->next ) { |
||||
|
/* ----------------------------------------------------------------- |
||||
|
* Look for match. |
||||
|
----------------------------------------------------------------- */ |
||||
|
if( hashtable->compare_func == (void *) NGHASH_DEF_CMP_STR ) { |
||||
|
ret_code = strcmp((char *)curPtr->key, user_key ) ; |
||||
|
} else if ( hashtable->compare_func == (void *) NGHASH_DEF_CMP_PTR|| |
||||
|
hashtable->compare_func == (void *) NGHASH_DEF_CMP_NUM ){ |
||||
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key ); |
||||
|
} else { |
||||
|
compare_func = (COMPARE_FUNC) hashtable->compare_func ; |
||||
|
ret_code = (*(compare_func))(curPtr->key, user_key ) ; |
||||
|
} |
||||
|
if( ret_code == STRINGEQ ){ |
||||
|
if( curPtr->data == data ){ |
||||
|
if( curPtr->thread_prev ){ /* no sentinel */ |
||||
|
curPtr->thread_prev->thread_next = curPtr->thread_next; |
||||
|
} else { |
||||
|
hashtable->thread = curPtr->thread_next ; |
||||
|
} |
||||
|
if( curPtr->thread_next ){ /* no sentinel */ |
||||
|
curPtr->thread_next->thread_prev = curPtr->thread_prev ; |
||||
|
} else { |
||||
|
hashtable->last_entry = curPtr->thread_prev ; |
||||
|
} |
||||
|
*prevPtr = curPtr->next; |
||||
|
if( hashtable->hash_func == (void *) NGHASH_DEF_HASH_STR ) { |
||||
|
/* we allocated this ourselves we can delete it */ |
||||
|
NGFREE( curPtr->key ) ; |
||||
|
} |
||||
|
NGFREE(curPtr); |
||||
|
hashtable->num_entries-- ; |
||||
|
return( TRUE ) ; |
||||
|
} |
||||
|
} |
||||
|
} /* end for(curPtr=temptr... */ |
||||
|
} /* end if( temptr... */ |
||||
|
|
||||
|
return( FALSE ) ; /* could not find item */ |
||||
|
|
||||
|
} /* end nghash_deleteItem() */ |
||||
|
|
||||
|
/*---------------------------- hash_table_size -------------------------*/ |
||||
|
int nghash_table_size(int minEntries) |
||||
|
{ |
||||
|
int i; |
||||
|
BOOL isPrime; |
||||
|
int prime; |
||||
|
int testPrime; |
||||
|
static int primes[PRIMECOUNT] = |
||||
|
{ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, |
||||
|
37, 41, 43, 47, 53, 59, 61, 67, 71, 73, |
||||
|
79, 83, 89, 97, 101, 103, 107, 109, 113, 127, |
||||
|
131, 137, 139, 149, 151, 157, 163, 167, 173, 179, |
||||
|
181, 191, 193, 197, 199, 211, 223, 227, 229, 233, |
||||
|
239, 241, 251, 257, 263, 269, 271, 277, 281, 283, |
||||
|
293, 307, 311, 313, 317, 331, 337, 347, 349, 353, |
||||
|
359, 367, 373, 379, 383, 389, 397, 401, 409, 419, |
||||
|
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, |
||||
|
479, 487, 491, 499, 503, 509, 521, 523, 541, 547, |
||||
|
557, 563, 569, 571, 577, 587, 593, 599, 601, 607, |
||||
|
613, 617, 619, 631, 641, 643, 647, 653, 659, 661, |
||||
|
673, 677, 683, 691, 701, 709, 719, 727, 733, 739, |
||||
|
743, 751, 757, 761, 769, 773, 787, 797, 809, 811, |
||||
|
821, 823, 827, 829, 839, 853, 857, 859, 863, 877, |
||||
|
881, 883, 887, 907, 991, 919, 929, 937, 941, 947, |
||||
|
953, 967, 971, 977, 983, 991, 997,1009,1013,1019, |
||||
|
1021,1031,1033,1039,1049,1051,1061,1063,1069,1087, |
||||
|
1091,1093,1097,1103,1109,1117,1123,1129,1151,1153, |
||||
|
1163,1171,1181,1187,1193,1201,1213,1217,1223,1229 }; |
||||
|
|
||||
|
if (minEntries <= MINPRIMESIZE){ |
||||
|
return(MINPRIMESIZE); |
||||
|
} else { |
||||
|
testPrime = minEntries; |
||||
|
/* test to see if even */ |
||||
|
if ((testPrime % 2) == 0){ |
||||
|
testPrime = testPrime + 1; |
||||
|
} |
||||
|
do { |
||||
|
testPrime = testPrime + 2; |
||||
|
isPrime = TRUE; |
||||
|
for (i=0;i < PRIMECOUNT;i++){ |
||||
|
prime = primes[i]; |
||||
|
if (testPrime < prime*prime){ |
||||
|
break; |
||||
|
} |
||||
|
if ((testPrime % prime) == 0){ |
||||
|
isPrime = FALSE; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} while (!(isPrime)); |
||||
|
|
||||
|
return(testPrime); |
||||
|
} |
||||
|
|
||||
|
} /* FUNCTION nghash_table_size */ |
||||
|
|
||||
|
int nghash_table_size2(int minEntries) |
||||
|
{ |
||||
|
int power ; |
||||
|
int table_size ; |
||||
|
power = 0 ; |
||||
|
while( minEntries > 0 ){ |
||||
|
minEntries = minEntries >> 1 ; |
||||
|
power++ ; |
||||
|
} |
||||
|
power = MIN( power, 32 ) ; |
||||
|
table_size = 1 << power ; |
||||
|
table_size = MAX( NGHASH_MIN_SIZE, table_size ) ; |
||||
|
return( table_size ) ; |
||||
|
|
||||
|
} /* end nghash_table_size2() */ |
||||
|
|
||||
|
|
||||
|
void nghash_distribution(NGHASHPTR hashtable) |
||||
|
{ |
||||
|
int i ; /* counter */ |
||||
|
long min ; /* min count */ |
||||
|
long max ; /* max count */ |
||||
|
long nzero_cnt ; /* non zero count */ |
||||
|
long this_count ; /* count items at this list */ |
||||
|
long tablesize ; /* table size */ |
||||
|
double avg ; /* average */ |
||||
|
double sum2 ; /* squared sum */ |
||||
|
double diff ; /* difference */ |
||||
|
double diff2 ; /* difference squared */ |
||||
|
double nzavg ; /* non zero average */ |
||||
|
double variance ; /* variance of table */ |
||||
|
NGTABLEPTR *table ; /* hash table */ |
||||
|
NGTABLEPTR hptr ; /* hash table pointer */ |
||||
|
FUNC_NAME("nghash_distribution" ) ; |
||||
|
|
||||
|
tablesize = nghash_tablesize(hashtable) ; |
||||
|
table = hashtable->hash_table ; |
||||
|
avg = hashtable->num_entries / (double) tablesize ; |
||||
|
sum2 = 0.0 ; |
||||
|
min = max = 0 ; |
||||
|
nzero_cnt = 0 ; |
||||
|
for( i = 0 ; i < tablesize ; i++ ) { |
||||
|
this_count = 0 ; |
||||
|
for( hptr = table[i]; hptr; hptr = hptr->next ) { |
||||
|
this_count++ ; |
||||
|
} |
||||
|
if( i == 0 ){ |
||||
|
min = max = this_count ; |
||||
|
} else { |
||||
|
if( this_count < min ){ |
||||
|
min = this_count ; |
||||
|
} |
||||
|
if( this_count > max ){ |
||||
|
max = this_count ; |
||||
|
} |
||||
|
} |
||||
|
if( this_count > 0 ){ |
||||
|
nzero_cnt++ ; |
||||
|
} |
||||
|
diff = this_count - avg ; |
||||
|
diff2 = diff * diff ; |
||||
|
sum2 += diff2 ; |
||||
|
} |
||||
|
variance = sum2 / hashtable->num_entries ; |
||||
|
nzavg = hashtable->num_entries / (double) nzero_cnt ; |
||||
|
fprintf( stderr, "[%s]:min:%ld max:%ld nonzero avg:%f\n", routine, min, max, nzavg ) ; |
||||
|
fprintf( stderr, " variance:%f std dev:%f target:%f nonzero entries:%ld / %ld\n", |
||||
|
variance, sqrt(variance), avg, nzero_cnt, tablesize ) ; |
||||
|
} /* end nghash_distribution() */ |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue