/*
elmo - ELectronic Mail Operator
Copyright (C) 2002 rzyjontko
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; version 2.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
----------------------------------------------------------------------
hash tables
*/
/****************************************************************************
* IMPLEMENTATION HEADERS
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include <math.h>
#include "hash.h"
#include "xmalloc.h"
#include "memblock.h"
#include "debug.h"
/****************************************************************************
* IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
****************************************************************************/
/**
* This is
* floor (((sqrt (5) - 1) / 2) * 2 ^ (8 * sizeof (unsigned)))
*/
#if SIZEOF_UNSIGNED == 4
#define KNUTH_CONST (2654435769U)
#elif SIZEOF_UNSIGNED == 8
#define KNUTH_CONST (11400714819323198485U)
#elif SIZEOF_UNSIGNED == 2
#define KNUTH_CONST (40503U)
#endif
/****************************************************************************
* IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE DATA
****************************************************************************/
/****************************************************************************
* INTERFACE DATA
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
****************************************************************************/
static unsigned hash (const char *s, unsigned exponent);
static entry_t *lookup_with_hash (htable_t *table, const char *key,
unsigned hashval);
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTIONS
****************************************************************************/
static unsigned
hashpjw (const char *s)
{
unsigned h = 0;
unsigned g;
while (*s){
h <<= 4;
h += *s;
if ((g = h & 0xf0000000)){
h ^= (g >> 24);
h ^= g;
}
s++;
}
return h;
}
static unsigned
hash (const char *s, unsigned exponent)
{
unsigned wordval;
int shift = 8 * sizeof (unsigned) - exponent;
wordval = hashpjw (s);
return (wordval * KNUTH_CONST) >> shift;
}
static entry_t *
lookup_with_hash (htable_t *table, const char *key, unsigned hashval)
{
entry_t *entry;
for (entry = table->array[hashval]; entry; entry = entry->next){
if (!strcmp (entry->key, key))
break;
}
return entry;
}
static void
rehash (htable_t *table)
{
entry_aux_t *list;
entry_t *item;
unsigned hashval;
debug_msg (DEBUG_INFO, "rehashing table of exponent %d", table->exponent);
for (list = table->list; list; list = list->next){
item = list->entry;
hashval = hash (item->key, table->exponent);
item->next = table->array[hashval];
table->array[hashval] = item;
}
}
/****************************************************************************
* INTERFACE FUNCTIONS
****************************************************************************/
htable_t *
htable_create (unsigned exponent)
{
int size = 1 << exponent;
htable_t *result = xmalloc (sizeof (htable_t));
result->memblock = memblock_create (1 << (exponent + 4));
result->exponent = exponent;
result->count = 0;
result->list = NULL;
result->array = xcalloc (size * sizeof (entry_t), 1);
return result;
}
void
htable_destroy (htable_t *table, void (*fun)(void *))
{
entry_aux_t *item;
while (table->list){
item = table->list;
table->list = table->list->next;
if (fun)
fun (item->entry->content);
}
memblock_destroy (table->memblock);
xfree (table->array);
xfree (table);
}
entry_t *
htable_lookup (htable_t *table, const char *key)
{
unsigned hashval = hash (key, table->exponent);
return lookup_with_hash (table, key, hashval);
}
entry_t *
htable_insert (htable_t *table, const char *key, void *content)
{
int size;
unsigned hashval = hash (key, table->exponent);
entry_t *found = lookup_with_hash (table, key, hashval);
entry_aux_t *list_item;
if (found)
return found;
found = memblock_malloc (&table->memblock, sizeof (entry_t));
found->key = memblock_strdup (&table->memblock, key);
found->content = content;
found->next = table->array[hashval];
table->array[hashval] = found;
list_item = memblock_malloc (&table->memblock, sizeof (entry_aux_t));
list_item->next = table->list;
list_item->entry = found;
table->list = list_item;
table->count++;
size = 1 << table->exponent;
if (table->count > 2 * size){
xfree (table->array);
table->exponent++;
size <<= 1;
table->array = xcalloc (size * sizeof (entry_t), 1);
rehash (table);
}
return found;
}
/**
* I don't recommend using this function because it drops some memory
* which will be unavailable from this moment.
*/
entry_t *
htable_remove (htable_t *table, const char *key)
{
unsigned hashval = hash (key, table->exponent);
entry_t *entry = lookup_with_hash (table, key, hashval);
entry_t *e_prev = table->array[hashval];
entry_aux_t *list = table->list;
entry_aux_t *prev = NULL;
if (entry == NULL)
return NULL;
while (list){
if (list->entry == entry)
break;
prev = list;
list = list->next;
}
if (list == NULL)
return NULL;
if (prev == NULL)
table->list = list->next;
else
prev->next = list->next;
if (e_prev == entry)
table->array[hashval] = entry->next;
else {
while (e_prev->next != entry)
e_prev = e_prev->next;
e_prev->next = entry->next;
}
return entry;
}
void
htable_clear (htable_t *table, void (*fun)(void *))
{
entry_aux_t *item;
while (table->list){
item = table->list;
table->list = table->list->next;
if (fun)
fun (item->entry->content);
}
memblock_destroy (table->memblock);
table->list = NULL;
table->count = 0;
table->memblock = memblock_create (1 << (table->exponent + 4));
memset (table->array, 0, (1 << table->exponent) * sizeof (entry_t *));
}
void
htable_iterator (htable_t *table, void (*fun)(entry_t *))
{
entry_aux_t *list;
for (list = table->list; list; list = list->next){
fun (list->entry);
}
}
/****************************************************************************
* INTERFACE CLASS BODIES
****************************************************************************/
/****************************************************************************
*
* END MODULE hash.c
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1