/*
** HASH: Simple hash table implementation.
** Copyright (C) 2002 Michael W. Shaffer <mwshaffer@angrypot.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 (see the file COPYING). If not, write to:
**
** The Free Software Foundation, Inc.
** 59 Temple Place, Suite 330,
** Boston, MA  02111-1307  USA
*/

#include <stdlib.h>
#include "primes.h"
#include "list.h"
#include "hash.h"

/* Peter J. Wienberger's hash */
static unsigned long hash_pjw (char *value)
{
	unsigned long h = 0;
	unsigned long g = 0;

	while (*value) {
		h = (h << 4) + *(value++);
		if ((g = h & 0xF0000000))
			h ^= g >> 24;
		h &= ~g;
	}
	return h;
}

void hash_table_init (struct hash_table *h)
{
	int i = 0;

	if (!h)
		return;

	if (h->size < 1)
		h->size = 100;

	h->size = find_prime (h->size);
	h->tbl = (struct list *) malloc (h->size * (sizeof (struct list)));
	if (!h->tbl)
		return;
	memset (h->tbl, 0, (h->size * sizeof (struct list)));

	for (i = 0 ; i < h->size ; i++) {
		list_init (&(h->tbl[i]));
	}

	return;
}

void hash_table_free (struct hash_table *h)
{
	int i = 0;
	struct datum *d = NULL;
	struct list_item *curr = NULL;

	if (!h)
		return;

	for (i = 0 ; i < h->size ; i++) {
		for (curr = h->tbl[i].head ; curr ; curr = curr->next) {
			if (curr->data) {
				d = (struct datum *) curr->data;
				if (d->key)
					free (d->key);
				if (d->val)
					free (d->val);
			}
		}
		list_free (&(h->tbl[i]));
	}

	if (h->tbl)
		free (h->tbl);

	h->size = 0;
	h->tbl = NULL;

	return;
}

struct datum *hash_table_insert (struct hash_table *h, struct datum *d)
{
	int slot = 0;  
	struct datum *new = NULL;
	struct list_item *item = NULL;

	if (!(d && h && (h->size > 0) && h->tbl))
		return NULL;

	new = (struct datum *) malloc (sizeof (struct datum));
	if (!new)
		goto ERROR;
	memset (new, 0, (sizeof (struct datum)));

	new->ksize = d->ksize;
	new->key = (void *) malloc (new->ksize + 1);
	if (!new->key)
		goto ERROR;
	memset (new->key, 0, (new->ksize + 1));
	memcpy (new->key, d->key, new->ksize);

	new->vsize = d->vsize;
	new->val = (void *) malloc (new->vsize + 1);
	if (!new->val)
		goto ERROR;
	memset (new->val, 0, (new->vsize + 1));
	memcpy (new->val, d->val, new->vsize);

	slot = (int) (hash_pjw (d->key) % h->size);
	item = list_insert (&(h->tbl[slot]), (void *) new, sizeof (struct datum));
	goto EXIT;

ERROR:
	if (new && (new->key))
		free (new->key);
	if (new && (new->val))
		free (new->val);
EXIT:
	if (new)
		free (new);
	return (struct datum *) item->data;
}

struct datum *hash_table_search (struct hash_table *h, struct datum *k)
{
	int slot = 0;
	struct list_item *curr = NULL;
	struct datum *d = NULL;

	if (!(k && h && (h->size > 0) && h->tbl))
		goto EXIT;

	slot = (int) (hash_pjw (k->key) % h->size);
	for (curr = h->tbl[slot].head ; curr ; curr = curr->next) {
		d = (struct datum *) curr->data;
		if (d->ksize == k->ksize){
			if (!memcmp (d->key, k->key, d->ksize))
				goto EXIT;
		}
	}
	d = NULL;

EXIT:
	return d;
}

static struct list_item *hash_table_search2 (struct hash_table *h, struct datum *k)
{
	int slot = 0;
	struct list_item *curr = NULL;
	struct datum *d = NULL;

	if (!(k && h && (h->size > 0) && h->tbl))
		goto EXIT;

	slot = (int) (hash_pjw (k->key) % h->size);
	for (curr = h->tbl[slot].head ; curr ; curr = curr->next) {
		d = (struct datum *) curr->data;
		if (d->ksize == k->ksize){
			if (!memcmp (d->key, k->key, d->ksize))
				goto EXIT;
		}
	}
	curr = NULL;

EXIT:
	return curr;
}

void hash_table_delete (struct hash_table *h, struct datum *k)
{
	struct datum *d = NULL;
	struct list_item *l = NULL;

	if (!(k && h && (h->size > 0) && h->tbl))
		return;

	if ((l = hash_table_search2 (h, k))) {
		d = (struct datum *) l->data;
		if (d->key)
			free (d->key);
		if (d->val)
			free (d->val);
		list_delete ((struct list_item *) l);
	}

	return;
}



syntax highlighted by Code2HTML, v. 0.9.1