Generic hashtable in C (2): the final API
Some time back I wrote a generic hashtable in C. Since that post, the API has seen some iteration as I've been using the code in my projects.
This hashtable heavily relies on macros that call type-generic functions which modify struct members based on their offsets and do other evil stuff of that sort, all in a way that isn't necessarily completely standard-conforming nor the most efficient way to do things. In exchange, it accepts any kind of data as keys or values, and accepts any hash function that returns a size_t. Below's a short description of the final API that differs a little from the one described in the original post.
Initialization
int err;
hashtable_init(table, 16, &err);
if (err)
/* Handle error */
Insertion
uint64_t key = 12345;
uint32_t value = 54321;
size_t hash = hashtable_hash(&key, sizeof(key));
hashtable_insert(table, key, hash, value, &err);
if (err)
/* Handle error */
Searching
size_t hash = hashtable_hash(&key, sizeof(key));
uint32_t *value = hashtable_find(table, key, hash);
if (value)
/* Do something with value. */
Erasing
size_t hash = hashtable_hash(&key, sizeof(key));
hashtable_erase(table, key, hash);
Iteration
uint32_t value;
hashtable_for_each_pair(table, key, value) {
/* Do something with key and value */
}
Strings and other comlex data types
This hashtable saves a copy of each key in its entirety. For non-fixes size types, three functions must be defined compare_keys, copy_key and free_key. Below is an example of insertion into and erasure from a string-int table.
Allocation and copying function examples
{return strcmp(*(const char**)a, *(const char**)b);}
int copy_key(void *dst, const void *src, size_t size)
{
size_t len = strlen(*(const char**)src);
*(char**)dst = malloc(len + 1);
if (!*(char**)dst)
return 1;
memcpy(*(char**)dst, *(const char**)src, len + 1);
return 0;
}
void free_key(void *key)
{free(*(char**)key);}
Insertion
size_t hash = hashtable_hash(key, strlen(key));
int value = 1;
hashtable_insert_ext(table, key, hash, value, compare_keys, copy_key,
&err);
if (err)
return -1;
Erasing
size_t hash = hashtable_hash(key, strlen(key));
hashtable_erase_ext(table, key, hash, compare_keys, free_key);
Typesafe function declarators
Finally, there's a couple of convenience macros for defining type-safe inline functions for tables of a given type. Their purpose is to reduce the need to type in all of the macro parameters every time as there can be quite a lot of them otherwise.
hashtable_define_ext(str_int_table, const char *, int, compute_hash,
compare_keys, copy_key, free_key);
...
struct str_int_table table;
if (str_int_table_init(&table, 8))
/* Handle error */
if (str_int_table_insert(&table, "one", 1))
/* Handle error */
Might still be bugs in the code, but I'll fix 'em as I come across 'em.