/*---------------------------------------------------------------------------*/
/* リンク対応コマンドテーブル処理（中間コード化時：切り放し不可）*/

#include <stdlib.h>
#include <limits.h>
#include "abuff.h"
#include "stk.h"
#include "amacro.h"

static	unsint	*Chash;	/* コマンドハッシュアドレス */
static	unsint	Csize;	/* ハッシュサイズ */

/*---------------------------------------------------------------------------*/

static void STKCalcHash(STKCOM *tbl,int tno)
/* ハッシュ計算 */
{
unsint h;
	h=hashi(tbl->name)%Csize; /* コマンドは英大小区別無し */
	#if DEBUG == 2
	printf2("%s(%d):%04x(%d)\n",tbl->name,tno,h,Chash[h]);
	#endif
	/* ハッシュ登録（重複チェック付き）*/
	if (Chash[h]==0) Chash[h]=tno;
	else Chash[h]|=BIT15;
}

static void ResetCFJUMP(STKCOM *tbl)
/* .retのｼﾞｬﾝﾌﾟﾌﾗｸﾞ解除 */
{
	tbl->ret&=NCFJUMP;
}

static int ComFunc(void (*func)())
/* 各コマンドテーブルに対して指定処理をかける */
/* func==NULLのときはカウントのみ */
{
int i,tno=0; /* tno:総コマンド数 */
STKCOM *tbl;
	tbl=FirstComLink();
	do {
	    i=1;
	    while (tbl[i].name!=NULL) {
		if (++tno>=SHRT_MAX) return(-1); /* 総コマンド数 */
		if (func!=NULL) func(&tbl[i],tno);
		if (i++>CMDMAX) return(-1); /* 1ﾃｰﾌﾞﾙ中のｺﾏﾝﾄﾞが多すぎる */
	    }
	    if (func!=NULL) {
	    	/* そのテーブル内までの総コマンド数+1 */
	    	/*   = このテーブル内には何番までのコマンドが存在するか */
	    	tbl[0].mode=tno;
	    }
	    /* 次のテーブルへ */
	    tbl=(STKCOM *)tbl[0].name;
	} while (tbl!=NULL);
	/* リンクテーブル最後を見つけた */
	return(tno);
}

/*---------------------------------------------------------------------------*/

int OpenCommand()
/* コマンド処理初期化（リンク対応）*/
/* コマンドを高速検索するためにハッシュ表を作る */
{
long sz;
	/* コマンド数を数える */
	sz=(long)ComFunc(NULL);
	#if DEBUG
	printf2("コマンド数=%ld\n",sz);
	#endif
	if (sz<0||sz>=SHRT_MAX) return(COM2MACH); /* ｺﾏﾝﾄﾞが多すぎる(BIT15=0の範囲)*/
	
	/* ハッシュエリア確保（サイズ計算は経験による）*/
	sz=(sz/8)*128;	/* コマンド８つに対して128の領域を割り当てる計算 */
			/* 256にしてもほとんど重複率は下がらない */
	if (sz<1024L)   sz=1024L;  // min : 64コマンドの時まで最小
	if (sz>=65536L) sz=65536L; // max : 4096コマンドの時いっぱいになる
	if ((Chash=(unsint *)newmalloc(sz*LSHORT))==NULL) return(COMOPEN); /* 確保出来ない */
	Csize=sz; /* ハッシュ表サイズ記録 */
	#if DEBUG
	printf2("hash size=%ld\n",Csize);
	#endif
	
	/* ハッシュ表初期化 */
	while (--sz>=0) Chash[sz]=0;
	
	/* ハッシュテーブル作成 */
	ComFunc(STKCalcHash);
	return(OK);
}

void CloseCommand()
/* コマンド処理終了 */
{
	ComFunc(ResetCFJUMP); /* CFJUMPフラグリセット */
	free(Chash);
}

/*---------------------------------------------------------------------------*/

int CheckCommand(unchar *token,STKCOM *(*ctbl))
/* コマンドチェック */
{
int i,no;
int fdbl=0;
STKCOM *tbl;
	/* ハッシュサーチ : i=コマンドテーブル要素番号 */
	i=Chash[hashi(token)%Csize];
	if (i&BIT15) {fdbl=1; i^=BIT15;}
	#if DEBUG == 2
	printf2("<%s[%04x]%d>",token,hashi(token)%Csize,i);
	#endif
	if (i) {
		no=0; /* このテーブルの先頭番号（通し）-1 */
		tbl=FirstComLink();
		while (tbl[0].mode<i) {
			no=tbl[0].mode; /* 次のテーブルの先頭番号（通し）-1 */
			/* 次のテーブルへ */
			if ((tbl=(STKCOM *)tbl[0].name)==NULL) {
				return(STKSysErr("CheckCommand"));
			}
		}
		/* このテーブル内でのサーチ */
		i-=no; /* このテーブル内での番号 */
		do {
		    do {
			/* ハッシュが競合なく一致すれば比較は不要に思えるが、
			　　token自体のハッシュがたまたま一致することもあり得るので、
			　　文字列比較もする。競合しているときは無論である。*/
			if (strcmpi(token,tbl[i].name)==0) { /* 一致 */
				#if DEBUG
				printf2(":ﾄｰｸﾝ ");
				#endif
				*ctbl=tbl;
				return(i);
			}
			if (!fdbl) return(0); /* ﾊｯｼｭ重複なし:一致しなければ他にはない */
			i++;
		    } while (tbl[i].name!=NULL);
		    /* 次のテーブルへ */
		    tbl=(STKCOM *)tbl[0].name;
		    i=1;
		} while (tbl!=NULL);
	}
	/* コマンドではない */
	return(0);
}

/*---------------------------------------------------------------------------*/
