ref: c8499f76236656b20c24d0e2bc0690d4ae049b8f
dir: /htable.c/
/*
* This work is dedicated to the public domain.
* See COPYING file for more information.
*/
#include <stdlib.h>
#include <stdio.h>
#include "htable.h"
/*
* Destroy the hash table and, if the free_key_val flag is true, free the keys and values.
*/
static void
destroy_and_free(struct htable *ht, int free_key_val)
{
unsigned int i;
struct htnode *n, *tmp;
for (i = 0; i < ht->cap; i++) {
for (n = ht->nodes[i]; n != NULL;) {
if (free_key_val) {
ht->key_free(n->key);
ht->val_free(n->val);
}
tmp = n;
n = n->next;
free(tmp);
}
}
free(ht->nodes);
free(ht);
}
/*
* Insert a new node or, if the replace flag is true, replace the value of an existing node.
*/
static int
insert_or_replace_val(struct htable *ht, void *key, void *val, int replace)
{
unsigned int i;
struct htnode *n, *ln; /* current and last node */
i = ht->hash(key, ht->cap);
ln = NULL;
for (n = ht->nodes[i]; n != NULL; n = n->next) {
/* if key already exist */
if (ht->key_cmp(key, n->key) == 0) {
if (replace) {
ht->val_free(n->val);
n->val = val;
return 0;
} else {
return -1;
}
}
ln = n;
}
if (replace) /* failed to replace */
return -1;
if ((n = malloc(sizeof(struct htnode))) == NULL) {
perror("error: malloc");
return -1;
}
n->key = key;
n->val = val;
n->next = NULL;
/* link to the last node */
if (ln == NULL)
ht->nodes[i] = n;
else
ln->next = n;
ht->len++;
return 0;
}
/*
* Remove a node or, if the replace flag is true, change the key of the node.
*/
static int
remove_or_replace_key(struct htable *ht, void *key, int replace, void *newkey)
{
unsigned int i;
struct htnode *n, *ln; /* current and last node */
i = ht->hash(key, ht->cap);
ln = NULL;
for (n = ht->nodes[i]; n != NULL; n = n->next) {
if (ht->key_cmp(key, n->key) == 0) {
ht->key_free(n->key);
if (!replace) {
ht->val_free(n->val);
} else {
if (htinsert(ht, newkey, n->val) == -1)
return -1;
}
/* link to the last node */
if (ln == NULL)
ht->nodes[i] = n->next;
else
ln->next = n->next;
free(n);
return 0;
}
ln = n;
}
return -1;
}
struct htable *
htcreate(hash_fn *hash, key_cmp_fn *key_cmp, key_free_fn *key_free,
val_free_fn *val_free, unsigned int cap)
{
unsigned int i;
struct htable *ht;
if ((ht = malloc(sizeof(struct htable))) == NULL) {
perror("error: malloc");
return NULL;
}
if ((ht->nodes = malloc(cap * sizeof(struct htnode *))) == NULL) {
perror("error: malloc");
return NULL;
}
for (i = 0; i < cap; i++)
ht->nodes[i] = NULL;
ht->cap = cap;
ht->len = 0;
ht->hash = hash;
ht->key_cmp = key_cmp;
ht->key_free = key_free;
ht->val_free = val_free;
return ht;
}
void
htdestroy(struct htable *ht)
{
destroy_and_free(ht, 1);
}
void *
htsearch(struct htable *ht, void *key)
{
unsigned int i;
struct htnode *n;
i = ht->hash(key, ht->cap);
for (n = ht->nodes[i]; n != NULL; n = n->next) {
if (ht->key_cmp(key, n->key) == 0)
return n->val;
}
return NULL;
}
int
htinsert(struct htable *ht, void *key, void *val)
{
return insert_or_replace_val(ht, key, val, 0);
}
int
htremove(struct htable *ht, void *key)
{
return remove_or_replace_key(ht, key, 0, NULL);
}
int
htmodkey(struct htable *ht, void *oldkey, void *newkey)
{
return remove_or_replace_key(ht, oldkey, 1, newkey);
}
int
htmodval(struct htable *ht, void *key, void *newval)
{
return insert_or_replace_val(ht, key, newval, 1);
}
struct htable *
htresize(struct htable *ht, unsigned int newcap)
{
struct htable *newht;
struct htiter it;
newht = htcreate(ht->hash, ht->key_cmp, ht->key_free, ht->val_free, newcap);
if (newht == NULL)
return NULL;
htiter_init(&it);
while(htiterate(ht, &it)) {
if (htinsert(newht, it.node->key, it.node->val) == -1) {
htdestroy(newht);
return NULL;
}
}
destroy_and_free(ht, 0);
return newht;
}
void
htiter_init(struct htiter *it)
{
it->index = 0;
it->node = NULL;
}
int
htiterate(struct htable *ht, struct htiter *it)
{
if (it->node != NULL)
it->node = it->node->next;
while (it->node == NULL && it->index < ht->cap) {
it->node = ht->nodes[it->index];
it->index++;
}
if (it->node != NULL)
return 1;
else
return 0;
}