#ifndef HASL2
static char sccsid[]="%Z% %M% %I% %E% %U%";
#endif
/************************************************/
/*												*/
/*	akxshasl.c									*/
/*												*/
/*		  coded by A.Kobayashi 2010/05/31		*/
/*												*/
/************************************************/
#ifndef HASL2
#include "akxcommon.h"
#endif

static int  max_count=0;

static int _hasld2();
static int _haslf();
int akxshaslr(/*hp,key*/);

int akxshasli(hp)
HASHB *hp;
{
	int i=0,mx,m1;
	int  opt;
	char c;

	if (!hp) return -1;
	if ((c=hp->ha_id[0]) == AKX_HASX_ID_NO_USE_ALL) opt = AKX_HASX_OPT_NO_USE_ALL;
	else if (c == AKX_HASX_ID_NO_NEXT) opt = AKX_HASX_OPT_NO_NEXT;
	else opt = 0;
	if ((mx=hp->ha_maxreg) <= 0) i -= 4;
	if ((m1=hp->ha_prereg) <= 0) i -= 8;
	if ((opt!=AKX_HASX_OPT_NO_NEXT) && hp->ha_prereg>=mx) i -= 16;
	if (!hp->ha_reg) i -= 32;

	if (i >= 0) {
		memset(hp->ha_reg,0,mx*sizeof(tdtHaslCell));
		hp->ha_aux = m1;
	}

	return i;
}

static int _haslf(hp,key)
HASHB *hp;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
{
	int i;

	if (!hp) return -1;
#ifdef HASL2
	if (!key) return -1224;
	i = (key[0]+key[1])%hp->ha_prereg + 1;
#else
	i = (ulong)key%hp->ha_prereg + 1;
#endif
	return i;
}

