diff --git a/include/villas/common.h b/include/villas/common.h index d6ac417a2..64de9bb8c 100644 --- a/include/villas/common.h +++ b/include/villas/common.h @@ -41,6 +41,12 @@ enum state { STATE_CLOSED = 5 /* alias for STATE_STARTED used by struct io */ }; +/** Callback to destroy list elements. + * + * @param data A pointer to the data which should be freed. + */ +typedef int (*dtor_cb_t)(void *); + #ifdef __cplusplus } #endif diff --git a/include/villas/hash_table.h b/include/villas/hash_table.h new file mode 100644 index 000000000..d8a79f873 --- /dev/null +++ b/include/villas/hash_table.h @@ -0,0 +1,75 @@ +/** A generic hash table + * + * @author Steffen Vogel + * @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include +#include + +#include + +struct hash_table_entry { + void *key; + void *data; + + struct hash_table_entry *next; +}; + +/** A thread-safe hash table using separate chaing with linked lists. */ +struct hash_table { + enum state state; + + struct hash_table_entry **table; + + size_t size; + pthread_mutex_t lock; +}; + +/** Initialize a new hash table. + * + */ +int hash_table_init(struct hash_table *ht, size_t size); + +/** Destroy a hash table. + * + * + */ +int hash_table_destroy(struct hash_table *ht, dtor_cb_t dtor, bool release); + +/** Insert a new key/value pair into the hash table. + * + */ +int hash_table_insert(struct hash_table *ht, void *key, void *data); + +/** Delete a key from the hash table. + * + * + */ +int hash_table_delete(struct hash_table *ht, void *key); + +/** Perform a lookup in the hash table. + * + * @retval != NULL The value for the given key. + * @retval NULL The given key is not stored in the hash table. + */ +void * hash_table_lookup(struct hash_table *ht, void *key); + +/** Dump the contents of the hash table in a human readable format to stdout. */ +void hash_table_dump(struct hash_table *ht); diff --git a/include/villas/list.h b/include/villas/list.h index 16d003f5c..ab79d6774 100644 --- a/include/villas/list.h +++ b/include/villas/list.h @@ -32,7 +32,7 @@ #include #include -#include "common.h" +#include #ifdef __cplusplus extern "C"{ @@ -65,12 +65,6 @@ __attribute__((destructor(105))) static void UNIQUE(__dtor)() { \ #define list_first(list) list_at(list, 0) #define list_last(list) list_at(list, (list)->length-1) -/** Callback to destroy list elements. - * - * @param data A pointer to the data which should be freed. - */ -typedef int (*dtor_cb_t)(void *); - /** Callback to search or sort a list. */ typedef int (*cmp_cb_t)(const void *, const void *); diff --git a/lib/Makefile.villas.inc b/lib/Makefile.villas.inc index dc0a3ca0a..68ff0b83d 100644 --- a/lib/Makefile.villas.inc +++ b/lib/Makefile.villas.inc @@ -31,6 +31,7 @@ LIB_SRCS += $(addprefix lib/kernel/, kernel.c rt.c) \ queue_signalled.c memory.c advio.c plugin.c node_type.c stats.c \ mapping.c shmem.c config_helper.c crypt.c compat.c \ log_helper.c task.c buffer.c table.c bitset.c signal.c \ + hash_table.c \ ) LIB_LDFLAGS += -shared diff --git a/lib/hash_table.c b/lib/hash_table.c new file mode 100644 index 000000000..2fb88aa88 --- /dev/null +++ b/lib/hash_table.c @@ -0,0 +1,202 @@ +/** A generic hash table + * + * @author Steffen Vogel + * @copyright 2017-2018, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include + +#include +#include + +static int hash_table_hash(struct hash_table *ht, void *key) +{ + uintptr_t ptr = (uintptr_t) key; + + return ptr % ht->size; +} + +int hash_table_init(struct hash_table *ht, size_t size) +{ + int ret; + size_t len = sizeof(struct hash_table_entry *) * size; + + assert(ht->state == STATE_DESTROYED); + + ret = pthread_mutex_init(&ht->lock, NULL); + if (ret) + return ret; + + ht->table = alloc(len); + + memset(ht->table, 0, len); + + ht->size = size; + ht->state = STATE_INITIALIZED; + + return 0; +} + +int hash_table_destroy(struct hash_table *ht, dtor_cb_t dtor, bool release) +{ + int ret; + struct hash_table_entry *cur, *next; + + assert(ht->state == STATE_INITIALIZED); + + pthread_mutex_lock(&ht->lock); + + for (int i = 0; i < ht->size; i++) { + for (cur = ht->table[i]; cur; cur = next) { + if (dtor) + dtor(cur->data); + + if (release) + free(cur->data); + + next = cur->next; + + free(cur); + } + } + + pthread_mutex_unlock(&ht->lock); + + ret = pthread_mutex_destroy(&ht->lock); + if (ret) + return ret; + + free(ht->table); + + ht->state = STATE_DESTROYED; + + return 0; +} + +int hash_table_insert(struct hash_table *ht, void *key, void *data) +{ + int ret, ikey = hash_table_hash(ht, key); + struct hash_table_entry *hte, *cur; + + assert(ht->state == STATE_INITIALIZED); + + pthread_mutex_lock(&ht->lock); + + /* Check that the key is not already in the table */ + for (cur = ht->table[ikey]; + cur && cur->key != key; + cur = cur->next); + + if (cur) + ret = -1; + else { + hte = alloc(sizeof(struct hash_table_entry)); + if (hte) { + hte->key = key; + hte->data = data; + hte->next = NULL; + + if ((cur = ht->table[ikey])) + hte->next = ht->table[ikey]; + + ht->table[ikey] = hte; + + ret = 0; + } + else + ret = -1; + } + + pthread_mutex_unlock(&ht->lock); + + return ret; +} + +#include + +int hash_table_delete(struct hash_table *ht, void *key) +{ + int ret, ikey = hash_table_hash(ht, key); + struct hash_table_entry *cur, *prev; + + assert(ht->state == STATE_INITIALIZED); + + pthread_mutex_lock(&ht->lock); + + for (prev = NULL, + cur = ht->table[ikey]; + cur && cur->key != key; + prev = cur, + cur = cur->next); + + if (cur) { + if (prev) + prev->next = cur->next; + else + ht->table[ikey] = cur->next; + + free(cur); + + ret = 0; + } + else + ret = -1; /* not found */ + + pthread_mutex_unlock(&ht->lock); + + return ret; +} + +void * hash_table_lookup(struct hash_table *ht, void *key) +{ + int ikey = hash_table_hash(ht, key); + struct hash_table_entry *hte; + + assert(ht->state == STATE_INITIALIZED); + + pthread_mutex_lock(&ht->lock); + + for (hte = ht->table[ikey]; + hte && hte->key != key; + hte = hte->next); + + void *data = hte ? hte->data : NULL; + + pthread_mutex_unlock(&ht->lock); + + return data; +} + +void hash_table_dump(struct hash_table *ht) +{ + struct hash_table_entry *hte; + + assert(ht->state == STATE_INITIALIZED); + + pthread_mutex_lock(&ht->lock); + + for (int i = 0; i < ht->size; i++) { + printf("%i: ", i); + for (hte = ht->table[i]; hte; hte = hte->next) + printf("%p->%p ", hte->key, hte->data); + printf("\n"); + } + + pthread_mutex_unlock(&ht->lock); +}