/* * Copyright Richard Tobin 1995-9. */ #include #include #include #include #include "system.h" #include "hash.h" struct hash_table { int entries; int buckets; struct hash_entry **bucket; }; static unsigned int hash(const void *key, int len); static void rehash(HashTable table); static HashEntry hash_lookup(HashTable table, const void *key, int key_len, int *foundp, int add); static int key_compare(const void *key1, int key1_len, const void *key2, int key2_len); static void *key_copy(const void *key, int key_len); /* * Create a hash table. * init_size is the initial number of buckets, it doesn't have to be "right". */ HashTable create_hash_table(int init_size) { int s, i; HashTable table; table = Malloc(sizeof(*table)); if(!table) return 0; for(s = 256; s < init_size; s <<= 1) ; table->entries = 0; table->buckets = s; table->bucket = Malloc(s * sizeof(*table->bucket)); if(!table->bucket) return 0; for(i=0; ibucket[i] = 0; return table; } /* * Free a hash table. */ void free_hash_table(HashTable table) { int i; HashEntry entry, next; for(i=0; ibuckets; i++) for(entry = table->bucket[i]; entry; entry = next) { next = entry->next; Free(entry->key); Free(entry); } Free(table->bucket); Free(table); } int hash_count(HashTable table) { return table->entries; } HashEntry hash_find(HashTable table, const void *key, int key_len) { return hash_lookup(table, key, key_len, 0, 0); } HashEntry hash_find_or_add(HashTable table, const void *key, int key_len, int *foundp) { return hash_lookup(table, key, key_len, foundp, 1); } static HashEntry hash_lookup(HashTable table, const void *key, int key_len, int *foundp, int add) { HashEntry *entry, new; unsigned int h = hash(key, key_len); for(entry = &table->bucket[h % table->buckets]; *entry; entry = &(*entry)->next) if(key_compare((*entry)->key, (*entry)->key_len, key, key_len) == 0) break; if(foundp) *foundp = (*entry != 0); if(*entry == 0 && add == 0) return 0; if(*entry != 0) return *entry; if(table->entries > table->buckets) /* XXX arbitrary! */ { rehash(table); return hash_lookup(table, key, key_len, foundp, add); } new = Malloc(sizeof(*new)); if(!new) return 0; new->key = key_copy(key, key_len); new->key_len = key_len; new->value = 0; new->next = 0; table->entries++; *entry = new; return new; } void hash_remove(HashTable table, HashEntry entry) { unsigned int h = hash(entry->key, entry->key_len); HashEntry *e; for(e = &table->bucket[h % table->buckets]; *e; e = &(*e)->next) if(*e == entry) { *e = entry->next; Free(entry); table->entries--; return; } fprintf(stderr, "Attempt to remove non-existent entry from table\n"); abort(); } void hash_map(HashTable table, void (*function)(const HashEntryStruct *, void *), void *arg) { int i; HashEntry entry; for(i=0; ibuckets; i++) for(entry = table->bucket[i]; entry; entry = entry->next) (*function)(entry, arg); } static void rehash(HashTable table) { HashTable new; unsigned h; int i; HashEntry entry, next, *chain; /* XXX Should collect some statistics here */ new = create_hash_table(2 * table->buckets); if(!new) return; for(i=0; ibuckets; i++) { for(entry = table->bucket[i]; entry; entry = next) { next = entry->next; h = hash(entry->key, entry->key_len); chain = &new->bucket[h % new->buckets]; entry->next = *chain; *chain = entry; new->entries++; } } assert(new->entries == table->entries); Free(table->bucket); *table = *new; Free(new); } /* * Chris Torek's hash function. I don't know whether it's any good for * this... */ static unsigned int hash(const void *key, int len) { const char *k = key; unsigned int h = 0; /* should probably be 32 bits */ int i; for(i=0; i