int akxshasls(hp,key)
HASHB *hp;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
{
	int i,k,maxk,iax,i_start,count,iha0,*data;
	tdtHaslCell *cell,*ce;
	int  opt;
	char c;

	if (!hp) return -1;
#ifdef HASL2
	if (!key) return -1224;
	if ((i=hp->ha_hix) <= 0) {
		i = (key[0]+key[1])%hp->ha_prereg + 1;
#else
	if ((i=hp->ha_hix) <= 0) {
		i = (ulong)key%hp->ha_prereg + 1;
#endif
	}
	else if (i > hp->ha_prereg) return -1225;

	cell = (tdtHaslCell *)hp->ha_reg - 1;
	data = hp->ha_next;
	maxk = hp->ha_maxreg;
	iax  = hp->ha_aux;
	i_start = i;
	count = 0;
	if ((c=hp->ha_id[0]) == AKX_HASX_ID_NO_USE_ALL) opt = AKX_HASX_OPT_NO_USE_ALL;
	else if (c == AKX_HASX_ID_NO_NEXT) opt = AKX_HASX_OPT_NO_NEXT;
	else opt = 0;
  L10:
	ce = &cell[i];
	if (!ce->hc_flag) {
		ce->hc_flag = 1;
#ifdef HASL2
		ce->hc_akey[0] = key[0];
		ce->hc_akey[1] = key[1];
#else
		ce->hc_hkey = key;
#endif
		ce->hc_datp = data;
		return i;
	}
#ifdef HASL2
	else if ((ce->hc_akey[0]==key[0]) && (ce->hc_akey[1]==key[1])) {
#else
	else if (ce->hc_hkey == key) {
#endif
		ce->hc_datp = data;
		return i;
	}

	if (opt == AKX_HASX_OPT_NO_NEXT) return 0;
	if (ce->hc_next > 0) {
		i = ce->hc_next;
		count++;
		if (i!=i_start && count<maxk) goto L10;
#ifdef NOTICE
		printf("hashs:loop i=%d count=%d\n",i,count);
#endif
	}

	if (iax<0) return 0;

	k = i;
	i = iax;
  L20:
	i++;
	if (i > maxk) {
		if (opt) {
			hp->ha_aux = -i;
			return 0;
		}
		else i = 1;
	}
	ce = &cell[i];
	if (!ce->hc_flag && !ce->hc_next) {
		ce->hc_flag = 1;
#ifdef HASL2
		ce->hc_akey[0] = key[0];
		ce->hc_akey[1] = key[1];
#else
		ce->hc_hkey = key;
#endif
		cell[k].hc_next = i;
		ce->hc_datp = data;
		hp->ha_aux = i;
		count++;
	}
	else if (!opt && i==iax) {
		hp->ha_aux = -i;
		i = 0;
	}
	else goto L20;
	return i;
}

int akxshaslr(hp,key)
HASHB *hp;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
{
	int i;
	tdtHaslCell *cell,*ce;

	if (!hp) return -1;
#ifdef HASL2
	if (!key) return -1224;
	if ((i=hp->ha_hix) <= 0) {
		i = (key[0]+key[1])%hp->ha_prereg + 1;
#else
	if ((i=hp->ha_hix) <= 0) {
		i = (ulong)key%hp->ha_prereg + 1;
#endif
	}
	else if (i > hp->ha_prereg) return -1225;

	cell = (tdtHaslCell *)hp->ha_reg - 1;
  L10:
	ce = &cell[i];
#ifdef HASL2
	if (ce->hc_flag && (ce->hc_akey[0]==key[0]) && (ce->hc_akey[1]==key[1]))
#else
	if (ce->hc_flag && (ce->hc_hkey==key))
#endif
		hp->ha_next = ce->hc_datp;
	else if ((i=ce->hc_next) > 0) goto L10;

	return i;
}

static int _hasld2(hp,key)
HASHB *hp;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
{
	int i,k;
	int nsp[10],isp;
	tdtHaslCell *cell,*ce;

	if (!hp) return -1;
#ifdef HASL2
	if (!key) return -1224;
	if ((i=hp->ha_hix)<=0) i = (key[0]+key[1])%hp->ha_prereg + 1;
#else
	if ((i=hp->ha_hix)<=0) i = (ulong)key%hp->ha_prereg + 1;
#endif
	else if (i > hp->ha_prereg) return -1225;

	cell = (tdtHaslCell *)hp->ha_reg - 1;
	isp = k = 0;
  L10:
	ce = &cell[i];
#ifdef HASL2
	if (!ce->hc_flag || (ce->hc_akey[0]!=key[0]) || (ce->hc_akey[1]!=key[1])) {
#else
	if (!ce->hc_flag || (ce->hc_hkey!=key)) {
#endif
		k = i;
		if (!ce->hc_flag) {
			if (isp < 10) nsp[isp++] = i;
			else {
				memcpy(&nsp[0],&nsp[1],(isp-1)*sizeof(int));
				nsp[isp-1] = i;
			}
		}
		else {
			isp = 0;
			nsp[isp++] = i;
		}
		if ((i=ce->hc_next) > 0) goto L10;
	}
	if (i > 0) {
		ce = &cell[i];
		ce->hc_flag = 0;
		hp->ha_next = ce->hc_datp;
		if (k > 0) {
			if (!ce->hc_next) {
				while (isp > 0) {
					cell[nsp[--isp]].hc_next = 0;
				}
			}
			else if (i > hp->ha_prereg) {
				cell[k].hc_next = ce->hc_next;
				ce->hc_next = 0;
			}
		}
		if (hp->ha_aux < 0) {
			hp->ha_aux = i - 1;
			if (hp->ha_aux <= 0) hp->ha_aux = hp->ha_maxreg;
		}
	}
	return i;
}

int akxshasld(hp,key)
HASHB *hp;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
{
	int i;
	tdtHaslCell *cell,*ce;
	char c;

	if (!hp) return -1;
	if (((c=hp->ha_id[0])!=AKX_HASX_ID_NO_USE_ALL) &&
	    (c!=AKX_HASX_ID_NO_NEXT)) return _hasld2(hp,key);

	i = akxshaslr(hp,key);
	if (i > 0) {
		cell = (tdtHaslCell *)hp->ha_reg - 1;
		ce = &cell[i];
		ce->hc_flag = 0;
		hp->ha_next = ce->hc_datp;
	}
	return i;
}

#ifdef HASL2
int akxshasl2k(hp,key)
HASHB *hp;
ulong key[];
#else
int akxshaslk(hp,keyp)
HASHB *hp;
long *keyp;
#endif
{
	int i;
	tdtHaslCell *ce;

	if (!hp) return -1;
	i = hp->ha_hix;
	if (i<=0 || i>hp->ha_maxreg) return -1225;
	ce = &((tdtHaslCell *)hp->ha_reg)[i] - 1;
	if (ce->hc_flag) {
#ifdef HASL2
		if (key) {
			key[0] = ce->hc_akey[0];
			key[1] = ce->hc_akey[1];
		}
#else
		if (keyp) *keyp = ce->hc_hkey;
#endif
		hp->ha_next = ce->hc_datp;
		return i;
	}
	return 0;
}

int akxshaslp(hp)
HASHB *hp;
{
	int i;
	tdtHaslCell *ce;

	if (!hp) return -1;
	i = hp->ha_hix;
	if (i<=0 || i>hp->ha_maxreg) return -1225;
	ce = &((tdtHaslCell *)hp->ha_reg)[i] - 1;
	if (ce->hc_flag) {
#ifdef HASL2
		hp->ha_key = (char *)ce->hc_akey;
#else
		hp->ha_key = (char *)&ce->hc_hkey;
#endif
		hp->ha_next = ce->hc_datp;
		return i;
	}
	return 0;
}

int akxshaslu(hp)
HASHB *hp;
{
	int x,i,max,n;

	if (!hp) return -1;
	x = hp->ha_hix;
	max = hp->ha_maxreg;
	n = 0;
	for (i=1;i<=max;i++) {
		hp->ha_hix = i;
#ifdef HASL2
		if (akxshasl2k(hp,NULL) > 0) n++;
#else
		if (akxshaslk(hp,NULL) > 0) n++;
#endif
	}
	hp->ha_hix = x;
	return n;
}

HASHB *akxs_hasl_new2(sKeyLen,lMaxReg,lPreReg,iOpt)
short sKeyLen;
int  lMaxReg,lPreReg;
int   iOpt;
{
	HASHB *tph;
	int l,pre;
	int  opt;
	char c;

	if (lMaxReg<2 || lPreReg<0) {
		errno = -1201;
		return NULL;
	}
	opt = iOpt & 0x03;
	if (lPreReg > 0) {
		if ((opt==AKX_HASX_OPT_NO_NEXT && lMaxReg<lPreReg) ||
		    (opt!=AKX_HASX_OPT_NO_NEXT && lMaxReg<=lPreReg)) {
			errno = -1202;
			return NULL;
		}
	}
	if (!(l=lPreReg)) {
		if (opt < AKX_HASX_OPT_NO_NEXT) pre = 0;
		else pre = 997;
		if ((l=akxs_hasx_pre_reg(lMaxReg,pre)) < 1) {
			errno = -1203;
			return NULL;
		}
	}
	if (!(tph=(HASHB *)Malloc(sizeof(HASHB)))) {
		errno = -1204;
		return NULL;
	}
	memset(tph,0,sizeof(HASHB));
#ifdef HASL2
	tph->ha_id[1] = '2';
#else
	tph->ha_id[1] = 'L';
#endif
	tph->ha_keylen = sKeyLen;
	tph->ha_maxreg = lMaxReg;
	tph->ha_prereg = l;
	tph->ha_hix    = 0;
	if (!(tph->ha_reg=Malloc(lMaxReg*sizeof(tdtHaslCell)))) {
		akxs_hasl_free(tph);
		errno = -1205;
		return NULL;
	}
	if (opt == AKX_HASX_OPT_NO_NEXT) c = AKX_HASX_ID_NO_NEXT;
	else if (opt == AKX_HASX_OPT_NO_USE_ALL) c = AKX_HASX_ID_NO_USE_ALL;
	else c = 'H';
	tph->ha_id[0] = c;
	if (errno=akxshasli(tph)) {
		akxs_hasl_free(tph);
		return NULL;
	}
	return tph;
}

int akxs_hasl_free(tph)
HASHB *tph;
{
	if (tph) {
		if (tph->ha_reg) Free(tph->ha_reg);
		Free(tph);
	}
	return 0;
}

int akxshasl(func,hp)
char func;
HASHB *hp;
{
	char cmd;
	int i;
#ifdef HASL2
	ulong key[2];
#else
	long key;
#endif

	if (!hp) return -1;

	switch (cmd=akxcupper(func)) {
	  case 'R':
	  case 'S':
	  case 'D':
	  case 'F':
			if (hp->ha_key) {
#ifdef HASL2
				key[0] = ((long *)hp->ha_key)[0];
				key[1] = ((long *)hp->ha_key)[1];
#else
				memcpy(&key,hp->ha_key,sizeof(long));
#endif
			}
			else return -1224;
			break;
	  default:
#ifdef HASL2
			key[0] = -1;
			key[1] = -1;
#else
	  		key = -1;
#endif
	}
	switch (cmd) {
	  case 'R':
			i = akxshaslr(hp,key);
			break;
	  case 'S':
			i = akxshasls(hp,key);
			break;
	  case 'D':
			i = akxshasld(hp,key);
			break;
	  case 'I':
			i = akxshasli(hp);
			break;
	  case 'K':
#ifdef HASL2
			i = akxshasl2k(hp,key);
			if (hp->ha_key) {
				((long *)hp->ha_key)[0] = key[0];
				((long *)hp->ha_key)[1] = key[1];
			}
#else
			i = akxshaslk(hp,&key);
			if (hp->ha_key) memcpy(hp->ha_key,&key,sizeof(long));
#endif
			break;
	  case 'P':
			i = akxshaslp(hp);
			break;
	  case 'U':
			i = akxshaslu(hp);
			break;
	  case 'F':
			i = _haslf(hp,key);
			break;
	  default:
			i = -2;
	}
	return i;
}

HASHB *akxs_hasl_new(sKeyLen,lMaxReg,lPreReg)
short sKeyLen;
int  lMaxReg,lPreReg;
{
	return akxs_hasl_new2(sKeyLen,lMaxReg,lPreReg,0);
}

static int _xhasl_chk(tpXHashb,cCmd,lH,ippDat)
tdtRbChain *tpXHashb;
char cCmd;
long lH;
int **ippDat;
{
	int ret;
	tdtRbChain *tpcurr;
	int i,offset,next,ix,max;
	HASHB *tph;

	ret = 0;
	i = lH;
	offset = 0;
	tpcurr = tpXHashb;
	tph = (HASHB *)tpcurr->rbc_buf;
	max = tph->ha_maxreg;
	while (tpcurr) {
		next = offset + max;
		if (next >= i) {
			if (tph=(HASHB *)tpcurr->rbc_buf) {
				tph->ha_hix = ix = i - offset;
				ret = akxshaslp(tph);
/*
printf("_xhasl_chk: i=%d ix=%d ret=%d\n",i,ix,ret);
*/
				if (ret > 0) {
					*ippDat = tph->ha_next;
					return i;
				}
			}
			break;
		}
		tpcurr = tpcurr->rbc_next;
		offset = next;
	}
	return ret;
}

static int _xhasl_max(tpXHashb)
tdtRbChain *tpXHashb;
{
	tdtRbChain *tpcurr;
	int max,count;
	HASHB *tph;

	count = 0;
	tpcurr = tpXHashb;
	tph = (HASHB *)tpcurr->rbc_buf;
	max = tph->ha_maxreg;
	while (tpcurr) {
		count += max;
		tpcurr = tpcurr->rbc_next;
	}
/*
printf("_xhasl_max: count=%d\n",count);
*/
	return count;
}

int akxs_xhasl(tpXHashb,cCmnd,key,ippDat)
tdtRbChain *tpXHashb;
char cCmnd;
#ifdef HASL2
ulong key[];
#else
long key;
#endif
int **ippDat;
{
	tdtRbChain *tpcurr,*tpnext;
	int l,i,offset,*dummy;
	HASHB *tph;
	char cCmd,c;
/*
printf("cpxsXHasl: tpXHashb=%08x cCmnd=%c key=%d ippDat=%08x\n",
tpXHashb,cCmnd,key,ippDat);
*/
	if (!tpXHashb) return -1;
	if (!ippDat) ippDat = &dummy;
	cCmd = akxcupper(cCmnd);
	if (cCmd=='P' || cCmd=='K')
#ifdef HASL2
		return _xhasl_chk(tpXHashb,cCmd,key[0],ippDat);
#else
		return _xhasl_chk(tpXHashb,cCmd,key,ippDat);
#endif
	else if (cCmd=='M') return _xhasl_max(tpXHashb);
	l = 0;
	offset = 0;
	tpnext = tpXHashb;
	while (tpnext) {
		tpcurr = tpnext;
		tph = (HASHB *)tpcurr->rbc_buf;
		tph->ha_hix = 0;
		if (cCmd == 'R') {
			i = akxshaslr(tph,key);
			if (i > 0) *ippDat = tph->ha_next;
		}
		else if (cCmd == 'S') {
			tph->ha_next = *ippDat;
			i = akxshasls(tph,key);
		}
		else if (cCmd == 'D') i = akxshasld(tph,key);
		if (i) {
			if (i>0) i += offset;
			return i;
		}
		tpnext = tpcurr->rbc_next;
		offset += tph->ha_maxreg;
	}
/*
printf("cpxsXHasl:(%08x)->tpXHNext=NULL\n",tpcurr);
*/
	if (cCmd=='R' || cCmd=='D') return i;
	c = tph->ha_id[0];
	if (!(tpnext=akxs_xhasl_new(tph->ha_keylen,tph->ha_maxreg,tph->ha_prereg,0))) return -76;
	tph = (HASHB *)tpnext->rbc_buf;
	tph->ha_id[0] = c;
	tpcurr->rbc_next = tpnext;
	if ((i=akxs_xhasl(tpnext,cCmd,key,ippDat)) > 0) i += offset;
	return i;
}

tdtRbChain *akxs_xhasl_new(sKeyLen,lMaxReg,lPreReg,iOpt)
short sKeyLen;
int  lMaxReg,lPreReg,iOpt;
{
	tdtRbChain *p;

	if (!(p=(tdtRbChain *)Malloc(sizeof(tdtRbChain)))) {
		errno = -1303;
		return NULL;
	}
	p->rbc_buf = (char *)akxs_hasl_new2(sKeyLen,lMaxReg,lPreReg,iOpt);
	p->rbc_next = NULL;
	return p;
}

int akxs_xhasl_free(tpXHashb)
tdtRbChain *tpXHashb;
{
	tdtRbChain *p;
	HASHB *tph;

	if (p=tpXHashb) {
		if (tph=(HASHB *)p->rbc_buf) akxs_hasl_free(tph);
		akxs_xhasl_free(p->rbc_next);
		Free(p);
	}
	return 0;
}
