/*
 * dml - Dialog Markup Language
 *
 * $Id: phash.c,v 1.2 2002/07/29 13:07:05 malekith Exp $
 * Author: Michal Moskal <malekith@pld-linux.org>
 * include COPYING-GNU
 */

#include "phash.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pi.h"

#define NEW(type) 		(type*)xmalloc(sizeof(type))
#define NEW_ARRAY(type, n)	(type*)xmalloc(sizeof(type) * (n))

#define halt() assert(0)

static inline unsigned int hash(const char *name)
{
	unsigned int v = 0;
	
	while (*name) 
		v = 33 * v + *name++;
		
	return v;
}

void *ph_find(struct phash *h, const char *name)
{
	struct phash_entry *p;
	
	for (p = h->tab[hash(name) & h->mask]; p; p = p->next)
		if (strcmp(p->name, name) == 0)
			break;

	return p;
}

static void ph_rehash(struct phash *h)
{
	unsigned int oldmask, i;
	struct phash_entry **oldtab, *p, *n;

	oldmask = h->mask;
	oldtab = h->tab;
	
	h->mask = (h->mask << 1) + 1;
	h->tab = NEW_ARRAY(struct phash_entry*, (h->mask + 1));

	for (i = 0; i <= h->mask; i++)
		h->tab[i] = 0;
		
	h->el = 0;
	
	if (oldtab == 0)
		return;
		
	for (i = 0; i <= oldmask; i++)
		for (p = oldtab[i]; p; p = n) {
			n = p->next;
			ph_add(h, p);
		}
	xfree(oldtab);
}

struct phash *ph_new()
{
	struct phash *h;

	h = NEW(struct phash);
	h->tab = 0;
	h->mask = 7;
	ph_rehash(h);

	return h;
}

void ph_std_kill(struct phash *h)
{
	unsigned int i;
	struct phash_entry *p, *n;
	
	for (i = 0; i <= h->mask; i++)
		for (p = h->tab[i]; p; p = n) {
			n = p->next;
			xfree(p);
		}
	
	xfree(h->tab);
	h->tab = 0;
	xfree(h);
}

void ph_kill(struct phash *h, void (*kill_item)(struct phash_entry *))
{
	unsigned int i;
	struct phash_entry *p, *n;
	
	if (kill_item) {
		for (i = 0; i <= h->mask; i++)
			for (p = h->tab[i]; p; p = n) {
				n = p->next;
				kill_item(p);
			}
	}
	
	xfree(h->tab);
	h->tab = 0;
	xfree(h);
}

void ph_add(struct phash *h, void *ee)
{
	struct phash_entry *e = (struct phash_entry*)ee;
	int p;

	p = hash(e->name) & h->mask;

	e->next = h->tab[p];
	h->tab[p] = e;
	
	if (++(h->el) > h->mask)
		ph_rehash(h);
}

void *ph_unlink(struct phash *h, const char *name)
{
	struct phash_entry *e, *p;
	int x;

	x = hash(name) & h->mask;
	
	e = h->tab[x];
	
	if (strcmp(e->name, name) == 0) {
		h->tab[x] = e->next;
		return e;
	}

	while (e->next && strcmp(e->next->name, name))
		e = e->next;

	if (e->next == 0)
		return 0;

	p = e->next;
	e->next = p->next;

	return p;
}

/*
 * dbuf - dynamic buffer
 */

struct dbuf *db_new()
{
	struct dbuf *r;

	r = NEW(struct dbuf);
	r->data = (byte*)xmalloc(r->size = 16);
	r->off = 0;

	return r;
}

void db_kill(struct dbuf *p)
{
	xfree(p->data);
	xfree(p);
}

void *db_finish(struct dbuf *p, int *size)
{
	void *r;

	r = xmalloc(p->off ? p->off : 1);
	memcpy(r, p->data, p->off);
	if (size)
		*size = p->off;
	db_kill(p);
	return r;
}

void *db_alloc(struct dbuf *r, int n)
{
	int old_off;
	
	old_off = r->off;
	r->off += n;

	if (r->size < r->off) {
		while (r->size < r->off)
			r->size <<= 1;
		r->data = xrealloc(r->data, r->size);
	}
	
	return r->data + old_off;
}

void *db_add(struct dbuf *db, const void *data, int size)
{
	return memmove(db_alloc(db, size), data, size);
}

/*
 * pstack - pointer stack
 */
struct pstack *pstack_new()
{
	struct pstack *r;

	r = NEW(struct pstack);
	r->beg = r->sp = NEW_ARRAY(void*, 16);
	r->end = r->sp + 16;

	return r;
}

void pstack_kill(struct pstack *r)
{
	xfree(r->beg);
	xfree(r);
}

int pstack_empty(struct pstack *r)
{
	return r->sp == r->beg;
}

void *pop(struct pstack *r)
{
	assert(r->sp > r->beg);
	return *(--(r->sp));
}

void *peek(struct pstack *r)
{
	assert(r->sp > r->beg);
	return *(r->sp);
}

void push(struct pstack *s, void *val)
{
	struct pstack *r = (struct pstack*)s;
	void **nb;
	
	if (r->sp == r->end) {
		/* realloc it to be twice as big as it was */
		nb = (void**)xrealloc(r->beg, 
			             ((byte*)r->end - (byte*)r->beg) * 2);
		r->sp = nb + (r->sp - r->beg);
		r->end = nb + (r->end - r->beg) * 2;
		r->beg = nb;
	}

	*((r->sp)++) = val;
}
