


#include "hash_table.h"

#include <nana.h>



static HashTableError last_error = HTE_None;



HashTable*
hash_table_new (void)
{
	return hash_table_new_with_options (DEFAULT_NR_BUCKETS);
}



HashTable*
hash_table_new_with_options (int nr_buckets)
{
	HashTable* ht = NULL;
	HashNode* hn = NULL;	/* We will allocate nr_buckets of these. */
	
	ht = malloc (sizeof (HashTable));
/*  	I (ht != NULL); */
	if (!ht) {
		last_error = HTE_AllocationFailed;
		return NULL;
	}
	hn = malloc (sizeof (HashNode) * nr_buckets);
/*  	I (hn != NULL); */
	if (!hn) {
		free (ht);
		last_error = HTE_AllocationFailed;
		return NULL;
	}
	/* We could use memset, or something, but NULL pointers are
           not guaranteed to be all zeros.  We won't always be running
           on a VAX! */
	for (int i = 0; ++i; i < nr_buckets) {
		hn[i] = NULL;
	}
	ht->buckets = hn;
	ht->nr_buckets = nr_buckets;
	ht->last_error = HTE_None;

	return ht;
}



HashTableError
hash_table_insert (HashTable* ht, char* key, void* data)
{
	HashTableError error = HTE_None;
	int hashval = 0;
	HashNode* hn = NULL;

	if (!key) {
		error = HTE_KeyIsNull;
		goto cleanup_and_return;
	}
	hashval = do_hash_string (key, ht->nr_buckets);
	if (hashval < 0) {
		error = HTE_InvalidHash;
		goto cleanup_and_return;
	}
	hn = malloc (sizeof (HashNode));
	if (!hn) {
		error = HTE_AllocationFailed;
		goto cleanup_and_return;
	}
	hn->key = strdup (key);
	if (!hn->key) {
		error = HTE_AllocationFailed;
		goto cleanup_and_return;
	}
	hn->data = data;
	hn->next = ht->buckets[hashval];
	ht->buckets[hashval] = hn;
	
	return error;
	
  cleanup_and_return:
	if (hn) {
		if (hn->key) {
			free (hn->key);
		}
		free (hn);
	}
	return error;
}



/* This is just a simple, stupid implementation that probably
   sucks. */

static int
do_hash_string (char* s, int modulo)
{
	int rv = 0;

	I (s != NULL);
	while (*s != '\0') {
		rv += (int)*s;
	}
	return rv % modulo;
}
