diff options
Diffstat (limited to 'src/st.c')
| -rw-r--r-- | src/st.c | 578 | 
1 files changed, 578 insertions, 0 deletions
| diff --git a/src/st.c b/src/st.c new file mode 100644 index 0000000..022880a --- /dev/null +++ b/src/st.c @@ -0,0 +1,578 @@ +/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */ + +/* static	char	sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +#include <malloc.h> +#endif + +#include "regint.h" +#include "st.h" + +typedef struct st_table_entry st_table_entry; + +struct st_table_entry { +    unsigned int hash; +    st_data_t key; +    st_data_t record; +    st_table_entry *next; +}; + +#define ST_DEFAULT_MAX_DENSITY 5 +#define 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(long, long); +static int numhash(long); +static struct st_hash_type type_numhash = { +    numcmp, +    numhash, +}; + +/* extern int strcmp(const char *, const char *); */ +static int strhash(const char *); +static struct st_hash_type type_strhash = { +    strcmp, +    strhash, +}; + +static void rehash(st_table *); + +#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 + +/* +Table of prime numbers 2^n+a, 2<=n<=30. +*/ +static const long primes[] = { +	8 + 3, +	16 + 3, +	32 + 5, +	64 + 3, +	128 + 3, +	256 + 27, +	512 + 9, +	1024 + 9, +	2048 + 5, +	4096 + 3, +	8192 + 27, +	16384 + 43, +	32768 + 3, +	65536 + 45, +	131072 + 29, +	262144 + 3, +	524288 + 21, +	1048576 + 7, +	2097152 + 17, +	4194304 + 15, +	8388608 + 9, +	16777216 + 43, +	33554432 + 35, +	67108864 + 15, +	134217728 + 29, +	268435456 + 3, +	536870912 + 11, +	1073741824 + 85, +	0 +}; + +static int +new_size(size) +    int size; +{ +    int i; + +#if 0 +    for (i=3; i<31; i++) { +	if ((1<<i) > size) return 1<<i; +    } +    return -1; +#else +    int newsize; + +    for (i = 0, newsize = MINSIZE; +	 i < (int )(sizeof(primes)/sizeof(primes[0])); +	 i++, newsize <<= 1) +    { +	if (newsize > size) return primes[i]; +    } +    /* Ran out of polynomials */ +    return -1;			/* should raise exception */ +#endif +} + +#ifdef HASH_LOG +static int collision = 0; +static int init_st = 0; + +static void +stat_col() +{ +    FILE *f = fopen("/tmp/col", "w"); +    fprintf(f, "collision: %d\n", collision); +    fclose(f); +} +#endif + +st_table* +st_init_table_with_size(type, size) +    struct st_hash_type *type; +    int size; +{ +    st_table *tbl; + +#ifdef HASH_LOG +    if (init_st == 0) { +	init_st = 1; +	atexit(stat_col); +    } +#endif + +    size = new_size(size);	/* round up to prime number */ + +    tbl = alloc(st_table); +    tbl->type = type; +    tbl->num_entries = 0; +    tbl->num_bins = size; +    tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*)); + +    return tbl; +} + +st_table* +st_init_table(type) +    struct st_hash_type *type; +{ +    return st_init_table_with_size(type, 0); +} + +st_table* +st_init_numtable(void) +{ +    return st_init_table(&type_numhash); +} + +st_table* +st_init_numtable_with_size(size) +    int size; +{ +    return st_init_table_with_size(&type_numhash, size); +} + +st_table* +st_init_strtable(void) +{ +    return st_init_table(&type_strhash); +} + +st_table* +st_init_strtable_with_size(size) +    int size; +{ +    return st_init_table_with_size(&type_strhash, size); +} + +void +st_free_table(table) +    st_table *table; +{ +    register 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) do {\ +    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;\ +    }\ +} while (0) + +int +st_lookup(table, key, value) +    st_table *table; +    register st_data_t key; +    st_data_t *value; +{ +    unsigned int hash_val, bin_pos; +    register 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)\ +do {\ +    st_table_entry *entry;\ +    if (table->num_entries/(table->num_bins) > ST_DEFAULT_MAX_DENSITY) {\ +	rehash(table);\ +        bin_pos = hash_val % table->num_bins;\ +    }\ +    \ +    entry = alloc(st_table_entry);\ +    \ +    entry->hash = hash_val;\ +    entry->key = key;\ +    entry->record = value;\ +    entry->next = table->bins[bin_pos];\ +    table->bins[bin_pos] = entry;\ +    table->num_entries++;\ +} while (0) + +int +st_insert(table, key, value) +    register st_table *table; +    register st_data_t key; +    st_data_t value; +{ +    unsigned int hash_val, bin_pos; +    register 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; +    } +} + +void +st_add_direct(table, key, value) +    st_table *table; +    st_data_t key; +    st_data_t 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); +} + +static void +rehash(table) +    register st_table *table; +{ +    register 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 = (st_table_entry**)Calloc(new_num_bins, sizeof(st_table_entry*)); + +    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; +} + +st_table* +st_copy(old_table) +    st_table *old_table; +{ +    st_table *new_table; +    st_table_entry *ptr, *entry; +    int i, num_bins = old_table->num_bins; + +    new_table = alloc(st_table); +    if (new_table == 0) { +	return 0; +    } + +    *new_table = *old_table; +    new_table->bins = (st_table_entry**) +	Calloc((unsigned)num_bins, sizeof(st_table_entry*)); + +    if (new_table->bins == 0) { +	free(new_table); +	return 0; +    } + +    for(i = 0; i < num_bins; i++) { +	new_table->bins[i] = 0; +	ptr = old_table->bins[i]; +	while (ptr != 0) { +	    entry = alloc(st_table_entry); +	    if (entry == 0) { +		free(new_table->bins); +		free(new_table); +		return 0; +	    } +	    *entry = *ptr; +	    entry->next = new_table->bins[i]; +	    new_table->bins[i] = entry; +	    ptr = ptr->next; +	} +    } +    return new_table; +} + +int +st_delete(table, key, value) +    register st_table *table; +    register st_data_t *key; +    st_data_t *value; +{ +    unsigned int hash_val; +    st_table_entry *tmp; +    register 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 +st_delete_safe(table, key, value, never) +    register st_table *table; +    register st_data_t *key; +    st_data_t *value; +    st_data_t never; +{ +    unsigned int hash_val; +    register 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 +#if defined(__GNUC__) +delete_never(st_data_t key __attribute__ ((unused)), st_data_t value, +	     st_data_t never) +#else +delete_never(key, value, never) +    st_data_t key, value, never; +#endif +{ +    if (value == never) return ST_DELETE; +    return ST_CONTINUE; +} + +void +st_cleanup_safe(table, never) +    st_table *table; +    st_data_t never; +{ +    int num_entries = table->num_entries; + +    st_foreach(table, delete_never, never); +    table->num_entries = num_entries; +} + +int +st_foreach(table, func, arg) +    st_table *table; +    int (*func)(); +    st_data_t arg; +{ +    st_table_entry *ptr, *last, *tmp; +    enum 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 ST_CHECK:	/* check if hash is modified during iteration */ +	        tmp = 0; +		if (i < table->num_bins) { +		    for (tmp = table->bins[i]; tmp; tmp=tmp->next) { +			if (tmp == ptr) break; +		    } +		} +		if (!tmp) { +		    /* call func with error notice */ +		    return 1; +		} +		/* fall through */ +	    case ST_CONTINUE: +		last = ptr; +		ptr = ptr->next; +		break; +	    case ST_STOP: +	        return 0; +	    case 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--; +	    } +	} +    } +    return 0; +} + +static int +strhash(string) +    register const 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 += c; +	val += (val << 10); +	val ^= (val >> 6); +    } +    val += (val << 3); +    val ^= (val >> 11); + +    return val + (val << 15); +#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; +} | 
