/*
 * dynamic hashed associative table for commands and variables
 */

#include "sh.h"
#include <search.h>

static int tbl_compare(const void *pa, const void *pb) {
	return strcmp(((struct tbl *)pa)->name, ((struct tbl *)pb)->name);
}

void
transitional_tinit(struct table *tp, Area *ap)
{
	tp->areap = ap;
	tp->tbls = NULL;
	tp->root = NULL;
	tp->size = tp->nfree = 0;
}

struct tbl *
transitional_tsearch(void **tbl_root, const char *n)
{
	struct tbl *p, **q;
	unsigned int len;

	len = strlen(n) + 1;
	p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len,
				 ATEMP);
	memcpy(p->name, n, len);

	q = tfind(p, (void **)tbl_root, tbl_compare);
	
	return q ? *q : NULL;
}

struct tbl *
transitional_tenter(void **tbl_root, const char *n, Area *ap)
{
	struct tbl *p, **q;
	unsigned int len;

	len = strlen(n) + 1;
	p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len,
				 ap);
	p->flag = 0;
	p->type = 0;
	p->areap = ap;
	p->u2.field = 0;
	p->u.array = (struct tbl *)0;
	memcpy(p->name, n, len);

	q = (struct tbl **)tsearch((void *)p, (void **)tbl_root, tbl_compare);

	return *q;
}

void
transitional_tdelete(void **tbl_root, struct tbl *p)
{
	tdelete((void *)p, tbl_root, tbl_compare);
}

#ifdef PERF_DEBUG /* performance debugging */

void tprintinfo ARGS((struct table *tp));

void
tprintinfo(tp)
	struct table *tp;
{
	struct tbl *te;
	char *n;
	unsigned int h;
	int ncmp;
	int totncmp = 0, maxncmp = 0;
	int nentries = 0;
	struct tstate ts;

	shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
	shellf("    Ncmp name\n");
	twalk(&ts, tp);
	while ((te = tnext(&ts))) {
		register struct tbl **pp, *p;

		h = hash(n = te->name);
		ncmp = 0;

		/* taken from tsearch() and added counter */
		for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
			ncmp++;
			if (*p->name == *n && strcmp(p->name, n) == 0
			    && (p->flag&DEFINED))
				break; /* return p; */
			if (pp == tp->tbls) /* wrap */
				pp += tp->size;
		}
		shellf("    %4d %s\n", ncmp, n);
		totncmp += ncmp;
		nentries++;
		if (ncmp > maxncmp)
			maxncmp = ncmp;
	}
	if (nentries)
		shellf("  %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
			nentries, maxncmp,
			totncmp / nentries,
			(totncmp % nentries) * 100 / nentries);
}
#endif /* PERF_DEBUG */
