/* this file has been modified again by Filip Pizlo, 2003, 2004, 2005, to be * incorporated into tsf. */ /* this file has been modified by Filip Pizlo, 2002, to support C++ */ /* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */ /* FIXME: make this code check for out-of-memory errors and do the * right thing, which should include using tsf_set_error() and friends. */ #include "tsf_internal.h" #include #ifdef NT #include #endif #define TSF_ST_DEFAULT_MAX_DENSITY 5 #define TSF_ST_DEFAULT_INIT_TABLE_SIZE 11 /* * DEFAULT_MAX_DENSITY is the default for the largest we allow the * average number of items per bin before increasing the number of * bins * * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins * allocated initially * */ static int numcmp(); static int numhash(); static struct tsf_st_hash_type type_numhash = { numcmp, numhash, }; static int ptrcmp(); static struct tsf_st_hash_type type_ptrhash = { ptrcmp, tsf_st_ptrhash, }; #undef strcmp extern int strcmp(); int tsf_st_strhash(); static struct tsf_st_hash_type type_strhash = { strcmp, tsf_st_strhash, }; #include #define xmalloc malloc #define xcalloc calloc #define xrealloc realloc #define xfree free static int rehash(); #define alloc(type) (type*)xmalloc((unsigned)sizeof(type)) #define Calloc(n,s) (char*)xcalloc((n),(s)) #define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0) #define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key)) #define do_hash_bin(key,table) (do_hash(key, table)&(table)->num_bins) /* * MINSIZE is the minimum size of a dictionary. */ #define MINSIZE 8 static int new_size(size) int size; { int i; for (i=3; i<31; i++) { if ((1< size) return 1<type = type; tbl->num_entries = 0; tbl->num_bins = size-1; tbl->bins = (tsf_st_table_entry **)Calloc(size, sizeof(tsf_st_table_entry*)); if (tbl->bins==NULL) { free(tbl); return NULL; } return tbl; } tsf_st_table* tsf_st_init_table(type) struct tsf_st_hash_type *type; { return tsf_st_init_table_with_size(type, 0); } tsf_st_table* tsf_st_init_numtable() { return tsf_st_init_table(&type_numhash); } tsf_st_table* tsf_st_init_numtable_with_size(size) int size; { return tsf_st_init_table_with_size(&type_numhash, size); } tsf_st_table* tsf_st_init_ptrtable() { return tsf_st_init_table(&type_ptrhash); } tsf_st_table* tsf_st_init_ptrtable_with_size(size) int size; { return tsf_st_init_table_with_size(&type_ptrhash, size); } tsf_st_table* tsf_st_init_strtable() { return tsf_st_init_table(&type_strhash); } tsf_st_table* tsf_st_init_strtable_with_size(size) int size; { return tsf_st_init_table_with_size(&type_strhash, size); } void tsf_st_free_table(table) tsf_st_table *table; { register tsf_st_table_entry *ptr, *next; int i; for(i = 0; i <= table->num_bins; i++) { ptr = table->bins[i]; while (ptr != 0) { next = ptr->next; free(ptr); ptr = next; } } free(table->bins); free(table); } #define PTR_NOT_EQUAL(table, ptr, hash_val, key) \ ((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key))) #ifdef HASH_LOG #define COLLISION collision++ #else #define COLLISION #endif #define FIND_ENTRY(table, ptr, hash_val, bin_pos) \ bin_pos = hash_val&(table)->num_bins;\ ptr = (table)->bins[bin_pos];\ if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\ COLLISION;\ while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\ ptr = ptr->next;\ }\ ptr = ptr->next;\ } int tsf_st_lookup(table, key, value) tsf_st_table *table; register char *key; void **value; { unsigned int hash_val, bin_pos; register tsf_st_table_entry *ptr; hash_val = do_hash(key, table); FIND_ENTRY(table, ptr, hash_val, bin_pos); if (ptr == 0) { return 0; } else { if (value != 0) *value = ptr->record; return 1; } } #define ADD_DIRECT(table, key, value, hash_val, bin_pos)\ {\ tsf_st_table_entry *entry;\ if (table->num_entries/(table->num_bins+1) > TSF_ST_DEFAULT_MAX_DENSITY) {\ if (rehash(table)<0) {\ return -1;\ }\ bin_pos = hash_val & table->num_bins;\ }\ \ entry = alloc(tsf_st_table_entry);\ if (entry==NULL) {\ return -1;\ }\ \ entry->hash = hash_val;\ entry->key = key;\ entry->record = value;\ entry->next = table->bins[bin_pos];\ table->bins[bin_pos] = entry;\ table->num_entries++;\ } /* make this function return -1 on error! */ int tsf_st_insert(table, key, value) register tsf_st_table *table; register char *key; void *value; { unsigned int hash_val, bin_pos; register tsf_st_table_entry *ptr; hash_val = do_hash(key, table); FIND_ENTRY(table, ptr, hash_val, bin_pos); if (ptr == 0) { ADD_DIRECT(table, key, value, hash_val, bin_pos); return 0; } else { ptr->record = value; return 1; } } /* make this function return -1 on error! */ int tsf_st_insert_noreplace(table, key, value) register tsf_st_table *table; register char *key; void *value; { unsigned int hash_val, bin_pos; register tsf_st_table_entry *ptr; hash_val = do_hash(key, table); FIND_ENTRY(table, ptr, hash_val, bin_pos); if (ptr == 0) { ADD_DIRECT(table, key, value, hash_val, bin_pos); return 0; } else { return 1; } } /* make this function return -1 on error! */ int tsf_st_add_direct(table, key, value) tsf_st_table *table; char *key; void *value; { unsigned int hash_val, bin_pos; hash_val = do_hash(key, table); bin_pos = hash_val & table->num_bins; ADD_DIRECT(table, key, value, hash_val, bin_pos); return 0; } static int rehash(table) register tsf_st_table *table; { register tsf_st_table_entry *ptr, *next, **new_bins; int i, old_num_bins = table->num_bins, new_num_bins; unsigned int hash_val; new_num_bins = new_size(old_num_bins+1); new_bins = (tsf_st_table_entry**)Calloc(new_num_bins, sizeof(tsf_st_table_entry*)); if (new_bins==NULL) { return -1; } new_num_bins--; for(i = 0; i <= old_num_bins; i++) { ptr = table->bins[i]; while (ptr != 0) { next = ptr->next; hash_val = ptr->hash & new_num_bins; ptr->next = new_bins[hash_val]; new_bins[hash_val] = ptr; ptr = next; } } free(table->bins); table->num_bins = new_num_bins; table->bins = new_bins; return 0; } tsf_st_table* tsf_st_copy(old_table) tsf_st_table *old_table; { tsf_st_table *new_table; tsf_st_table_entry *ptr, *entry; int i, num_bins = old_table->num_bins+1; new_table = alloc(tsf_st_table); if (new_table == NULL) { return NULL; } *new_table = *old_table; new_table->bins = (tsf_st_table_entry**) Calloc((unsigned)num_bins, sizeof(tsf_st_table_entry*)); if (new_table->bins == NULL) { free(new_table); return NULL; } for(i = 0; i < num_bins; i++) { new_table->bins[i] = 0; ptr = old_table->bins[i]; while (ptr != 0) { entry = alloc(tsf_st_table_entry); if (entry == NULL) { free(new_table->bins); free(new_table); return NULL; } *entry = *ptr; entry->next = new_table->bins[i]; new_table->bins[i] = entry; ptr = ptr->next; } } return new_table; } int tsf_st_delete(table, key, value) register tsf_st_table *table; register char **key; void **value; { unsigned int hash_val; tsf_st_table_entry *tmp; register tsf_st_table_entry *ptr; hash_val = do_hash_bin(*key, table); ptr = table->bins[hash_val]; if (ptr == 0) { if (value != 0) *value = 0; return 0; } if (EQUAL(table, *key, ptr->key)) { table->bins[hash_val] = ptr->next; table->num_entries--; if (value != 0) *value = ptr->record; *key = ptr->key; free(ptr); return 1; } for(; ptr->next != 0; ptr = ptr->next) { if (EQUAL(table, ptr->next->key, *key)) { tmp = ptr->next; ptr->next = ptr->next->next; table->num_entries--; if (value != 0) *value = tmp->record; *key = tmp->key; free(tmp); return 1; } } return 0; } int tsf_st_delete_safe(table, key, value, never) register tsf_st_table *table; register char **key; void **value; char *never; { unsigned int hash_val; register tsf_st_table_entry *ptr; hash_val = do_hash_bin(*key, table); ptr = table->bins[hash_val]; if (ptr == 0) { if (value != 0) *value = 0; return 0; } for(; ptr != 0; ptr = ptr->next) { if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) { table->num_entries--; *key = ptr->key; if (value != 0) *value = ptr->record; ptr->key = ptr->record = never; return 1; } } return 0; } static int delete_never(key, value, never) char *key, *never; void *value; { if (value == never) return TSF_ST_DELETE; return TSF_ST_CONTINUE; } void tsf_st_cleanup_safe(table, never) tsf_st_table *table; char *never; { int num_entries = table->num_entries; tsf_st_foreach(table, (tsf_st_func_t)delete_never, never); table->num_entries = num_entries; } void tsf_st_foreach(table, func, arg) tsf_st_table *table; tsf_st_func_t func; void *arg; { tsf_st_table_entry *ptr, *last, *tmp; enum tsf_st_retval retval; int i; for(i = 0; i <= table->num_bins; i++) { last = 0; for(ptr = table->bins[i]; ptr != 0;) { retval = (*func)(ptr->key, ptr->record, arg); switch (retval) { case TSF_ST_CONTINUE: last = ptr; ptr = ptr->next; break; case TSF_ST_STOP: return; case TSF_ST_DELETE: tmp = ptr; if (last == 0) { table->bins[i] = ptr->next; } else { last->next = ptr->next; } ptr = ptr->next; free(tmp); table->num_entries--; } } } } void tsf_st_iterator_init(tsf_st_table *table, tsf_st_iterator *iter) { iter->table=table; iter->i=0; iter->ptr=NULL; tsf_st_iterator_next(iter); } void tsf_st_iterator_get(tsf_st_iterator *iter, char **key, void **value) { if (key!=NULL) { *key=iter->ptr->key; } if (value!=NULL) { *value=iter->ptr->record; } } void tsf_st_iterator_next(tsf_st_iterator *iter) { if (iter->ptr!=NULL) { iter->ptr=iter->ptr->next; } if (iter->ptr==NULL) { for (;iter->ptr==NULL && iter->i<=iter->table->num_bins; ++iter->i) { iter->ptr=iter->table->bins[iter->i]; } } } int tsf_st_iterator_valid(tsf_st_iterator *iter) { return iter->ptr!=NULL; } int tsf_st_strhash(string) register char *string; { register int c; #ifdef HASH_ELFHASH register unsigned int h = 0, g; while ((c = *string++) != '\0') { h = ( h << 4 ) + c; if ( g = h & 0xF0000000 ) h ^= g >> 24; h &= ~g; } return h; #elif HASH_PERL register int val = 0; while ((c = *string++) != '\0') { val = val*33 + c; } return val + (val>>5); #else register int val = 0; while ((c = *string++) != '\0') { val = val*997 + c; } return val + (val>>5); #endif } static int numcmp(x, y) long x, y; { return x != y; } static int numhash(n) long n; { return n; } static int ptrcmp(void *x, void *y) { return x != y; }