/*****************************************************************************/
/* ＳＴＫコマンド：配列処理（切り離し可能／外部から利用可能）*/
/*****************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "stk.h"

extern STKSYS *stksys;

/* 配列

定義
( x y z ).次元(1) name(0) ARY.? → NADRS(0)
				.atrb  = NADRS
				.num   = 要素ofst
				.point = 配列top
		（NSTACK nstkが引数になっているものは、すべてこの設定）

		token	要素	要素サイズ	変数タイプ
		ARY.L	NNUM	long		VARY
		ARY.W	NNUM	short		VARY
		ARY.B	NNUM	char		VARY

	１次元の要素個数は16ビットで指定できる。従って１次元で確保できる
	最大サイズは4*64K=256Kバイトまでだが、これ以上必要なときには２次元以上
	を使えばいい。（２次元以上配列を１次元のように使うことは、コマンドに
	よって可能。）

	変数/スタックに記録されるのは、配列エリア内のオフセットである。
	（Abuff先頭からのオフセット。）
	これは、バッファーの先頭アドレスAbuffが領域拡大にともなって変更されても
	いいようにするためである。（オフセットは変わらないから。）
	絶対アドレス化は利用する各関数内で行なう。
	
	.numは要素内容の位置を示すため、0Lは有り得ない(.pointでは有り得る）。
	このため、ofst=0はNULLの意味を持たせる。

	配列の要素番号はＣと同じとなる。従って、定義が
		( n ). ary ARY.?
	の場合、アクセス出来るのは、
		( 0〜n-1 ). ary
	である。
	ということは、( 1 1 1 1 ). では　( 0 0 0 0 ). しかアクセス出来ないと
	いうことになる。

アドレス
	配列の指定要素のアドレスはname @で指定する。即ち、
		( x y z ). name @
	が&name[z][y][x]と同等となる。ただし、記録は相対アドレスである。

格納形態
	新規配列は&Abuff[Apoint]から格納される。
	スタックに／変数に記録される先頭アドレスはこのApointである。
	
	配列はヘッダーと本体からなる。
	[atrb size x y z r] 本体
	   ヘッダー部分
	
	x,y,z,r（実際にはJIMAX次元まである）の部分は各要素のサイズがunsintで
	記録されている。JIMAX次元以下の場合でも常にJIMAX分取られる。
	（高速化のため）
	
	atrbには属性が入る。atrbはunchar*2で全体としてunsintとなる。
	
	atrb0:=type
	BIT1,0：要素タイプ(TYBITS)
		%00/0=byte,%01/1=word,%10/2=未定義,%11/3=long
		=要素サイズ(byte)-1
		要素サイズはヘッダー部分が小さくなるように次元数を４に
		限定した。
	
	atrb1:=jigen
	BIT1,0：次元-1(JIBITS)
		=0〜3(1〜4=JIMAX)
	
	これ以外のビットは現在未使用。
	
	sizeには配列サイズ（バイト）が入る。このsizeはヘッダー部分を含まない、
	本体領域のサイズである。
	
	領域の先頭アドレスは必ず偶数アドレスからとなる。これは、アクセスを簡単
	（＝高速）にするためと、そこがトークンのワークエリアにも使われるため、
	偶数アドレスからでないとアドレスエラーが出るからである。（Ｃの関数が
	そういう作りになっているということ。）
*/
/*****************************************************************************/
// ここの関数名はすべてARY*にすること
/*****************************************************************************/

int ARYZoom(long size)
/* 配列エリア拡大（判定）*/
{
long len,Asize;
unchar *buff;
STKSYS *sys;
	sys=stksys; // 省メモリーのためローカル変数に置き換える
	Asize=sys->Asize;
	len=size-(Asize-sys->Apoint);
	if (len>0) { /* エリアが足りない */
		// 現在のエリア内には入りきらないとき → 領域拡大（少し広めに）
		if (len>ADABUFF) {
			Asize+=(len+ADABUFF);
		} else	Asize+=ADABUFF;
		// 拡大
		if ((buff=realloc(sys->Abuff,Asize))==NULL) return(AROVER);
		#if DEBUG
		printf2("Abuff:%p→%p\n",sys->Abuff,buff);
		#endif
		sys->Abuff=buff;
		sys->Asize=Asize;
	}
	return(OK);
}

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

