void hash_table_put (struct hash_table *ht, const void *key, void *value) { struct mapping *mappings = ht->mappings; int size = ht->size; int (*equals) (const void *, const void *) = ht->test_function; struct mapping *mp = mappings + HASH_POSITION (ht, key); LOOP_NON_EMPTY (mp, mappings, size) if (equals (key, mp->key)) { mp->key = (void *)key; /* const? */ mp->value = value; return; } ++ht->count; mp->key = (void *)key; /* const? */ mp->value = value; if (ht->count > ht->resize_threshold) /* When table is 75% full, regrow it. */ grow_hash_table (ht); }
static void grow_hash_table (struct hash_table *ht) { struct mapping *old_mappings = ht->mappings; struct mapping *old_end = ht->mappings + ht->size; struct mapping *mp, *mappings; int newsize; newsize = prime_size (ht->size * 2); #if 0 printf ("growing from %d to %d\n", ht->size, newsize); #endif ht->size = newsize; ht->resize_threshold = newsize * 3 / 4; mappings = xmalloc (ht->size * sizeof (struct mapping)); memset (mappings, '\0', ht->size * sizeof (struct mapping)); ht->mappings = mappings; for (mp = old_mappings; mp < old_end; mp++) if (!EMPTY_MAPPING_P (mp)) { struct mapping *new_mp = mappings + HASH_POSITION (ht, mp->key); /* We don't need to call test function and worry about collisions because all the keys come from the hash table and are therefore guaranteed to be unique. */ LOOP_NON_EMPTY (new_mp, mappings, newsize) ; *new_mp = *mp; } xfree (old_mappings); }
static inline struct mapping * find_mapping (struct hash_table *ht, const void *key) { struct mapping *mappings = ht->mappings; int size = ht->size; struct mapping *mp = mappings + HASH_POSITION (ht, key); int (*equals) (const void *, const void *) = ht->test_function; LOOP_NON_EMPTY (mp, mappings, size) if (equals (key, mp->key)) return mp; return NULL; }
static inline struct cell * find_cell (const struct hash_table *ht, const void *key) { struct cell *cells = ht->cells; int size = ht->size; struct cell *c = cells + HASH_POSITION (key, ht->hash_function, size); testfun_t equals = ht->test_function; FOREACH_OCCUPIED_ADJACENT (c, cells, size) if (equals (key, c->key)) break; return c; }
int hash_table_remove (struct hash_table *ht, const void *key) { struct cell *c = find_cell (ht, key); if (!CELL_OCCUPIED (c)) return 0; else { int size = ht->size; struct cell *cells = ht->cells; hashfun_t hasher = ht->hash_function; CLEAR_CELL (c); --ht->count; /* Rehash all the entries following C. The alternative approach is to mark the entry as deleted, i.e. create a "tombstone". That speeds up removal, but leaves a lot of garbage and slows down hash_table_get and hash_table_put. */ c = NEXT_CELL (c, cells, size); FOREACH_OCCUPIED_ADJACENT (c, cells, size) { const void *key2 = c->key; struct cell *c_new; /* Find the new location for the key. */ c_new = cells + HASH_POSITION (key2, hasher, size); FOREACH_OCCUPIED_ADJACENT (c_new, cells, size) if (key2 == c_new->key) /* The cell C (key2) is already where we want it (in C_NEW's "chain" of keys.) */ goto next_rehash; *c_new = *c; CLEAR_CELL (c); next_rehash: ; } return 1; } }
int hash_table_remove (struct hash_table *ht, const void *key) { struct mapping *mp = find_mapping (ht, key); if (!mp) return 0; else { int size = ht->size; struct mapping *mappings = ht->mappings; mp->key = NULL; --ht->count; /* Rehash all the entries following MP. The alternative approach is to mark the entry as deleted, i.e. create a "tombstone". That makes remove faster, but leaves a lot of garbage and slows down hash_table_get and hash_table_put. */ mp = NEXT_MAPPING (mp, mappings, size); LOOP_NON_EMPTY (mp, mappings, size) { const void *key2 = mp->key; struct mapping *mp_new = mappings + HASH_POSITION (ht, key2); /* Find the new location for the key. */ LOOP_NON_EMPTY (mp_new, mappings, size) if (key2 == mp_new->key) /* The mapping MP (key2) is already where we want it (in MP_NEW's "chain" of keys.) */ goto next_rehash; *mp_new = *mp; mp->key = NULL; next_rehash: ; } return 1; } }
static void grow_hash_table (struct hash_table *ht) { hashfun_t hasher = ht->hash_function; struct cell *old_cells = ht->cells; struct cell *old_end = ht->cells + ht->size; struct cell *c, *cells; int newsize; newsize = prime_size (ht->size * HASH_RESIZE_FACTOR, &ht->prime_offset); #if 0 printf ("growing from %d to %d; fullness %.2f%% to %.2f%%\n", ht->size, newsize, 100.0 * ht->count / ht->size, 100.0 * ht->count / newsize); #endif ht->size = newsize; ht->resize_threshold = newsize * HASH_MAX_FULLNESS; cells = xnew_array (struct cell, newsize); memset (cells, INVALID_PTR_CHAR, newsize * sizeof (struct cell)); ht->cells = cells; for (c = old_cells; c < old_end; c++) if (CELL_OCCUPIED (c)) { struct cell *new_c; /* We don't need to test for uniqueness of keys because they come from the hash table and are therefore known to be unique. */ new_c = cells + HASH_POSITION (c->key, hasher, newsize); FOREACH_OCCUPIED_ADJACENT (new_c, cells, newsize) ; *new_c = *c; } xfree (old_cells); }