// hash table implementation static int hashword(uint32 c) { // Bob Jenkin's mix function, possibly overkill for only 32 bits? // but a simpler one was no faster, so what the heck uint32 a,b; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ a -= b; a -= c; a ^= (c>>13); b -= c; b -= a; b ^= (a<<8); c -= a; c -= b; c ^= (b>>13); a -= b; a -= c; a ^= (c>>12); b -= c; b -= a; b ^= (a<<16); c -= a; c -= b; c ^= (b>>5); a -= b; a -= c; a ^= (c>>3); b -= c; b -= a; b ^= (a<<10); c -= a; c -= b; c ^= (b>>15); return c; } #define KEY_NULL 0 #define KEY_DELETED 1 typedef struct { uint32 key, value; } Item; typedef struct { int mask; Item *data; int has_key[2]; uint32 value[2]; // handle out of bound values int data_len; int population, deletes; int grow_size, shrink_size, delete_size; // cache for speed void *free_ptr; } Hash; #define OVERAGE 1 // allocate extra because of either weird memory errors in win98 // or a bug in the hash table code itself void *hashCreate(int size) { Hash *h = malloc(sizeof(*h)); if (size < 8) size = 8; assert(nongreater_power_of_two(size) == size); h->data_len = size; h->mask = size-1; h->population = h->deletes = 0; h->has_key[0] = h->has_key[1] = 0; h->grow_size = (int) (size * hash_table_max_full) - 1; if (h->grow_size < 2) h->grow_size = 2; h->shrink_size = (int) (size * hash_table_empty); if (h->data_len == 8) h->shrink_size = 0; h->delete_size = (int) (size * hash_delete_rehash) - 1; h->data = calloc(sizeof(h->data[0]), size+OVERAGE*2); h->free_ptr = h->data; h->data += OVERAGE; return h; } void hashFree(void *h) { free(((Hash *) h)->free_ptr); free(h); } uint32 *hashFind(void *hash, uint32 key) { Hash *d = (Hash *) hash; Item *z = d->data; uint32 mask = d->mask; uint32 h = hashword(key); uint32 p = h & mask; uint32 s; if (key <= KEY_DELETED) { if (d->has_key[key]) return &d->value[key]; return NULL; } if (z[p].key == key) return &z[p].value; if (z[p].key == KEY_NULL) return NULL; s = (((h>> 16) | (h << 16)) & mask) | 1; assert(d->population + d->deletes != d->data_len); do { p = (p + s) & mask; if (z[p].key == key) return &z[p].value; } while (z[p].key != KEY_NULL); return NULL; } static void rehash(Hash *h, int len) { Hash *d = hashCreate(len); int i; ++rehashes; for (i=0; i < h->data_len; ++i) { if (h->data[i].key> KEY_DELETED) { uint32 *p = hashInsert(d, h->data[i].key); assert(p != NULL); *p = h->data[i].value; } } assert(h->population == d->population); free(h->free_ptr); for (i=0; i < 2; ++i) d->has_key[i] = h->has_key[i], d->value[i] = h->value[i]; memcpy(h, d, sizeof(*h)); free(d); } uint32 *hashInsert(void *hash, uint32 key) { Hash *d = (Hash *) hash; Item *z = d->data; uint32 mask = d->mask; int f = -1; uint32 h = hashword(key); uint32 p = h & mask; uint32 s; if (key <= KEY_DELETED) { d->has_key[key] = 1; return &d->value[key]; } if (z[p].key == key) return &z[p].value; if (z[p].key == KEY_DELETED) f = p; if (z[p].key> KEY_NULL) { s = (((h>> 16) | (h << 16)) & mask) | 1; assert(d->population + d->deletes != d->data_len); do { p = (p + s) & mask; if (z[p].key == key) return &z[p].value; if (z[p].key == KEY_DELETED && f == -1) f = p; } while (z[p].key> KEY_NULL); } if (z[p].key == KEY_NULL && d->population>= d->grow_size) { rehash(d, d->data_len * 2); return hashInsert(hash, key); } else if (d->population + d->deletes> d->delete_size) { rehash(d, d->data_len); return hashInsert(hash, key); } else { if (f>= 0) p = f; if (z[p].key == KEY_DELETED) --d->deletes; z[p].key = key; ++d->population; return &z[p].value; } } int hashDelete(void *hash, uint32 key) { Hash *h = (Hash *) hash; Item *p; uint32 *value = hashFind(hash, key); if (value == NULL) return 0; if (key <= KEY_DELETED) { h->has_key[key] = 0; return 1; } p = (Item *) (value-1); p->key = KEY_DELETED; --h->population; ++h->deletes; if (h->population < h->shrink_size) rehash(h, h->data_len>> 1); else if (h->population + h->deletes>= h->delete_size) rehash(h, h->data_len); return 1; } int hashCount(void *hash) { Hash *h = hash; return h->population + h->has_key[KEY_NULL] + h->has_key[KEY_DELETED]; } int hashMem(void *hash) { return sizeof(Hash) + sizeof(Item) * ((Hash *) hash)->data_len; }