mirror of
https://github.com/samsonjs/lake.git
synced 2026-03-25 08:55:49 +00:00
310 lines
7 KiB
C
310 lines
7 KiB
C
/* hashtab.c - Simple, Reliable C Hashtable
|
|
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@gmail.com>
|
|
*
|
|
* 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 2 of the
|
|
* License, or (at your option) 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "hashtab.h"
|
|
|
|
hashtab_t *ht_init (size_t size, int (*hash_func) (void *, size_t, size_t))
|
|
{
|
|
hashtab_t *new_ht = (hashtab_t *) malloc (sizeof (hashtab_t));
|
|
|
|
new_ht->arr = (hashtab_node_t **) malloc (sizeof (hashtab_node_t *) * size);
|
|
|
|
new_ht->size = size;
|
|
new_ht->count = 0;
|
|
|
|
/* all entries are empty */
|
|
int i = 0;
|
|
for (i = 0; i < (int) size; i++)
|
|
{
|
|
new_ht->arr[i] = NULL;
|
|
}
|
|
|
|
if (hash_func == NULL)
|
|
new_ht->hash_func = &ht_hash;
|
|
else
|
|
new_ht->hash_func = hash_func;
|
|
|
|
return new_ht;
|
|
}
|
|
|
|
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen)
|
|
{
|
|
int index = ht_hash (key, keylen, hashtable->size);
|
|
|
|
if (hashtable->arr[index] == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
hashtab_node_t *last_node = hashtable->arr[index];
|
|
while (last_node != NULL)
|
|
{
|
|
/* only compare matching keylens */
|
|
if (last_node->keylen == keylen)
|
|
{
|
|
/* compare keys */
|
|
if (memcmp (key, last_node->key, keylen) == 0)
|
|
{
|
|
return last_node->value;
|
|
}
|
|
}
|
|
|
|
last_node = last_node->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *ht_put (hashtab_t * hashtable,
|
|
void *key, size_t keylen, void *value, size_t vallen)
|
|
{
|
|
int index = ht_hash (key, keylen, hashtable->size);
|
|
|
|
hashtab_node_t *next_node, *last_node;
|
|
next_node = hashtable->arr[index];
|
|
last_node = NULL;
|
|
|
|
/* Search for an existing key. */
|
|
while (next_node != NULL)
|
|
{
|
|
/* only compare matching keylens */
|
|
if (next_node->keylen == keylen)
|
|
{
|
|
/* compare keys */
|
|
if (memcmp (key, next_node->key, keylen) == 0)
|
|
{
|
|
/* this key already exists, replace it */
|
|
if (next_node->vallen != vallen)
|
|
{
|
|
/* new value is a different size */
|
|
free (next_node->value);
|
|
next_node->value = malloc (vallen);
|
|
if (next_node->value == NULL)
|
|
return NULL;
|
|
}
|
|
memcpy (next_node->value, value, vallen);
|
|
next_node->vallen = vallen;
|
|
return next_node->value;
|
|
}
|
|
}
|
|
|
|
last_node = next_node;
|
|
next_node = next_node->next;
|
|
}
|
|
|
|
/* create a new node */
|
|
hashtab_node_t *new_node;
|
|
new_node = (hashtab_node_t *) malloc (sizeof (hashtab_node_t));
|
|
if (new_node == NULL)
|
|
return NULL;
|
|
|
|
/* get some memory for the new node data */
|
|
new_node->key = malloc (keylen);
|
|
new_node->value = malloc (vallen);
|
|
if (new_node->key == NULL || new_node->key == NULL)
|
|
{
|
|
free (new_node->key);
|
|
free (new_node->value);
|
|
free (new_node);
|
|
return NULL;
|
|
}
|
|
|
|
/* copy over the value and key */
|
|
memcpy (new_node->key, key, keylen);
|
|
memcpy (new_node->value, value, vallen);
|
|
new_node->keylen = keylen;
|
|
new_node->vallen = vallen;
|
|
|
|
/* no next node */
|
|
new_node->next = NULL;
|
|
|
|
/* Tack the new node on the end or right on the table. */
|
|
if (last_node != NULL)
|
|
last_node->next = new_node;
|
|
else
|
|
hashtable->arr[index] = new_node;
|
|
|
|
hashtable->count++;
|
|
return new_node->value;
|
|
}
|
|
|
|
/* delete the given key from the hashtable */
|
|
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen)
|
|
{
|
|
hashtab_node_t *last_node, *next_node;
|
|
int index = ht_hash (key, keylen, hashtable->size);
|
|
next_node = hashtable->arr[index];
|
|
last_node = NULL;
|
|
|
|
while (next_node != NULL)
|
|
{
|
|
if (next_node->keylen == keylen)
|
|
{
|
|
/* compare keys */
|
|
if (memcmp (key, next_node->key, keylen) == 0)
|
|
{
|
|
/* free node memory */
|
|
free (next_node->value);
|
|
free (next_node->key);
|
|
|
|
/* adjust the list pointers */
|
|
if (last_node != NULL)
|
|
last_node->next = next_node->next;
|
|
else
|
|
hashtable->arr[index] = next_node->next;
|
|
|
|
/* free the node */
|
|
free (next_node);
|
|
break;
|
|
}
|
|
}
|
|
|
|
last_node = next_node;
|
|
next_node = next_node->next;
|
|
}
|
|
}
|
|
|
|
/* grow the hashtable */
|
|
void *ht_grow (hashtab_t * old_ht, size_t new_size)
|
|
{
|
|
/* create new hashtable */
|
|
hashtab_t *new_ht = ht_init (new_size, old_ht->hash_func);
|
|
if (new_ht == NULL)
|
|
return NULL;
|
|
|
|
void *ret; /* captures return values */
|
|
|
|
/* Iterate through the old hashtable. */
|
|
hashtab_iter_t ii;
|
|
ht_iter_init (old_ht, &ii);
|
|
for (; ii.key != NULL; ht_iter_inc (&ii))
|
|
{
|
|
ret = ht_put (new_ht, ii.key, ii.keylen, ii.value, ii.vallen);
|
|
if (ret == NULL)
|
|
{
|
|
/* Insert failed. Destroy new hashtable and return. */
|
|
ht_destroy (new_ht);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Destroy the old hashtable. */
|
|
ht_destroy (old_ht);
|
|
|
|
return new_ht;
|
|
}
|
|
|
|
/* free all resources used by the hashtable */
|
|
void ht_destroy (hashtab_t * hashtable)
|
|
{
|
|
hashtab_node_t *next_node, *last_node;
|
|
|
|
/* Free each linked list in hashtable. */
|
|
int i;
|
|
for (i = 0; i < (int) hashtable->size; i++)
|
|
{
|
|
next_node = hashtable->arr[i];
|
|
while (next_node != NULL)
|
|
{
|
|
/* destroy node */
|
|
free (next_node->key);
|
|
free (next_node->value);
|
|
last_node = next_node;
|
|
next_node = next_node->next;
|
|
free (last_node);
|
|
}
|
|
}
|
|
|
|
free (hashtable->arr);
|
|
free (hashtable);
|
|
}
|
|
|
|
/* iterator initilaize */
|
|
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii)
|
|
{
|
|
/* stick in initial bookeeping data */
|
|
ii->internal.hashtable = hashtable;
|
|
ii->internal.node = NULL;
|
|
ii->internal.index = -1;
|
|
|
|
/* have iterator point to first element */
|
|
ht_iter_inc (ii);
|
|
}
|
|
|
|
/* iterator increment */
|
|
void ht_iter_inc (hashtab_iter_t * ii)
|
|
{
|
|
hashtab_t *hashtable = ii->internal.hashtable;
|
|
int index = ii->internal.index;
|
|
|
|
/* attempt to grab the next node */
|
|
if (ii->internal.node == NULL || ii->internal.node->next == NULL)
|
|
index++;
|
|
else
|
|
{
|
|
/* next node in the list */
|
|
ii->internal.node = ii->internal.node->next;
|
|
ii->key = ii->internal.node->key;
|
|
ii->value = ii->internal.node->value;
|
|
ii->keylen = ii->internal.node->keylen;
|
|
ii->vallen = ii->internal.node->vallen;
|
|
return;
|
|
}
|
|
|
|
/* find next node */
|
|
while (hashtable->arr[index] == NULL && index < (int) hashtable->size)
|
|
index++;
|
|
|
|
if (index >= (int) hashtable->size)
|
|
{
|
|
/* end of hashtable */
|
|
ii->internal.node = NULL;
|
|
ii->internal.index = (int) hashtable->size;
|
|
|
|
ii->key = NULL;
|
|
ii->value = NULL;
|
|
ii->keylen = 0;
|
|
ii->vallen = 0;
|
|
return;
|
|
}
|
|
|
|
/* point to the next item in the hashtable */
|
|
ii->internal.node = hashtable->arr[index];
|
|
ii->internal.index = index;
|
|
ii->key = ii->internal.node->key;
|
|
ii->value = ii->internal.node->value;
|
|
ii->keylen = ii->internal.node->keylen;
|
|
ii->vallen = ii->internal.node->vallen;
|
|
}
|
|
|
|
int ht_hash (void *key, size_t keylen, size_t hashtab_size)
|
|
{
|
|
int sum = 0;
|
|
|
|
/* very simple hash function for now */
|
|
int i;
|
|
for (i = 0; i < (int) keylen; i++)
|
|
{
|
|
sum += ((unsigned char *) key)[i] * (i + 1);
|
|
}
|
|
|
|
return (sum % (int) hashtab_size);
|
|
}
|