int ARYMake(int jigen,unsint jsize[],int type,long *top)
/* 配列を確保する */
// type : 0(ARYBYTE)=byte,1(ARYWORD)=short,3(ARYLONG)=long
// top <- その配列の本体先頭アドレス（相対）
{
long size,Apoint;
unchar *des;
int i;
	// 次元のチェック
	if (jigen<=0||jigen>=JIMAX) return(JIGENERR);
	
	/* 各次元要素数から配列エリアサイズを計算する */
	size=(type+1);
	#if DEBUG
	printf2("[次元=%d][Typesize=%d]",jigen,size);
	#endif
	for (i=0;i<jigen;i++) {
		/* 要素数=0は認めない(unsintなので<0はない) */
		if (jsize[i]==0) return(ARSZERR);
		// 次元サイズの偶数化(byte要素時のみ)
		// byte以外の時は元から偶数サイズなので何もしない
		if (type==ARYBYTE) jsize[i]+=(jsize[i]&1);
		size*=(long)jsize[i];
	}
	#if DEBUG
	printf2("[Getsize=%d]",size);
	#endif
	
	// 配列を確保する（ヘッダー込み）
	if ((i=ARYZoom(ARYHEAD+size))) return(i);
	
	// ヘッダー書き込み：属性＋本体サイズ
	Apoint=stksys->Apoint;
	des=ARYadrs(Apoint);
	*des++=type;	   // 要素タイプ
	*des++=jigen-1;	   // 次元-1
	*(long *)des=size; // 本体サイズ
	des+=LLONG;
	
	// 各次元要素数を記録
	for (i=0;i<jigen;i++) *(unsint *)(des+LSHORT*i)=jsize[i];
	des+=LSHORT*JIMAX;
	Apoint+=ARYHEAD;
	*top=Apoint; // この配列の本体先頭位置を記録
	
	// クリア＋次の記録位置へApointを移動
	Apoint+=size;
	while (size-->0) *des++=0;
	stksys->Apoint=Apoint;
	return(OK);
}

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

int ARYJAdrs(long base,int jigen,unsint jno[],long *ofst)
/* 指定番号要素のオフセットを得る */
/* base:配列topアドレス , jigen:配列次元 , jno[]:要素番号 */
// ofst <- オフセットアドレス
{
int i,type;
long bs,of;
unsint jn;
unchar *adrs;
	// 属性読みだし
	adrs=ARYadrs(base);
	type=((*adrs++)&TYBITS)+1; // 要素サイズ
	if (((*adrs++)&JIBITS)+1!=jigen) return(JIGENERR); // 次元が合わない
	adrs+=LLONG;
	
	/* オフセット計算 */
	/* ofst=z*Ysize*Xsize+y*Xsize+x(*1) */
	of=0;
	bs=1; // 各次元毎のベース
	jn=1; // 最初の１つ前の次元サイズは１とする
	for (i=0;i<jigen;i++) { // x -> y -> z -> r
		bs*=jn;
		// その次元の要素数を得る
		jn=*(unsint *)adrs;
		// その要素番号が配列の全要素数内に収まるか
		if (jno[i]>=jn||jno[i]<0) return(ARYOUSO); // 収まらない
		adrs+=LSHORT;
		of+=(jno[i]*bs);
	}
	*ofst=of*type+base+ARYHEAD; /* 要素サイズをかける+先頭 */
	return(OK);
}

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

int ARYyouso(int jigen,unsint jno[JIMAX])
// 配列の要素番号（次元）読みとり
// jigen(次元数) -> jno(各次元の番号)
// ここではjigenに上限(JIMAX)はつけない
{
int i,ret;
long num;
NSTACK ns;
	if (jigen<=0) return(JIGENERR); /* 次元が0以下の時 */
	for (i=0;i<jigen;i++) {
		if ((ret=STKChgVarNumSub(NNUM,&ns))) break;
		 // 次元数は数値
		num=ns.num;
		if (num>USHRT_MAX||num<0L) return(ARYOUSO);
			// 要素番号に16bitより大きい数値や負数は使えない
		jno[i]=(unsint)num;
	}
	return(ret);
}

/*---------------------------------------------------------------------------*/
// アドレスはオフセットのもの
/*---------------------------------------------------------------------------*/

unchar *ARYSTRoadrs(NSTACK *nstk)
// NSTR1の時は絶対アドレスとしてそのまま、TOADRS->NADRSのときは絶対アドレス化して返す
// NSTR1のNULL対応
{
	return(nstk->atrb==NSTR1? Cadrs(nstk->num):ARYadrs(nstk->num));
}

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

