C2x.Examples/src/hashtable/open_address/oa_hashtable.c
Source: C2x.Examples/src/hashtable/open_address/oa_hashtable.c
#include "oa_hashtable.h"
static void node_reset(struct Node *n) {
if (n == nullptr)
return;
n->text = nullptr;
n->value = nullptr;
n->bytes = 0;
n->cleanup = nullptr;
}
static void node_release(struct Node *n) {
if (n == nullptr)
return;
free(n->text);
if (n->cleanup != nullptr && n->value != nullptr)
n->cleanup(n->value);
node_reset(n);
}
char *dup_string(const char *text) {
if (text == nullptr)
return nullptr;
size_t len = strlen(text) + 1;
char *out = malloc(len);
if (out == nullptr)
return nullptr;
memcpy(out, text, len);
return out;
}
void cleanup_ptr(void *ptr) {
if (ptr != nullptr)
free(ptr);
}
size_t hash(const char *key, size_t bucket_size) {
if (key == nullptr || bucket_size == 0)
return 0;
size_t h = 1469598103934665603ull;
while (*key != '\0') {
h ^= (unsigned char)*key;
h *= 1099511628211ull;
++key;
}
return h % bucket_size;
}
static bool hash_should_grow(const struct HashTable *table) {
if (table == nullptr || table->bucket_size == 0)
return false;
size_t used = table->count + table->deleted;
return used * 100 >= table->bucket_size * 70;
}
static bool hash_reinsert_owned(struct HashTable *table, struct Node *src) {
size_t start = hash(src->text, table->bucket_size);
for (size_t probe = 0; probe < table->bucket_size; ++probe) {
size_t idx = (start + probe) % table->bucket_size;
struct HashSlot *slot = &table->slots[idx];
if (slot->state == SLOT_EMPTY) {
slot->state = SLOT_OCCUPIED;
slot->node = *src;
table->count++;
node_reset(src);
return true;
}
}
return false;
}
static bool hash_resize(struct HashTable *table, size_t new_size) {
if (table == nullptr || new_size == 0)
return false;
struct HashSlot *old_slots = table->slots;
size_t old_size = table->bucket_size;
struct HashSlot *new_slots = calloc(new_size, sizeof(*new_slots));
if (new_slots == nullptr)
return false;
table->slots = new_slots;
table->bucket_size = new_size;
table->count = 0;
table->deleted = 0;
for (size_t i = 0; i < old_size; ++i) {
if (old_slots[i].state == SLOT_OCCUPIED) {
if (!hash_reinsert_owned(table, &old_slots[i].node)) {
for (size_t j = 0; j < new_size; ++j) {
if (table->slots[j].state == SLOT_OCCUPIED)
node_release(&table->slots[j].node);
}
free(table->slots);
table->slots = old_slots;
table->bucket_size = old_size;
table->count = 0;
table->deleted = 0;
for (size_t j = 0; j < old_size; ++j) {
if (old_slots[j].state == SLOT_OCCUPIED)
table->count++;
else if (old_slots[j].state == SLOT_DELETED)
table->deleted++;
}
return false;
}
}
}
free(old_slots);
return true;
}
bool hash_init(struct HashTable *table, size_t bucket_size) {
if (table == nullptr || bucket_size == 0)
return false;
memset(table, 0, sizeof(*table));
table->slots = calloc(bucket_size, sizeof(*table->slots));
if (table->slots == nullptr)
return false;
table->bucket_size = bucket_size;
table->count = 0;
table->deleted = 0;
return true;
}
void hash_cleanup(struct HashTable *table) {
if (table == nullptr || table->slots == nullptr || table->bucket_size == 0)
return;
for (size_t i = 0; i < table->bucket_size; ++i) {
if (table->slots[i].state == SLOT_OCCUPIED)
node_release(&table->slots[i].node);
}
free(table->slots);
table->slots = nullptr;
table->bucket_size = 0;
table->count = 0;
table->deleted = 0;
}
static struct HashSlot *hash_find_slot(const struct HashTable *table, const char *text) {
if (table == nullptr || table->slots == nullptr || table->bucket_size == 0 || text == nullptr)
return nullptr;
size_t start = hash(text, table->bucket_size);
for (size_t probe = 0; probe < table->bucket_size; ++probe) {
size_t idx = (start + probe) % table->bucket_size;
struct HashSlot *slot = (struct HashSlot *)&table->slots[idx];
if (slot->state == SLOT_EMPTY)
return nullptr;
if (slot->state == SLOT_OCCUPIED && strcmp(slot->node.text, text) == 0)
return slot;
}
return nullptr;
}
struct Node *hash_lookup(const struct HashTable *table, const char *text) {
struct HashSlot *slot = hash_find_slot(table, text);
if (slot == nullptr)
return nullptr;
return &slot->node;
}
struct Node *hash_insert(struct HashTable *table, const char *text) {
if (table == nullptr || table->slots == nullptr || table->bucket_size == 0 || text == nullptr)
return nullptr;
struct HashSlot *existing = hash_find_slot(table, text);
if (existing != nullptr)
return &existing->node;
if (hash_should_grow(table)) {
size_t new_size = table->bucket_size * 2;
if (new_size < 8)
new_size = 8;
if (!hash_resize(table, new_size))
return nullptr;
}
size_t start = hash(text, table->bucket_size);
struct HashSlot *first_deleted = nullptr;
for (size_t probe = 0; probe < table->bucket_size; ++probe) {
size_t idx = (start + probe) % table->bucket_size;
struct HashSlot *slot = &table->slots[idx];
if (slot->state == SLOT_DELETED) {
if (first_deleted == nullptr)
first_deleted = slot;
continue;
}
if (slot->state == SLOT_EMPTY) {
if (first_deleted != nullptr)
slot = first_deleted;
slot->node.text = dup_string(text);
if (slot->node.text == nullptr)
return nullptr;
slot->node.value = nullptr;
slot->node.bytes = 0;
slot->node.cleanup = nullptr;
if (slot->state == SLOT_DELETED)
table->deleted--;
slot->state = SLOT_OCCUPIED;
table->count++;
return &slot->node;
}
}
if (first_deleted != nullptr) {
first_deleted->node.text = dup_string(text);
if (first_deleted->node.text == nullptr)
return nullptr;
first_deleted->node.value = nullptr;
first_deleted->node.bytes = 0;
first_deleted->node.cleanup = nullptr;
first_deleted->state = SLOT_OCCUPIED;
table->deleted--;
table->count++;
return &first_deleted->node;
}
return nullptr;
}
struct Node *hash_set(struct HashTable *table, const char *text, void *value, size_t bytes, void (*cleanup)(void *)) {
if (table == nullptr || text == nullptr)
return nullptr;
struct Node *n = hash_insert(table, text);
if (n == nullptr)
return nullptr;
if (n->cleanup != nullptr && n->value != nullptr)
n->cleanup(n->value);
n->value = value;
n->bytes = bytes;
n->cleanup = cleanup;
return n;
}
enum HASH_VALUE_RETURN hash_set_value(struct HashTable *table, const char *key, bool (*hash_setvalue)(struct Node *n), void (*cleanup)(void *)) {
if (table == nullptr || key == nullptr || hash_setvalue == nullptr)
return VALUE_ERROR;
struct Node *n = hash_lookup(table, key);
if (n == nullptr) {
n = hash_insert(table, key);
if (n == nullptr)
return VALUE_ERROR;
n->cleanup = cleanup;
if (hash_setvalue(n))
return VALUE_NEW;
return VALUE_ERROR;
}
n->cleanup = cleanup;
if (hash_setvalue(n))
return VALUE_UPDATE;
return VALUE_ERROR;
}
void hash_remove(struct HashTable *table, const char *text) {
if (table == nullptr || table->slots == nullptr || table->bucket_size == 0 || text == nullptr)
return;
struct HashSlot *slot = hash_find_slot(table, text);
if (slot == nullptr)
return;
node_release(&slot->node);
slot->state = SLOT_DELETED;
table->count--;
table->deleted++;
}
void hash_print(const struct HashTable *table) {
if (table == nullptr || table->slots == nullptr || table->bucket_size == 0)
return;
printf("Keys: {\n");
for (size_t i = 0; i < table->bucket_size; ++i) {
if (table->slots[i].state == SLOT_OCCUPIED)
printf("\t[%s]\n", table->slots[i].node.text);
}
printf("}\n");
}
size_t hash_count(struct HashTable *table) {
if (table == nullptr)
return 0;
return table->count;
}