929 lines
30 KiB
C
929 lines
30 KiB
C
/* -----------------------------------------------------------------
|
|
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/ngspice.h"
|
|
#include "ngspice/hash.h"
|
|
|
|
/* definitions local to this file only */
|
|
|
|
/* ********************** TYPE DEFINITIONS ************************* */
|
|
#define PRIMECOUNT 200
|
|
#define MINPRIMESIZE 7
|
|
|
|
/* ********************** STATIC DEFINITIONS ************************* */
|
|
static NGTABLEPTR _nghash_find_item(NGHASHPTR hhtable,void *user_key,void *data) ;
|
|
|
|
|
|
NGHASHPTR nghash_init_with_parms(nghash_compare_func_t *comp_func, nghash_func_t *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 = comp_func ;
|
|
hashtable->hash_func = 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 ? 1 : 0);
|
|
hashtable->power_of_two = (power_of_two ? 1 : 0);
|
|
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 == NGHASH_DEF_HASH(NGHASH_FUNC_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(NGHASH_FUNC_STR), NGHASH_DEF_HASH(NGHASH_FUNC_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(NGHASH_FUNC_PTR), NGHASH_DEF_HASH(NGHASH_FUNC_PTR),
|
|
num_entries, NGHASH_DEF_MAX_DENSITY,
|
|
NGHASH_DEF_GROW_FACTOR,
|
|
NGHASH_UNIQUE_TWO) ) ;
|
|
} /* end nghash_init_pointer() */
|
|
|
|
NGHASHPTR nghash_init_integer(int num_entries)
|
|
{
|
|
return( nghash_init_with_parms( NGHASH_DEF_CMP(NGHASH_FUNC_NUM), NGHASH_DEF_HASH(NGHASH_FUNC_NUM),
|
|
num_entries, NGHASH_DEF_MAX_DENSITY,
|
|
NGHASH_DEF_GROW_FACTOR,
|
|
NGHASH_UNIQUE_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 *))
|
|
{
|
|
NGTABLEPTR *table, hptr , zapptr ;
|
|
|
|
nghash_reset_stat(hashtable);
|
|
|
|
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 == NGHASH_DEF_HASH(NGHASH_FUNC_STR) ) {
|
|
/* we allocated this ourselves we can delete it */
|
|
NGFREE( zapptr->key ) ;
|
|
} else if( delete_key ){
|
|
delete_key (zapptr->key);
|
|
}
|
|
NGFREE( zapptr ) ;
|
|
}
|
|
memset(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 ;
|
|
unsigned int hsum ;
|
|
NGTABLEPTR curPtr ;
|
|
NGTABLEPTR *table ;
|
|
|
|
/* initialization */
|
|
table = hashtable->hash_table ;
|
|
DS(hashtable->access++;) ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Process the hash function.
|
|
----------------------------------------------------------------- */
|
|
switch( (intptr_t) hashtable->hash_func ) {
|
|
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 = 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 == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key );
|
|
} else {
|
|
ret_code = hashtable->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 */
|
|
|
|
/* initialization */
|
|
DS(hashtable->access++;) ;
|
|
|
|
if( hashtable->searchPtr ){
|
|
for(curPtr=hashtable->searchPtr->next; curPtr ; curPtr=curPtr->next ) {
|
|
if( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key );
|
|
} else {
|
|
ret_code = hashtable->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 ;
|
|
unsigned int hsum ;
|
|
void * user_data_p ;
|
|
NGTABLEPTR curPtr, *prevPtr ;
|
|
NGTABLEPTR *table ;
|
|
|
|
/* initialization */
|
|
table = hashtable->hash_table ;
|
|
DS(hashtable->access++;) ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Process the hash function.
|
|
----------------------------------------------------------------- */
|
|
switch( (intptr_t) hashtable->hash_func ) {
|
|
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 = 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 == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key );
|
|
} else {
|
|
ret_code = hashtable->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 == NGHASH_DEF_HASH(NGHASH_FUNC_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 ;
|
|
unsigned int hsum ;
|
|
NGTABLEPTR curPtr, temptr, curTable ;
|
|
NGTABLEPTR *table ;
|
|
|
|
/* initialization */
|
|
table = hashtable->hash_table ;
|
|
DS(hashtable->access++;)
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Process the hash function.
|
|
----------------------------------------------------------------- */
|
|
switch( (intptr_t) hashtable->hash_func ) {
|
|
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 = 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 == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key);
|
|
} else {
|
|
ret_code = hashtable->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 == NGHASH_DEF_HASH(NGHASH_FUNC_STR) ) {
|
|
curTable->key = copy((char *) 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 = (int)(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 ;
|
|
unsigned int hsum ;
|
|
NGTABLEPTR curPtr, temptr ;
|
|
NGTABLEPTR *table ;
|
|
|
|
/* initialization */
|
|
table = htable->hash_table ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Process the hash function.
|
|
----------------------------------------------------------------- */
|
|
switch( (intptr_t) htable->hash_func ) {
|
|
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 = htable->hash_func (htable, user_key);
|
|
}
|
|
|
|
/* insert into table only if distinct number */
|
|
if( (temptr = table[hsum]) != NULL ){
|
|
/* list started at this hash */
|
|
for(curPtr=temptr ; curPtr ; curPtr=curPtr->next ) {
|
|
if( htable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( htable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
htable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key);
|
|
} else {
|
|
ret_code = htable->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) != NULL ) {
|
|
current_spot = htable->enumeratePtr ;
|
|
*key_return = current_spot->key ;
|
|
return( current_spot->data ) ;
|
|
}
|
|
} else {
|
|
if( htable->enumeratePtr &&
|
|
(htable->enumeratePtr = htable->enumeratePtr->thread_next) != NULL ){
|
|
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) != NULL ){
|
|
current_spot = htable->enumeratePtr ;
|
|
return( current_spot->data ) ;
|
|
}
|
|
} else {
|
|
if( htable->enumeratePtr &&
|
|
(htable->enumeratePtr = htable->enumeratePtr->thread_next) != NULL ){
|
|
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) != NULL ){
|
|
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) != NULL ){
|
|
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) != NULL ){
|
|
current_spot = iter_p->position ;
|
|
return( current_spot->data ) ;
|
|
}
|
|
} else {
|
|
if( iter_p->position &&
|
|
(iter_p->position = iter_p->position->thread_next) != NULL ){
|
|
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]) != NULL ){
|
|
fprintf( stderr, " [%5d]:", i ) ;
|
|
count = 0 ;
|
|
for( ; hptr ; hptr=hptr->next ){
|
|
if( ++count == 3 ){
|
|
fprintf( stderr, "\n\t" ) ;
|
|
count = 0 ;
|
|
}
|
|
if( htable->hash_func == NGHASH_DEF_HASH(NGHASH_FUNC_STR) ) {
|
|
fprintf( stderr, " key:%s ", (char *) hptr->key ) ;
|
|
} else {
|
|
fprintf( stderr, " key:%p ", hptr->key ) ;
|
|
}
|
|
if( print_key) {
|
|
print_key (hptr->data);
|
|
} else {
|
|
fprintf( stderr, " data:%p ", 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 ;
|
|
unsigned long hsum ;
|
|
NGTABLEPTR curPtr, temptr, *prevPtr ;
|
|
NGTABLEPTR *table ;
|
|
|
|
/* initialization */
|
|
table = hashtable->hash_table ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* Process the hash function.
|
|
----------------------------------------------------------------- */
|
|
switch( (intptr_t) hashtable->hash_func ) {
|
|
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 = 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 == NGHASH_DEF_CMP(NGHASH_FUNC_STR) ) {
|
|
ret_code = strcmp((char *)curPtr->key, (char *) user_key ) ;
|
|
} else if ( hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_PTR) ||
|
|
hashtable->compare_func == NGHASH_DEF_CMP(NGHASH_FUNC_NUM) ) {
|
|
ret_code = NGHASH_PTR_COMPARE_FUNC( curPtr->key, user_key );
|
|
} else {
|
|
ret_code = hashtable->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 == NGHASH_DEF_HASH(NGHASH_FUNC_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 = (double)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() */
|