void hash_resize(struct hashtable *table, int buckets) { struct entry **new_buckets; if ((new_buckets = (struct entry **)malloc( sizeof(struct entry *) * buckets )) == NULL) return; // Set all buckets to NULL (empty linked lists) for (int i = 0; i < buckets; i++) (new_buckets)[i] = NULL; // Iterate over all existing buckets for (int i = 0; i < table->num_buckets; i++) { // Iterate over each entry in the existing bucket, // rehashing it and adding it to a bucket (or creating // a bucket) in the new list of buckets. struct entry *entry = table->buckets[i]; while (entry != NULL) { struct entry *next = entry->next; int new_index = bucket_index(entry->key, buckets); add_entry(&(new_buckets[new_index]), entry); // Get next entry entry = next; } } // Free the buckets! free(table->buckets); table->buckets = new_buckets; table->num_buckets = buckets; }
bool hash_insert(struct hashtable *table, const char *key, const void *value) { // Find relevant bucket and keep index (used to find reference to bucket in buckets array) int index = bucket_index(key, table->num_buckets); struct entry *bucket = table->buckets[index]; // Check the table for an existing entry (bucket is first entry, as it // represents the head of the linked list) for (struct entry *entry = bucket; entry != NULL; entry = entry->next) { if (!strcmp(entry->key, key)) { entry->value = value; return true; } } // Ensure that the table will not exceed the maximum load factor // with the new number of entries; if it will, resize it to double the // number of buckets, and set the new index/bucket. if ( (float)(table->num_entries + 1) / table->num_buckets > MAX_LOAD_FACTOR ) { hash_resize(table, table->num_buckets * 2); index = bucket_index(key, table->num_buckets); bucket = table->buckets[index]; } // Create the new entry to add to the table struct entry *new_entry; if ((new_entry = (struct entry *)malloc( sizeof(struct entry) )) == NULL) return false; new_entry->key = key; new_entry->value = value; new_entry->next = NULL; add_entry(&(table->buckets[index]), new_entry); // Increment the number of table entries ++table->num_entries; return true; }
void* ht_find( struct ht const* ht, void const* cmp, struct r_set_cfg const* cfg ) { r_hash hash = cfg->hashf(cmp); size_t i = bucket_index(ht, hash); ht_dbg("Finding element with hash %zi in bucket %zi", hash, i); return avl_find(&ht->buckets[i].avl, hash, cmp, cfg); }
int ht_insert( struct ht* ht, void* data, struct r_set_cfg const* cfg ) { r_hash hash = cfg->hashf(data); // this is equivalent to hash / 2^(BITCOUNT(hash) - ht->sizeexp) due to the // right shift size_t i = bucket_index(ht, hash); ht_dbg("Adding element %p with hash %zi in bucket %zi", data, hash, i); return avl_insert(&ht->buckets[i].avl, hash, data, cfg); }
const void *hash_get(struct hashtable *table, const char *key) { int index = bucket_index(key, table->num_buckets); struct entry *bucket = table->buckets[index]; // If bucket exists, traverse the list if (bucket != NULL) { for (struct entry *entry = bucket; entry != NULL; entry = entry->next) { // Check to see if entry key matches if (!strcmp(entry->key, key)) return entry->value; } } return NULL; }
const void *hash_delete(struct hashtable *table, const char *key) { int index = bucket_index(key, table->num_buckets); struct entry *bucket = table->buckets[index]; // Stop if bucket doesn't exist if (!bucket) return NULL; struct entry *prev = NULL; for (struct entry *entry = bucket; entry != NULL; prev = entry, entry = entry->next) { // Find element with same key in bucket if (!strcmp(entry->key, key)) { const void *value = entry->value; // Remove from linked list chain, if it was not the head. // Otherwise, make the next element the head (which may be NULL). if (prev != NULL) prev->next = entry->next; else table->buckets[index] = entry->next; // Will set to NULL if it is the only element // Free the entry (it was malloc'd upon insertion) free(entry); // Decrement number of entries table->num_entries--; return value; } } return NULL; }
void test_bucket_index() { uint64_t digest = 11538222250332604846LLU; sassert(5 == bucket_index(digest, 3)); sassert(1 == bucket_index(digest, 1)); sassert(160 == bucket_index(digest, 8)); }