#include "dat.h"
#include "fn.h"

enum {
	Tagsize = sizeof(Hnode),
};

static uvlong
hash(Str *s)
{
	uvlong h;
	int i;

	h = 7;
	for(i = 0; i < s->n; i++)
		h = h*31 + s->r[i];
	return h;
}

Hmap*
hmapalloc(int nbuckets, int size)
{
	void *store;
	Hmap *h;
	int nsz;

	nsz = Tagsize + size;
	store = emalloc(sizeof(*h) + nbuckets * nsz);
	h = store;
	h->nbs = nbuckets;
	h->nsz = nsz;
	h->len = h->cap = nbuckets;
	h->nodes = (uchar*)store + sizeof(*h);
	return h;
}

static int
keycmp(Hnode *n, Str *key)
{
	char buf[256];
	int len;

	len = stoutf(key, buf, sizeof(buf));
	if(n->klen != len)
		return 1;
	return memcmp(n->key, buf, len);
}

Hnode*
hmapget(Hmap *h, Str *key)
{
	Hnode *n;
	uchar *v;

	v = h->nodes + (hash(key) % h->nbs) * h->nsz;
	for(;;){
		n = (Hnode*)v;
		if(n->filled && keycmp(n, key) == 0)
			return n;
		if(n->next == 0)
			break;
		v = h->nodes + n->next * h->nsz;
	}
	return nil;
}

static char*
sdup(Str *s)
{
	char buf[256];
	char *p;
	int n;

	n = stoutf(s, buf, sizeof(buf));
	p = emalloc(n + 1);
	memmove(p, buf, n);
	p[n] = '\0';
	return p;
}

int
hmapset(Hmap **store, Str *key, Str *val)
{
	Hnode *n;
	uchar *v;
	Hmap *h;
	int next;
	vlong diff;

	h = *store;
	v = h->nodes + (hash(key) % h->nbs) * h->nsz;
	for(;;){
		n = (Hnode*)v;
		next = n->next;
		if(n->filled == 0)
			goto replace;
		if(keycmp(n, key) == 0)
			goto replace;
		if(next == 0)
			break;
		v = h->nodes + next * h->nsz;
	}
	if(h->cap == h->len){
		diff = v - h->nodes;
		h->cap *= 2;
		*store = erealloc(*store, sizeof(*h) + h->cap * h->nsz);
		h = *store;
		h->nodes = (uchar*)*store + sizeof(*h);
		v = h->nodes + diff;
		n = (Hnode*)v;
	}
	n->next = h->len;
	memset(h->nodes + h->len * h->nsz, 0, h->nsz);
	h->len++;
	v = h->nodes + n->next * h->nsz;
	n = (Hnode*)v;
replace:
	if(n->filled == 0){
		n->key = sdup(key);
		n->klen = strlen(n->key);
		n->filled = 1;
	}
	n->next = next;
	if(val->n > 0){
		n->val = sdup(val);
		n->vlen = strlen(n->val);
	}
	return 0;
}
