diff --git a/common/utils/collection/hashtable/hashtable.c b/common/utils/collection/hashtable/hashtable.c new file mode 100755 index 0000000000000000000000000000000000000000..9c82f474194050e7b23b207886ea1e8a1b6df9d2 --- /dev/null +++ b/common/utils/collection/hashtable/hashtable.c @@ -0,0 +1,286 @@ +/* from: http://en.literateprograms.org/Hash_table_%28C%29#chunk%20def:node + * + */ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "hashtable.h" + + +//------------------------------------------------------------------------------------------------------------------------------- +char* hashtble_rc_code2string(hashtable_rc_t rcP) +//------------------------------------------------------------------------------------------------------------------------------- +{ + switch (rcP) { + case HASH_TABLE_OK: return "HASH_TABLE_OK";break; + case HASH_TABLE_INSERT_OVERWRITTEN_DATA: return "HASH_TABLE_INSERT_OVERWRITTEN_DATA";break; + case HASH_TABLE_KEY_NOT_EXISTS: return "HASH_TABLE_KEY_NOT_EXISTS";break; + case HASH_TABLE_KEY_ALREADY_EXISTS: return "HASH_TABLE_KEY_ALREADY_EXISTS";break; + case HASH_TABLE_BAD_PARAMETER_HASHTABLE: return "HASH_TABLE_BAD_PARAMETER_HASHTABLE";break; + default: return "UNKNOWN hashtable_rc_t"; + } +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * free int function + * hash_free_int_func() is used when this hashtable is used to store int values as data (pointer = value). + */ + +void hash_free_int_func(void* memoryP){} + +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Default hash function + * def_hashfunc() is the default used by hashtable_create() when the user didn't specify one. + * This is a simple/naive hash function which adds the key's ASCII char values. It will probably generate lots of collisions on large hash tables. + */ + +static hash_size_t def_hashfunc(const uint64_t keyP) +{ + return (hash_size_t)keyP; +} + +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Initialisation + * hashtable_create() sets up the initial structure of the hash table. The user specified size will be allocated and initialized to NULL. + * The user can also specify a hash function. If the hashfunc argument is NULL, a default hash function is used. + * If an error occurred, NULL is returned. All other values in the returned hash_table_t pointer should be released with hashtable_destroy(). + */ +hash_table_t *hashtable_create(hash_size_t sizeP, hash_size_t (*hashfuncP)(const uint64_t ), void (*freefuncP)(void*)) +{ + hash_table_t *hashtbl; + + if(!(hashtbl=malloc(sizeof(hash_table_t)))) return NULL; + + if(!(hashtbl->nodes=calloc(sizeP, sizeof(hash_node_t*)))) { + free(hashtbl); + return NULL; + } + + hashtbl->size=sizeP; + + if(hashfuncP) hashtbl->hashfunc=hashfuncP; + else hashtbl->hashfunc=def_hashfunc; + + if(freefuncP) hashtbl->freefunc=freefuncP; + else hashtbl->freefunc=free; + + return hashtbl; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Cleanup + * The hashtable_destroy() walks through the linked lists for each possible hash value, and releases the elements. It also releases the nodes array and the hash_table_t. + */ +hashtable_rc_t hashtable_destroy(hash_table_t *hashtblP) +{ + hash_size_t n; + hash_node_t *node, *oldnode; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + + for(n=0; n<hashtblP->size; ++n) { + node=hashtblP->nodes[n]; + while(node) { + oldnode=node; + node=node->next; + if (oldnode->data) { + hashtblP->freefunc(oldnode->data); + } + free(oldnode); + } + } + free(hashtblP->nodes); + free(hashtblP); + return HASH_TABLE_OK; +} +//------------------------------------------------------------------------------------------------------------------------------- +hashtable_rc_t hashtable_is_key_exists (hash_table_t *hashtblP, const uint64_t keyP) +//------------------------------------------------------------------------------------------------------------------------------- +{ + hash_node_t *node; + hash_size_t hash; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + + hash=hashtblP->hashfunc(keyP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + return HASH_TABLE_OK; + } + node=node->next; + } + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +hashtable_rc_t hashtable_apply_funct_on_elements (hash_table_t *hashtblP, void functP(uint64_t keyP, void* dataP, void* parameterP), void* parameterP) +//------------------------------------------------------------------------------------------------------------------------------- +{ + hash_node_t *node = NULL; + unsigned int i = 0; + unsigned int num_elements = 0; + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + while ((num_elements < hashtblP->num_elements) && (i < hashtblP->size)) { + if (hashtblP->nodes[i] != NULL) { + node=hashtblP->nodes[i]; + while(node) { + num_elements += 1; + functP(node->key, node->data, parameterP); + node=node->next; + } + } + i += 1; + } + return HASH_TABLE_OK; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Adding a new element + * To make sure the hash value is not bigger than size, the result of the user provided hash function is used modulo size. + */ +hashtable_rc_t hashtable_insert(hash_table_t *hashtblP, const uint64_t keyP, void *dataP) +{ + hash_node_t *node; + hash_size_t hash; + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP)%hashtblP->size; + + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + if (node->data) { + hashtblP->freefunc(node->data); + } + node->data=dataP; + return HASH_TABLE_INSERT_OVERWRITTEN_DATA; + } + node=node->next; + } + if(!(node=malloc(sizeof(hash_node_t)))) return -1; + node->key=keyP; + node->data=dataP; + if (hashtblP->nodes[hash]) { + node->next=hashtblP->nodes[hash]; + } else { + node->next = NULL; + } + hashtblP->nodes[hash]=node; + hashtblP->num_elements += 1; + return HASH_TABLE_OK; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * To remove an element from the hash table, we just search for it in the linked list for that hash value, + * and remove it if it is found. If it was not found, it is an error and -1 is returned. + */ +hashtable_rc_t hashtable_remove(hash_table_t *hashtblP, const uint64_t keyP) +{ + hash_node_t *node, *prevnode=NULL; + hash_size_t hash; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key != keyP) { + if(prevnode) prevnode->next=node->next; + else hashtblP->nodes[hash]=node->next; + if (node->data) { + hashtblP->freefunc(node->data); + } + free(node); + hashtblP->num_elements -= 1; + return HASH_TABLE_OK; + } + prevnode=node; + node=node->next; + } + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Searching for an element is easy. We just search through the linked list for the corresponding hash value. + * NULL is returned if we didn't find it. + */ +hashtable_rc_t hashtable_get(hash_table_t *hashtblP, const uint64_t keyP, void** dataP) +{ + hash_node_t *node; + hash_size_t hash; + + if (hashtblP == NULL) { + *dataP = NULL; + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP)%hashtblP->size; +/* fprintf(stderr, "hashtable_get() key=%s, hash=%d\n", key, hash);*/ + + node=hashtblP->nodes[hash]; + + while(node) { + if(node->key == keyP) { + *dataP = node->data; + return HASH_TABLE_OK; + } + node=node->next; + } + *dataP = NULL; + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Resizing + * The number of elements in a hash table is not always known when creating the table. + * If the number of elements grows too large, it will seriously reduce the performance of most hash table operations. + * If the number of elements are reduced, the hash table will waste memory. That is why we provide a function for resizing the table. + * Resizing a hash table is not as easy as a realloc(). All hash values must be recalculated and each element must be inserted into its new position. + * We create a temporary hash_table_t object (newtbl) to be used while building the new hashes. + * This allows us to reuse hashtable_insert() and hashtable_remove(), when moving the elements to the new table. + * After that, we can just free the old table and copy the elements from newtbl to hashtbl. + */ + +hashtable_rc_t hashtable_resize(hash_table_t *hashtblP, hash_size_t sizeP) +{ + hash_table_t newtbl; + hash_size_t n; + hash_node_t *node,*next; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + + newtbl.size = sizeP; + newtbl.hashfunc = hashtblP->hashfunc; + + if(!(newtbl.nodes=calloc(sizeP, sizeof(hash_node_t*)))) return -1; + + for(n=0; n<hashtblP->size; ++n) { + for(node=hashtblP->nodes[n]; node; node=next) { + next = node->next; + hashtable_insert(&newtbl, node->key, node->data); + // Lionel GAUTHIER: BAD CODE TO BE REWRITTEN + hashtable_remove(hashtblP, node->key); + + } + } + + free(hashtblP->nodes); + hashtblP->size=newtbl.size; + hashtblP->nodes=newtbl.nodes; + + return HASH_TABLE_OK; +} + + + diff --git a/common/utils/collection/hashtable/hashtable.h b/common/utils/collection/hashtable/hashtable.h new file mode 100755 index 0000000000000000000000000000000000000000..b76b88506fda53b9a00aafda70ea8cd903f611f7 --- /dev/null +++ b/common/utils/collection/hashtable/hashtable.h @@ -0,0 +1,47 @@ +#ifndef _UTILS_COLLECTION_HASH_TABLE_H_ +#define _UTILS_COLLECTION_HASH_TABLE_H_ +#include<stdlib.h> +#include <stdint.h> +#include <stddef.h> + +typedef size_t hash_size_t; + +typedef enum hashtable_return_code_e { + HASH_TABLE_OK = 0, + HASH_TABLE_INSERT_OVERWRITTEN_DATA = 1, + HASH_TABLE_KEY_NOT_EXISTS = 2, + HASH_TABLE_KEY_ALREADY_EXISTS = 3, + HASH_TABLE_BAD_PARAMETER_HASHTABLE = 4, + HASH_TABLE_CODE_MAX +} hashtable_rc_t; + + +typedef struct hash_node_s { + uint64_t key; + void *data; + struct hash_node_s *next; +} hash_node_t; + +typedef struct hash_table_s { + hash_size_t size; + hash_size_t num_elements; + struct hash_node_s **nodes; + hash_size_t (*hashfunc)(const uint64_t); + void (*freefunc)(void*); +} hash_table_t; + +char* hashtble_rc_code2string(hashtable_rc_t rcP); +void hash_free_int_func(void* memoryP); +hash_table_t *hashtable_create (hash_size_t size, hash_size_t (*hashfunc)(const uint64_t ), void (*freefunc)(void*)); +hashtable_rc_t hashtable_destroy(hash_table_t *hashtbl); +hashtable_rc_t hashtable_is_key_exists (hash_table_t *hashtbl, const uint64_t key); +hashtable_rc_t hashtable_apply_funct_on_elements (hash_table_t *hashtblP, void funct(uint64_t keyP, void* dataP, void* parameterP), void* parameterP); +hashtable_rc_t hashtable_insert (hash_table_t *hashtbl, const uint64_t key, void *data); +hashtable_rc_t hashtable_remove (hash_table_t *hashtbl, const uint64_t key); +hashtable_rc_t hashtable_get (hash_table_t *hashtbl, const uint64_t key, void **dataP); +hashtable_rc_t hashtable_resize (hash_table_t *hashtbl, hash_size_t size); + + + +#endif + diff --git a/common/utils/collection/hashtable/obj_hashtable.c b/common/utils/collection/hashtable/obj_hashtable.c new file mode 100755 index 0000000000000000000000000000000000000000..192a9f28eeca8e2608a4699e46413b89ec9bc75f --- /dev/null +++ b/common/utils/collection/hashtable/obj_hashtable.c @@ -0,0 +1,238 @@ +/* from: http://en.literateprograms.org/Hash_table_%28C%29#chunk%20def:node + * + */ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "obj_hashtable.h" +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Default hash function + * def_hashfunc() is the default used by hashtable_create() when the user didn't specify one. + * This is a simple/naive hash function which adds the key's ASCII char values. It will probably generate lots of collisions on large hash tables. + */ + +static hash_size_t def_hashfunc(const void *keyP, int key_sizeP) +{ + hash_size_t hash=0; + + while(key_sizeP) hash^=((unsigned char*)keyP)[key_sizeP --]; + + return hash; +} + +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Initialisation + * hashtable_create() sets up the initial structure of the hash table. The user specified size will be allocated and initialized to NULL. + * The user can also specify a hash function. If the hashfunc argument is NULL, a default hash function is used. + * If an error occurred, NULL is returned. All other values in the returned obj_hash_table_t pointer should be released with hashtable_destroy(). + */ +obj_hash_table_t *obj_hashtable_create(hash_size_t sizeP, hash_size_t (*hashfuncP)(const void*, int ), void (*freekeyfuncP)(void*), void (*freedatafuncP)(void*)) +{ + obj_hash_table_t *hashtbl; + + if(!(hashtbl=malloc(sizeof(obj_hash_table_t)))) return NULL; + + if(!(hashtbl->nodes=calloc(sizeP, sizeof(obj_hash_node_t*)))) { + free(hashtbl); + return NULL; + } + + hashtbl->size=sizeP; + + if(hashfuncP) hashtbl->hashfunc=hashfuncP; + else hashtbl->hashfunc=def_hashfunc; + + if(freekeyfuncP) hashtbl->freekeyfunc=freekeyfuncP; + else hashtbl->freekeyfunc=free; + + if(freedatafuncP) hashtbl->freedatafunc=freedatafuncP; + else hashtbl->freedatafunc=free; + + return hashtbl; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Cleanup + * The hashtable_destroy() walks through the linked lists for each possible hash value, and releases the elements. It also releases the nodes array and the obj_hash_table_t. + */ +hashtable_rc_t obj_hashtable_destroy(obj_hash_table_t *hashtblP) +{ + hash_size_t n; + obj_hash_node_t *node, *oldnode; + + for(n=0; n<hashtblP->size; ++n) { + node=hashtblP->nodes[n]; + while(node) { + oldnode=node; + node=node->next; + hashtblP->freekeyfunc(oldnode->key); + hashtblP->freedatafunc(oldnode->data); + free(oldnode); + } + } + free(hashtblP->nodes); + free(hashtblP); + return HASH_TABLE_OK; +} +//------------------------------------------------------------------------------------------------------------------------------- +hashtable_rc_t obj_hashtable_is_key_exists (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP) +//------------------------------------------------------------------------------------------------------------------------------- +{ + obj_hash_node_t *node; + hash_size_t hash; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + return HASH_TABLE_OK; + } + node=node->next; + } + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Adding a new element + * To make sure the hash value is not bigger than size, the result of the user provided hash function is used modulo size. + */ +hashtable_rc_t obj_hashtable_insert(obj_hash_table_t *hashtblP, void* keyP, int key_sizeP, void *dataP) +{ + obj_hash_node_t *node; + hash_size_t hash; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + if (node->data) { + hashtblP->freedatafunc(node->data); + } + node->data=dataP; + // waste of memory here (keyP is lost) we should free it now + return HASH_TABLE_INSERT_OVERWRITTEN_DATA; + } + node=node->next; + } + if(!(node=malloc(sizeof(obj_hash_node_t)))) return -1; + node->key=keyP; + node->data=dataP; + if (hashtblP->nodes[hash]) { + node->next=hashtblP->nodes[hash]; + } else { + node->next = NULL; + } + hashtblP->nodes[hash]=node; + return HASH_TABLE_OK; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * To remove an element from the hash table, we just search for it in the linked list for that hash value, + * and remove it if it is found. If it was not found, it is an error and -1 is returned. + */ +hashtable_rc_t obj_hashtable_remove(obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP) +{ + obj_hash_node_t *node, *prevnode=NULL; + hash_size_t hash; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + + hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + if(prevnode) { + prevnode->next=node->next; + } else { + hashtblP->nodes[hash]=node->next; + } + hashtblP->freekeyfunc(node->key); + hashtblP->freedatafunc(node->data); + free(node); + return HASH_TABLE_OK; + } + prevnode=node; + node=node->next; + } + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Searching for an element is easy. We just search through the linked list for the corresponding hash value. + * NULL is returned if we didn't find it. + */ +hashtable_rc_t obj_hashtable_get(obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP, void** dataP) +{ + obj_hash_node_t *node; + hash_size_t hash; + + if (hashtblP == NULL) { + *dataP = NULL; + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size; + node=hashtblP->nodes[hash]; + while(node) { + if(node->key == keyP) { + *dataP = node->data; + return HASH_TABLE_OK; + } + node=node->next; + } + *dataP = NULL; + return HASH_TABLE_KEY_NOT_EXISTS; +} +//------------------------------------------------------------------------------------------------------------------------------- +/* + * Resizing + * The number of elements in a hash table is not always known when creating the table. + * If the number of elements grows too large, it will seriously reduce the performance of most hash table operations. + * If the number of elements are reduced, the hash table will waste memory. That is why we provide a function for resizing the table. + * Resizing a hash table is not as easy as a realloc(). All hash values must be recalculated and each element must be inserted into its new position. + * We create a temporary obj_hash_table_t object (newtbl) to be used while building the new hashes. + * This allows us to reuse hashtable_insert() and hashtable_remove(), when moving the elements to the new table. + * After that, we can just free the old table and copy the elements from newtbl to hashtbl. + */ +hashtable_rc_t obj_hashtable_resize(obj_hash_table_t *hashtblP, hash_size_t sizeP) +{ + obj_hash_table_t newtbl; + hash_size_t n; + obj_hash_node_t *node,*next; + + if (hashtblP == NULL) { + return HASH_TABLE_BAD_PARAMETER_HASHTABLE; + } + + newtbl.size = sizeP; + newtbl.hashfunc = hashtblP->hashfunc; + + if(!(newtbl.nodes=calloc(sizeP, sizeof(obj_hash_node_t*)))) return -1; + + for(n=0; n<hashtblP->size; ++n) { + for(node=hashtblP->nodes[n]; node; node=next) { + next = node->next; + obj_hashtable_insert(&newtbl, node->key, node->key_size, node->data); + //WARNING Lionel GAUTHIER: BAD CODE TO BE REWRITTEN + obj_hashtable_remove(hashtblP, node->key, node->key_size); + } + } + + free(hashtblP->nodes); + hashtblP->size=newtbl.size; + hashtblP->nodes=newtbl.nodes; + + return HASH_TABLE_OK; +} + + + diff --git a/common/utils/collection/hashtable/obj_hashtable.h b/common/utils/collection/hashtable/obj_hashtable.h new file mode 100755 index 0000000000000000000000000000000000000000..ff126a3dab90fdfd47fe61938bd07e6b9c3e6022 --- /dev/null +++ b/common/utils/collection/hashtable/obj_hashtable.h @@ -0,0 +1,39 @@ +#ifndef _OBJ_HASH_TABLE_H_ +#define _OBJ_HASH_TABLE_H_ +#include<stdlib.h> +#include <stdint.h> +#include <stddef.h> + +#include "hashtable.h" + +typedef size_t hash_size_t; + + +typedef struct obj_hash_node_s { + int key_size; + void *key; + void *data; + struct obj_hash_node_s *next; +} obj_hash_node_t; + +typedef struct obj_hash_table_s { + hash_size_t size; + hash_size_t num_elements; + struct obj_hash_node_s **nodes; + hash_size_t (*hashfunc)(const void*, int); + void (*freekeyfunc)(void*); + void (*freedatafunc)(void*); +} obj_hash_table_t; + +obj_hash_table_t *obj_hashtable_create (hash_size_t size, hash_size_t (*hashfunc)(const void*, int ), void (*freekeyfunc)(void*), void (*freedatafunc)(void*)); +hashtable_rc_t obj_hashtable_destroy(obj_hash_table_t *hashtblP); +hashtable_rc_t obj_hashtable_is_key_exists (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP); +hashtable_rc_t obj_hashtable_insert (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP, void *dataP); +hashtable_rc_t obj_hashtable_remove (obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP); +hashtable_rc_t obj_hashtable_get (obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP, void ** dataP); +hashtable_rc_t obj_hashtable_resize (obj_hash_table_t *hashtblP, hash_size_t sizeP); + + + +#endif +