diff --git a/lib/types/hashtable.c b/lib/types/hashtable.c index a55e4478aa5f42bc688bc2ac615b4f10aa51e2cf..419e8be7486ef5d30aacf45526ad355a99122b37 100644 --- a/lib/types/hashtable.c +++ b/lib/types/hashtable.c @@ -5,11 +5,7 @@ #include <stdlib.h> #include <string.h> -typedef struct -{ - char *key; - char *value; -} KeyValue; +#define HASH_SIZE 16 /** * HashTable: @@ -19,36 +15,119 @@ typedef struct */ struct _HashTable { - SList **array; - size_t size; + SList **array; + + HashFunc hash_func; + HashEqualFunc equal_func; + FreeFunc free_key_func; + FreeFunc free_value_func; }; +typedef struct +{ + void *key; + void *value; +} KeyValue; + +static KeyValue * +key_value_new (const void *key, + const void *value) +{ + KeyValue *kv = check_malloc (sizeof (KeyValue)); + + kv->key = (void *) key; + kv->value = (void *) value; + + return kv; +} + +static void +key_value_free (KeyValue *kv, + FreeFunc key_func, + FreeFunc val_func) +{ + if (key_func) + key_func (kv->key); + + if (val_func) + val_func (kv->value); + + free (kv); +} + /** * hash_table_new: - * @size: Number of buckets to use. + * @hash_func: A hash function for the key. + * @equal_func: A function to check for equality. + * @free_key_func: A function to free keys. + * @free_value_func: A function to free values. * * Creates a new #HashTable. */ HashTable * -hash_table_new (size_t size) +hash_table_new (HashFunc hash_func, + HashEqualFunc equal_func, + FreeFunc free_key_func, + FreeFunc free_value_func) + { - HashTable *new = malloc (sizeof (HashTable)); - new->array = calloc (size, sizeof (SList *)); - new->size = size; + HashTable *new = check_malloc (sizeof (HashTable)); + + new->array = calloc (HASH_SIZE, sizeof (SList *)); + new->hash_func = hash_func; + new->equal_func = equal_func; + new->free_key_func = free_key_func; + new->free_value_func = free_value_func; - return new; + return new; } -static unsigned -strhash (const char *string) +/** + * hash_table_free: + * @self: A #HashTable. + * + * Frees the memory taken by a #HashTable. + */ +void +hash_table_free (HashTable *self) { - unsigned h = 5381; - char c; + for (size_t i = 0; i < HASH_SIZE; i++) + { + SList *bucket = self->array[i]; - while ((c = *string++)) - h = (h << 5) + h + c; + // Can't use slist_free all here because of separate key and value frees + for (SList *l = bucket; l; l = l->next) + { + KeyValue *kv = l->data; + key_value_free (kv, self->free_key_func, self->free_value_func); + } + } +} - return h; +/** + * hash_table_lookup: + * @self: A #HashTable. + * @key: A key to look up. + * + * Finds the value associated with @key. + * + * Returns: The value. + */ +const void * +hash_table_lookup (HashTable *self, + const void *key) +{ + unsigned idx = self->hash_func (key) % HASH_SIZE; + SList *bucket = self->array[idx]; + + for (SList *el = bucket; el; el = el->next) + { + KeyValue *kv = el->data; + if (self->equal_func (key, kv->key)) + return kv->value; + } + + return NULL; } /** @@ -59,43 +138,39 @@ strhash (const char *string) * * Inserts a new key and value into a #HashTable. * - * Duplicates are ignored, and @key and @value are copied. + * Duplicates are ignored, @key and @value are not copied. */ void hash_table_insert (HashTable *table, - const char *key, - const char *value) + const void *key, + const void *value) { - unsigned index = strhash (key) % table->size; + unsigned index = table->hash_func (key) % HASH_SIZE; - KeyValue *kv = check_malloc (sizeof (KeyValue)); - kv->key = strdup (key); - kv->value = strdup (value); - - SList *list = slist_prepend (table->array[index], kv); - table->array[index] = list; + KeyValue *kv = key_value_new (key, value); + SList *list = slist_prepend (table->array[index], kv); + table->array[index] = list; } /** - * hash_table_print_all: + * hash_table_foreach: * @table: a #HashTable. * - * Prints out the contents of the #HashTable, one key-value pair per line, - * in the format "key=value". + * Runs a function on the contents of a #HashTable. */ void -hash_table_print_all (HashTable *table) +hash_table_foreach (HashTable *self, + HashIterFunc func, + void *user_data) { - for (size_t i = 0; i < table->size; i++) - { - SList *bucket = table->array[i]; - if (bucket == NULL) - continue; - - for (SList *list = bucket; list; list = list->next) - { - KeyValue *kv = list->data; - printf ("%s:%s\n", kv->key, kv->value); - } - } + for (size_t i = 0; i < HASH_SIZE; i++) + { + SList *bucket = self->array[i]; + + for (SList *el = bucket; el; el = el->next) + { + KeyValue *kv = el->data; + func (kv->key, kv->value, user_data); + } + } } diff --git a/lib/types/hashtable.h b/lib/types/hashtable.h index 03d02f480f65d28130979fa680faeb7eaee4e50c..bd7f71c815bc3f62c9cc4fef083adba07f7d0f2a 100644 --- a/lib/types/hashtable.h +++ b/lib/types/hashtable.h @@ -1,13 +1,23 @@ #pragma once -#include <stddef.h> - #include "slist.h" +#include "types.h" typedef struct _HashTable HashTable; -HashTable * hash_table_new (size_t size); -void hash_table_insert (HashTable *table, - const char *key, - const char *value); -void hash_table_print_all (HashTable *table); + +HashTable * hash_table_new (HashFunc hash_func, + HashEqualFunc equal_func, + FreeFunc free_key_func, + FreeFunc free_value_func); +void hash_table_free (HashTable *self); + +void hash_table_insert (HashTable *table, + const void *key, + const void *value); +const void * hash_table_lookup (HashTable *self, + const void *key); + +void hash_table_foreach (HashTable *self, + HashIterFunc func, + void *user_data); diff --git a/lib/types/types.h b/lib/types/types.h index 4db195422e4ef563d4f26726f4e5cb5c5e40c574..c7c115b55a87ba902409f7297801cdd6651e9232 100644 --- a/lib/types/types.h +++ b/lib/types/types.h @@ -1,8 +1,17 @@ #pragma once +#include <stdbool.h> + typedef void (*FreeFunc) (void *data); typedef void (*ForEachFunc) (void *data, void *user_data); typedef double (*ValueFunc) (const void *element); typedef int (*CompareFunc) (const void *a, const void *b); + +typedef unsigned (*HashFunc) (const void *key); +typedef bool (*HashEqualFunc) (const void *a, + const void *b); +typedef void (*HashIterFunc) (void *key, + void *value, + void *user_data); diff --git a/src/hashtable-test.c b/src/hashtable-test.c index 0ac02e708323008f3e6b30ba678551580a466ddb..392cade6374d154214d424d933d275d39319089c 100644 --- a/src/hashtable-test.c +++ b/src/hashtable-test.c @@ -1,33 +1,64 @@ -#include "hashtable.h" - #include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <hashtable.h> static void add_data (HashTable *table) { - char key[20]; - char value[20]; + char key[20]; + char value[20]; + + printf ("Input the key "); + scanf ("%s", key); + printf ("Input the value "); + scanf ("%s", value); + + hash_table_insert (table, strdup (key), strdup (value)); + + char *key_lookup = strndup (key, strlen (key)); + printf ("The last one inserted was: {'%s': ", key_lookup); + const char *val = hash_table_lookup (table, key); + printf ("'%s'}\n", val); + + free (key_lookup); +} + +static void +print_all (void *key, + void *value, + void *user_data) +{ + printf ("%s:%s\n", (const char *) key, (const char *) value); +} - printf ("Input the key "); - scanf ("%s", key); - printf ("Input the value "); - scanf ("%s", value); +static unsigned +str_hash (const void *data) +{ + const char *str = (const char *) data; + return (unsigned) str[0]; +} - hash_table_insert (table, key, value); +static bool +str_equal (const void *a, + const void *b) +{ + return !strcmp (a, b); } int main (void) { - HashTable *table; + HashTable *table; - table = hash_table_new (16); + table = hash_table_new (str_hash, str_equal, free, free); - while (1) - { - add_data (table); - hash_table_print_all (table); - } + while (1) + { + add_data (table); + hash_table_foreach (table, print_all, NULL); + } - return 0; + return 0; }