Add a hash table implementation.
This commit is contained in:
parent
b29e2a7db2
commit
b4d76f7925
@ -33,6 +33,7 @@ src_libisofs_la_SOURCES = \
|
|||||||
src/util.h \
|
src/util.h \
|
||||||
src/util.c \
|
src/util.c \
|
||||||
src/util_rbtree.c \
|
src/util_rbtree.c \
|
||||||
|
src/util_htable.c \
|
||||||
src/filesrc.h \
|
src/filesrc.h \
|
||||||
src/filesrc.c \
|
src/filesrc.c \
|
||||||
src/ecma119.h \
|
src/ecma119.h \
|
||||||
|
84
src/util.h
84
src/util.h
@ -216,6 +216,11 @@ char *strcopy(const char *buf, size_t len);
|
|||||||
char *ucs2str(const char *buf, size_t len);
|
char *ucs2str(const char *buf, size_t len);
|
||||||
|
|
||||||
typedef struct iso_rbtree IsoRBTree;
|
typedef struct iso_rbtree IsoRBTree;
|
||||||
|
typedef struct iso_htable IsoHTable;
|
||||||
|
|
||||||
|
typedef unsigned int (*hash_funtion_t)(const void *key);
|
||||||
|
typedef int (*compare_function_t)(const void *a, const void *b);
|
||||||
|
typedef void (*hfree_data_t)(void *key, void *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new binary tree. libisofs binary trees allow you to add any data
|
* Create a new binary tree. libisofs binary trees allow you to add any data
|
||||||
@ -282,4 +287,83 @@ size_t iso_rbtree_get_size(IsoRBTree *tree);
|
|||||||
void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
|
void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
|
||||||
size_t *size);
|
size_t *size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash function suitable for keys that are char strings.
|
||||||
|
*/
|
||||||
|
unsigned int iso_str_hash(const void *key);
|
||||||
|
|
||||||
#endif /*LIBISO_UTIL_H_*/
|
#endif /*LIBISO_UTIL_H_*/
|
||||||
|
285
src/util_htable.c
Normal file
285
src/util_htable.c
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* 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 || free_data == 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(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 || free_data == 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
@ -256,7 +256,7 @@ static void test_iso_rbtree_insert()
|
|||||||
char *str1, *str2, *str3, *str4, *str5;
|
char *str1, *str2, *str3, *str4, *str5;
|
||||||
void *str;
|
void *str;
|
||||||
|
|
||||||
res = iso_rbtree_new(strcmp, &tree);
|
res = iso_rbtree_new((compare_function_t)strcmp, &tree);
|
||||||
CU_ASSERT_EQUAL(res, 1);
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
/* ok, insert one str */
|
/* ok, insert one str */
|
||||||
@ -300,6 +300,75 @@ static void test_iso_rbtree_insert()
|
|||||||
iso_rbtree_destroy(tree, NULL);
|
iso_rbtree_destroy(tree, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_iso_htable_put_get()
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
IsoHTable *table;
|
||||||
|
char *str1, *str2, *str3, *str4, *str5;
|
||||||
|
void *str;
|
||||||
|
|
||||||
|
res = iso_htable_create(4, iso_str_hash, (compare_function_t)strcmp, &table);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
/* try to get str from empty table */
|
||||||
|
res = iso_htable_get(table, "first str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 0);
|
||||||
|
|
||||||
|
/* ok, insert one str */
|
||||||
|
str1 = "first str";
|
||||||
|
res = iso_htable_put(table, str1, str1);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
/* and now get str from table */
|
||||||
|
res = iso_htable_get(table, "first str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
CU_ASSERT_PTR_EQUAL(str, str1);
|
||||||
|
res = iso_htable_get(table, "second str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 0);
|
||||||
|
|
||||||
|
str2 = "second str";
|
||||||
|
res = iso_htable_put(table, str2, str2);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
str = NULL;
|
||||||
|
res = iso_htable_get(table, "first str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
CU_ASSERT_PTR_EQUAL(str, str1);
|
||||||
|
res = iso_htable_get(table, "second str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
CU_ASSERT_PTR_EQUAL(str, str2);
|
||||||
|
|
||||||
|
/* insert again, with same key but other data */
|
||||||
|
res = iso_htable_put(table, str2, str1);
|
||||||
|
CU_ASSERT_EQUAL(res, 0);
|
||||||
|
|
||||||
|
res = iso_htable_get(table, "second str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
CU_ASSERT_PTR_EQUAL(str, str2);
|
||||||
|
|
||||||
|
str3 = "third str";
|
||||||
|
res = iso_htable_put(table, str3, str3);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
str4 = "four str";
|
||||||
|
res = iso_htable_put(table, str4, str4);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
str5 = "fifth str";
|
||||||
|
res = iso_htable_put(table, str5, str5);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
|
||||||
|
/* some searches */
|
||||||
|
res = iso_htable_get(table, "sixth str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 0);
|
||||||
|
|
||||||
|
res = iso_htable_get(table, "fifth str", &str);
|
||||||
|
CU_ASSERT_EQUAL(res, 1);
|
||||||
|
CU_ASSERT_PTR_EQUAL(str, str5);
|
||||||
|
|
||||||
|
iso_htable_destroy(table, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void add_util_suite()
|
void add_util_suite()
|
||||||
{
|
{
|
||||||
CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL);
|
CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL);
|
||||||
@ -316,4 +385,5 @@ void add_util_suite()
|
|||||||
CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid);
|
CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid);
|
||||||
CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid);
|
CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid);
|
||||||
CU_add_test(pSuite, "iso_rbtree_insert()", test_iso_rbtree_insert);
|
CU_add_test(pSuite, "iso_rbtree_insert()", test_iso_rbtree_insert);
|
||||||
|
CU_add_test(pSuite, "iso_htable_put/get()", test_iso_htable_put_get);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user