#include <string.h>
#include <stdlib.h>

#include "bin.h"
#include "ht.h"
#include "mem.h"

int ht_init(ht *x, unsigned tab, unsigned long (*hash)(const void *,unsigned))
{
	unsigned tmps;

	x->b = (htbucket **)mem_alloc(tmps = tab * sizeof(htbucket *));
	if (!x->b) return -1;
	memzero(x->b, tmps);

	x->ondel = 0;
	x->onwrite = 0;
	x->onfail = 0;

	x->s = tab;
	x->hash = hash;
	return 1;
}
static int ht_die_fn(ht *x, void *key, unsigned klen, void *data)
{
	if (ht_delete(x, key, klen) != 1)
		return HT_WILLFAIL;
	return HT_RESTART;
}
int ht_die(ht *x)
{
	if (ht_walk(x, ht_die_fn) == -1)
		return 1;
	return 0;
}
int ht_ondelete(ht *x, int (*fn)(void *data))
{
	x->ondel = fn;
	return 1;
}
int ht_onwrite(ht *x, void (*fn)(const void *,unsigned,const void *))
{
	x->onwrite = fn;
	return 1;
}
int ht_onfail(ht *x, int (*fn)(const void *, unsigned))
{
	x->onfail = fn;
	return 1;
}
int ht_walk(ht *x, int (*fn)(ht *x, void *key, unsigned klen, void *data))
{
	unsigned i;
	union { void *A; const void *B; } qu;
	htbucket *n;
	int ret = -1;

restart_l:
	for (i = 0; i < x->s; i++) {
		n = x->b[i];
		if (n == 0)
			continue;
		while (n) {
			qu.B = n->key;
			switch (fn(x, qu.A, n->klen, n->data)) {
			case HT_RESTART:	goto restart_l;
			case HT_FAILNOW:	return 0;
			case HT_SUCCESSNOW:	return 1;
			case HT_TRIPSUCCESS:	ret = 1; break;
			case HT_TRIPFAIL:	ret = 0; break;
			case HT_WILLSUCCESS:	if (ret != 0) ret = 1; break;
			case HT_WILLFAIL:	if (ret != 1) ret = 0; break;
			case HT_NEXT:		break;
			case HT_AGAIN:		continue;
			};
			n = (htbucket *)n->next;
		}
	}
	return ret;
}

static int ht_store_flag(ht *x, const void *key, unsigned klen,
		void *data, int flag, int i)
{
	htbucket *n;
	unsigned long hash;
	unsigned pos;
	bin_t kx;

	hash = x->hash(key, klen);
	n = x->b[pos = hash % x->s];
	while (n) {
		if (n->hash == hash && n->klen == klen &&
			memcmp(key, n->key, klen) == 0) {
			return 0; /* collision */
		}
		n = (htbucket *)n->next;
	}

	n = (htbucket *)mem_alloc(sizeof(htbucket));
	if (!n) return -1;

	bin_init(kx);
	bin_copy(kx, key, klen);
	n->key = caddr(kx);
	n->klen = klen;
	n->hash = x->hash(key, klen);;
	n->data = data;
	n->next = (void *)x->b[pos];
	n->free_data = flag;
	n->idata = i;

	x->b[pos] = (htbucket *)n;
	/* write to log (as necessary) */
	if (x->onwrite)
		x->onwrite(n->key, n->klen, n->data);

	return 1;
}
int ht_store(ht *x, const void *key, unsigned klen, void *data)
{
	return ht_store_flag(x, key, klen, data, 0, -1);
}
int ht_storeint(ht *x, const void *key, unsigned klen, int i)
{
	return ht_store_flag(x, key, klen, 0, 0, i);
}
int ht_storecopy(ht *x, const void *key, unsigned klen, void *data, unsigned dlen)
{
	int ret;
	void *copy;

	copy = (void *)mem_alloc(dlen);
	if (!copy) return -1;
	memcpy(copy, data, dlen);
	ret = ht_store_flag(x, key, klen, copy, 1, -1);

	if (ret != 1)
		mem_free(copy);
	return ret;
}
int ht_fetchint(ht *x, const void *key, unsigned klen)
{
	htbucket *n, *sn;
	unsigned long hash;
	int retry = 0;

	hash = x->hash(key, klen);
	sn = n = x->b[hash % x->s];
retry_fetchint_l:
	while (n) {
		if (n->hash == hash && n->klen == klen &&
				memcmp(key, n->key, klen) == 0) {
			return n->idata;
		}
		n = (htbucket *)n->next;
	}
	if (x->onfail && !retry) {
		if (x->onfail(key, klen) == HT_RESTART) {
			n = sn;
			retry = 1;
			goto retry_fetchint_l;
		}
	}
	return -1;
}
void *ht_fetch(ht *x, const void *key, unsigned klen)
{
	htbucket *n, *sn;
	unsigned long hash;
	int retry = 0;

	hash = x->hash(key, klen);
	sn = n = x->b[hash % x->s];
retry_fetch_l:
	while (n) {
		if (n->hash == hash && n->klen == klen &&
			memcmp(key, n->key, klen) == 0) {
			return n->data;
		}
		n = (htbucket *)n->next;
	}
	if (x->onfail && !retry) {
		if (x->onfail(key, klen) == HT_RESTART) {
			n = sn;
			retry = 1;
			goto retry_fetch_l;
		}
	}
	return (void *)0;
}
int ht_delete(ht *x, const void *key, unsigned klen)
{
	htbucket *n, *ln;
	unsigned long hash;
	unsigned pos;

	hash = x->hash(key, klen);
	n = x->b[pos = hash % x->s];
	ln = 0;
	while (n) {
		if (n->hash == hash && n->klen == klen &&
			memcmp(key, n->key, klen) == 0) {
			if (ln)
				ln->next = n->next;
			else
				x->b[pos] = n->next;
			mem_free(n->key);
			if (n->free_data)
				mem_free(n->data);
			mem_free(n);
			return 1;
		}
		ln = n;
		n = (htbucket *)n->next;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1