You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
6.6 KiB
286 lines
6.6 KiB
/* |
|
* Copyright (c) 2007 Vreixo Formoso |
|
* |
|
* This file is part of the libisofs project; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. See COPYING file for details. |
|
*/ |
|
|
|
#include "util.h" |
|
#include "error.h" |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
/* |
|
* Hash table implementation |
|
*/ |
|
|
|
struct iso_hnode |
|
{ |
|
void *key; |
|
void *data; |
|
|
|
/** next node for chaining */ |
|
struct iso_hnode *next; |
|
}; |
|
|
|
struct iso_htable |
|
{ |
|
struct iso_hnode **table; |
|
|
|
size_t size; /**< number of items in table */ |
|
size_t cap; /**< number of slots in table */ |
|
|
|
hash_funtion_t hash; |
|
compare_function_t compare; |
|
}; |
|
|
|
static |
|
struct iso_hnode *iso_hnode_new(void *key, void *data) |
|
{ |
|
struct iso_hnode *node = malloc(sizeof(struct iso_hnode)); |
|
if (node == NULL) |
|
return NULL; |
|
|
|
node->data = data; |
|
node->key = key; |
|
node->next = NULL; |
|
return node; |
|
} |
|
|
|
/** |
|
* Put an element in a Hash Table. The element will be identified by |
|
* the given key, that you should use to retrieve the element again. |
|
* |
|
* This function allow duplicates, i.e., two items with the same key. In those |
|
* cases, the value returned by iso_htable_get() is undefined. If you don't |
|
* want to allow duplicates, use iso_htable_put() instead; |
|
* |
|
* Both the key and data pointers will be stored internally, so you should |
|
* free the objects they point to. Use iso_htable_remove() to delete an |
|
* element from the table. |
|
*/ |
|
int iso_htable_add(IsoHTable *table, void *key, void *data) |
|
{ |
|
struct iso_hnode *node; |
|
struct iso_hnode *new; |
|
unsigned int hash; |
|
|
|
if (table == NULL || key == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
new = iso_hnode_new(key, data); |
|
if (new == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
hash = table->hash(key) % table->cap; |
|
node = table->table[hash]; |
|
|
|
table->size++; |
|
new->next = node; |
|
table->table[hash] = new; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Like iso_htable_add(), but this doesn't allow dulpicates. |
|
* |
|
* @return |
|
* 1 success, 0 if an item with the same key already exists, < 0 error |
|
*/ |
|
int iso_htable_put(IsoHTable *table, void *key, void *data) |
|
{ |
|
struct iso_hnode *node; |
|
struct iso_hnode *new; |
|
unsigned int hash; |
|
|
|
if (table == NULL || key == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
hash = table->hash(key) % table->cap; |
|
node = table->table[hash]; |
|
|
|
while (node) { |
|
if (!table->compare(key, node->key)) { |
|
return 0; |
|
} |
|
node = node->next; |
|
} |
|
|
|
new = iso_hnode_new(key, data); |
|
if (new == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
table->size++; |
|
new->next = table->table[hash]; |
|
table->table[hash] = new; |
|
return ISO_SUCCESS; |
|
} |
|
|
|
/** |
|
* Retrieve an element from the given table. |
|
* |
|
* @param table |
|
* Hash table |
|
* @param key |
|
* Key of the element that will be removed |
|
* @param data |
|
* Will be filled with the element found. Remains untouched if no |
|
* element with the given key is found. |
|
* @return |
|
* 1 if found, 0 if not, < 0 on error |
|
*/ |
|
int iso_htable_get(IsoHTable *table, void *key, void **data) |
|
{ |
|
struct iso_hnode *node; |
|
unsigned int hash; |
|
|
|
if (table == NULL || key == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
hash = table->hash(key) % table->cap; |
|
node = table->table[hash]; |
|
while (node) { |
|
if (!table->compare(key, node->key)) { |
|
if (data) { |
|
*data = node->data; |
|
} |
|
return 1; |
|
} |
|
node = node->next; |
|
} |
|
return 0; |
|
} |
|
|
|
/** |
|
* Remove an item with the given key from the table. In tables that allow |
|
* duplicates, it is undefined the element that will be deleted. |
|
* |
|
* @param table |
|
* Hash table |
|
* @param key |
|
* Key of the element that will be removed |
|
* @param free_data |
|
* Function that will be called passing as parameters both the key and |
|
* the element that will be deleted. The user can use it to free the |
|
* element. You can pass NULL if you don't want to delete the item itself. |
|
* @return |
|
* 1 success, 0 no element exists with the given key, < 0 error |
|
*/ |
|
int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data) |
|
{ |
|
struct iso_hnode *node, *prev; |
|
unsigned int hash; |
|
|
|
if (table == NULL || key == NULL) { |
|
return ISO_NULL_POINTER; |
|
} |
|
|
|
hash = table->hash(key) % table->cap; |
|
node = table->table[hash]; |
|
prev = NULL; |
|
while (node) { |
|
if (!table->compare(key, node->key)) { |
|
if (free_data) |
|
free_data(node->key, node->data); |
|
if (prev) { |
|
prev->next = node->next; |
|
} else { |
|
table->table[hash] = node->next; |
|
} |
|
free(node); |
|
table->size--; |
|
return 1; |
|
} |
|
prev = node; |
|
node = node->next; |
|
} |
|
return 0; |
|
} |
|
|
|
/** |
|
* Hash function suitable for keys that are char strings. |
|
*/ |
|
unsigned int iso_str_hash(const void *key) |
|
{ |
|
int i, len; |
|
const char *p = key; |
|
unsigned int h = 2166136261u; |
|
|
|
len = strlen(p); |
|
for (i = 0; i < len; i++) |
|
h = (h * 16777619 ) ^ p[i]; |
|
|
|
return h; |
|
} |
|
|
|
/** |
|
* Destroy the given hash table. |
|
* |
|
* Note that you're responsible to actually destroy the elements by providing |
|
* a valid free_data function. You can pass NULL if you only want to delete |
|
* the hash structure. |
|
*/ |
|
void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data) |
|
{ |
|
size_t i; |
|
struct iso_hnode *node, *tmp; |
|
|
|
if (table == NULL) { |
|
return; |
|
} |
|
|
|
for (i = 0; i < table->cap; ++i) { |
|
node = table->table[i]; |
|
while (node) { |
|
tmp = node->next; |
|
if (free_data) |
|
free_data(node->key, node->data); |
|
free(node); |
|
node = tmp; |
|
} |
|
} |
|
free(table->table); |
|
free(table); |
|
} |
|
|
|
/** |
|
* Create a new hash table. |
|
* |
|
* @param size |
|
* Number of slots in table. |
|
* @param hash |
|
* Function used to generate |
|
*/ |
|
int iso_htable_create(size_t size, hash_funtion_t hash, |
|
compare_function_t compare, IsoHTable **table) |
|
{ |
|
IsoHTable *t; |
|
|
|
if (table == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
|
|
t = malloc(sizeof(IsoHTable)); |
|
if (t == NULL) { |
|
return ISO_MEM_ERROR; |
|
} |
|
t->table = calloc(size, sizeof(void*)); |
|
if (t->table == NULL) { |
|
free(t); |
|
return ISO_MEM_ERROR; |
|
} |
|
t->cap = size; |
|
t->size = 0; |
|
t->hash = hash; |
|
t->compare = compare; |
|
|
|
*table = t; |
|
return ISO_SUCCESS; |
|
}
|
|
|