|
|
@@ -36,8 +36,21 @@ static inline size_t hash_key(const HashTable *table, const char *key) |
|
|
|
|
|
|
|
/* |
|
|
|
Create and return a new HashTable. |
|
|
|
|
|
|
|
These HashTables are designed to be generic, a sort of poor-man's C++ |
|
|
|
template. They can store any kind of node data as long as they are structs |
|
|
|
with a char* field (for the node key), and a pointer to its own type (to |
|
|
|
implement separate chaining). |
|
|
|
|
|
|
|
key_offset is the (byte) offset of the key field, and next_offset is the |
|
|
|
offset of the self-pointer. The callback function is called on a node when |
|
|
|
it is removed from the table. Typically, it should free() the node's |
|
|
|
members and then free() the node itself. |
|
|
|
|
|
|
|
The hash_table_NEW macro can be used to call this function more easily. |
|
|
|
*/ |
|
|
|
HashTable* hash_table_new(size_t key_offset, size_t next_offset) |
|
|
|
HashTable* hash_table_new( |
|
|
|
size_t key_offset, size_t next_offset, HashFreeCallback callback) |
|
|
|
{ |
|
|
|
HashTable *table; |
|
|
|
if (!(table = malloc(sizeof(HashTable)))) |
|
|
@@ -49,17 +62,14 @@ HashTable* hash_table_new(size_t key_offset, size_t next_offset) |
|
|
|
table->buckets = INITIAL_BUCKETS; |
|
|
|
table->key_offset = key_offset; |
|
|
|
table->next_offset = next_offset; |
|
|
|
table->free = callback; |
|
|
|
return table; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
Deallocate a HashTable. This function does nothing if the table is NULL. |
|
|
|
|
|
|
|
The given callback function is called on each node as it is removed from |
|
|
|
the table. Typically, it will free() the node's members and then free() |
|
|
|
the node itself. |
|
|
|
*/ |
|
|
|
void hash_table_free(HashTable *table, HashFreeCallback callback) |
|
|
|
void hash_table_free(HashTable *table) |
|
|
|
{ |
|
|
|
if (!table) |
|
|
|
return; |
|
|
@@ -68,7 +78,7 @@ void hash_table_free(HashTable *table, HashFreeCallback callback) |
|
|
|
HashNode *node = table->nodes[bucket]; |
|
|
|
while (node) { |
|
|
|
HashNode *temp = NEXT_NODE(table, node); |
|
|
|
callback(node); |
|
|
|
table->free(node); |
|
|
|
node = temp; |
|
|
|
} |
|
|
|
} |
|
|
@@ -94,7 +104,10 @@ const HashNode* hash_table_find(const HashTable *table, const char *key) |
|
|
|
/* |
|
|
|
Insert a node into the table. |
|
|
|
|
|
|
|
This doesn't check for duplicate keys, so you must do that beforehand. |
|
|
|
This doesn't prevent inserting duplicate keys. If a duplicate key is |
|
|
|
inserted, the table acts like a stack; the new one will shadow the old one, |
|
|
|
and hash_table_remove() will remove the most-recently inserted key to |
|
|
|
reveal the second-most recent. |
|
|
|
*/ |
|
|
|
void hash_table_insert(HashTable *table, HashNode *node) |
|
|
|
{ |
|
|
@@ -102,3 +115,29 @@ void hash_table_insert(HashTable *table, HashNode *node) |
|
|
|
NEXT_NODE(table, node) = table->nodes[index]; |
|
|
|
table->nodes[index] = node; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
(Try to) remove a node with the given key from the table. |
|
|
|
|
|
|
|
Return true if the node was removed, or false if it was not found. |
|
|
|
*/ |
|
|
|
bool hash_table_remove(HashTable *table, const char *key) |
|
|
|
{ |
|
|
|
size_t index = hash_key(table, key); |
|
|
|
HashNode *node = table->nodes[index], *prev = NULL, *next; |
|
|
|
|
|
|
|
while (node) { |
|
|
|
next = NEXT_NODE(table, node); |
|
|
|
if (!strcmp(key, NODE_KEY(table, node))) { |
|
|
|
if (prev) |
|
|
|
NEXT_NODE(table, prev) = next; |
|
|
|
else |
|
|
|
table->nodes[index] = next; |
|
|
|
table->free(node); |
|
|
|
return true; |
|
|
|
} |
|
|
|
prev = node; |
|
|
|
node = next; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |