| /* 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> | |
| #include "OnigurumaUefiPort.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); | |
| CHECK_NULL_RETURN(tbl); | |
| 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);\ | |
| if (entry == NULL) {\ | |
| break;\ | |
| }\ | |
| \ | |
| 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*)); | |
| if (new_bins == NULL) { | |
| return; | |
| } | |
| 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; | |
| } |