long ARYosize(NSTACK *nstk)
// 配列nstk.numから後ろの領域サイズ
{
	// 全サイズ-前サイズ
	#if DEBUG
	printf2("[ARYo:現在=%d,top=%d]",nstk->num,nstk->point+ARYHEAD);
	#endif
	return(*(long *)ARYadrs(nstk->point+LSHORT)-ARYbsize(nstk));
}

/*---------------------------------------------------------------------------*/
// アドレスはオフセットで相対でもどちらでも揃っていれば良いもの
/*---------------------------------------------------------------------------*/

long ARYbsize(NSTACK *nstk)
// 配列nstk.num(を含まない)までの領域サイズ
{
	// 現在位置-本体先頭
	#if DEBUG
	printf2("[ARYbsize=%d]",nstk->num-(nstk->point+ARYHEAD));
	#endif
	return(nstk->num-(nstk->point+ARYHEAD));
}

/*---------------------------------------------------------------------------*/
// これ以降、アドレスは全てすでに絶対アドレスになっているものとする
/*---------------------------------------------------------------------------*/

long ARYsize(NSTACK *nstk)
// 配列nstk.numから後ろの領域サイズ
{
	// 全サイズ-前サイズ
	#if DEBUG
	printf2("[ARY:現在=%p,top=%p]",Cadrs(nstk->num),Cadrs(nstk->point+ARYHEAD));
	#endif
	return(*(long *)(nstk->point+LSHORT)-ARYbsize(nstk));
}

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

long ARYSTRSize(NSTACK *nstk)
// NSTR1の時はlen(EOSを含む)、NADRSのときはARYsizeを返す
{
	return(nstk->atrb==NSTR1? nstk->point:ARYsize(nstk));
}

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

long ARYSTRLen(NSTACK *ns1,NSTACK *ns2)
// <=0のときは元データ長を返す
{
	return(ns1->num>0? ns1->num:ARYSTRSize(ns2));
}

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

int ARYStoreB(NSTACK *nstk,unchar *str,long len)
// 配列領域にbyteデータ群を書き込む（領域オーバーチェック付き）
{
unchar *adrs;
	if (len==0L) len=strlen(str)+1; // 文字列型格納：EOSまで
	if (ARYChkSize(nstk,len)) return(ARSZOVER); // 領域を越える
	adrs=Cadrs(nstk->num);
	while (len-->0L) *adrs++=*str++;
	return(OK);
}

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

int ARYWrite(NSTACK *nstk,long dt)
// 配列１要素に書き込む
// nstk.point : 配列先頭アドレス
// nstk.num   : ARYJAdrsで求めた要素のアドレス
// dt         : 書き込むデータ；LONG以外は上位ビットは切り捨てられる
{
unchar *adrs;
	adrs=Cadrs(nstk->num);
	#if DEBUG
	printf2("[num=%p,point=%p]",adrs,Cadrs(nstk->point));
	#endif
	switch ((*Cadrs(nstk->point))&TYBITS) { // 配列タイプ別
		case ARYBYTE:/* byte */
			*adrs=(unchar)dt;
			break;
		case ARYWORD:/* word */
			*(unsint *)adrs=(unsint)dt;
			break;
		case ARYLONG:/* long */
			*(long *)adrs=dt;
			break;
	}
	return(OK);
}

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

long ARYRead(NSTACK *nstk,int mode)
// 配列１要素を読み込む
// nstk.point : 配列先頭アドレス
// nstk.num   : ARYJAdrsで求めた要素のオフセットアドレス
// mode       : 0=unsigned , 1=signed (LONG以外の時)
// リターン値 : 読み込んだデータ
{
unchar *adrs;
long ret;
	adrs=Cadrs(nstk->num);
	#if DEBUG
	printf2("[num=%p,point=%p]",adrs,Cadrs(nstk->point));
	#endif
	switch ((*Cadrs(nstk->point))&TYBITS) { // 配列タイプ別
		case ARYBYTE:/* byte */
			// charがGCCのオプションでunsignedになっているので、
			// ここではあえて符号付きにするためにsigned宣言している
			// このバグを解消するのに１時間かかってしまった
			if (!mode) ret=*adrs; else ret=*(signed char *)adrs;
			break;
		case ARYWORD:/* word */
			if (!mode) ret=*(unsint *)adrs; else ret=*(short *)adrs;
			break;
		case ARYLONG:/* long */
			ret=*(long *)adrs;
			break;
	}
	return(ret);
}

/*****************************************************************************/
