static    char    sccsid[]="%Z% %M% %I% %E% %U%";
/************************************************
 *
 *	akxsCache.c
 *
 *	    coded   by A.Kobayashi 96.5.24
 *	  updated   by A.Kobayashi 97.1.26
 *
 ************************************************/
#include "akxcommon.h"

static struct {
	time_t    my_mtime;	/* ファイルの最後の変更時間 */
	off_t     st_size;	/* ファイルの合計サイズ (バイト単位) */
} tMod;

#define DEBUGP(x)
/********************************************
#undef Malloc
#undef Free
#define Malloc	malloc
#define Free	free
int main()
{
	tdtCacheCtlHead *pCH;
	tdtCacheCtl qCt;
	char *cp, buf[10];
	int rc,d;

	pCH = akxs_cache_new(3);
	qCt.ca_cmp1 = qCt.ca_key = "f1";
	qCt.ca_data = buf;
	printf("set 1:rc=%d\n",akxs_cache_set(pCH,&qCt));
	qCt.ca_cmp1 = qCt.ca_key = "f2";
	printf("set 2:rc=%d\n",akxs_cache_set(pCH,&qCt));
	qCt.ca_cmp1 = qCt.ca_key = NULL;
	printf("set NULL,NULL:rc=%d\n",akxs_cache_set(pCH,&qCt));
	qCt.ca_key = "f3";
	qCt.ca_cmp1= NULL;
	printf("set 3,NULL:rc=%d\n",akxs_cache_set(pCH,&qCt));
	qCt.ca_cmp1 = qCt.ca_key = "f3";
	printf("set 3:rc=%d\n",akxs_cache_set(pCH,&qCt));
	qCt.ca_cmp1 = qCt.ca_key = "f4";
	printf("set 4:rc=%d\n",akxs_cache_set(pCH,&qCt));

	for (;;) {
		printf("Enter file name:");
		gets(buf);
		if (*buf == '/') break;
		rc = akxs_cache_chk(pCH,buf,&qCt);
		printf("rc=%d\n",rc);
		if (rc>=0) {
			memcpy((char *)&d,qCt.ca_cmp2,4);
			printf("Key=[%s] Cmp1=[%s] Cmp2=%d Dat=[%s]\n",
			       qCt.ca_key,qCt.ca_cmp1,d,qCt.ca_data);
		}
	}
	rc=akxs_cache_free(pCH);
	printf("Free:rc=%d\n",rc);

	exit(0);
}
**************************************************/
static int _get_msec(pCH,pCt)
tdtCacheCtlHead *pCH;
tdtCacheCtl *pCt;
{
	int t;
	struct timeval *ptval;
	struct timeval *ptlval;

	if (!pCt) return -1;
	ptval = &pCt->ca_tval;
	t = akxe_get_msec(NULL,ptval);
	if (pCH) {
		ptlval = &pCH->ch_tctval;
		if (akxe_timer_cmp(ptlval,ptval) >= 0) {
printf("_get_msec: Latest(%d.%06d) >= now(%d.%06d)\n",ptlval->tv_sec,ptlval->tv_usec,ptval->tv_sec,ptval->tv_usec);
			ptlval->tv_usec++;
			akxe_timer_add(ptlval,ptlval,NULL);
			*ptval = *ptlval;
			t = ptval->tv_sec;
		}
	}
	return t;
}

/*tdtCacheCtlHead *akxs_cache_new(int iMax)*/
tdtCacheCtlHead *akxs_cache_new(iMax)
int iMax;
{
	tdtCacheCtlHead *pCH;
	tdtCacheCtl **ppCt;
	int i;

	if (iMax==0) return NULL;
	
	if (pCH = (tdtCacheCtlHead *)Malloc(sizeof(tdtCacheCtlHead))) {
		memset(pCH,0,sizeof(tdtCacheCtlHead));	/* add 2000.12.13 Koba */
		if (iMax<0) {
			iMax = -iMax;
			pCH->ch_opt |= AKX_CACHE_NOAUTOEXT;
		}
		if (ppCt = (tdtCacheCtl **)Malloc(sizeof(tdtCacheCtl *)*iMax)) {
			for (i=0;i<iMax;i++) ppCt[i] = NULL;
			pCH->ch_imax = iMax;
			pCH->ch_kind = 1;
			pCH->ch_keylen = 0;
			pCH->ch_cmplen = sizeof(time_t)+sizeof(off_t);
			pCH->ch_cshlen = 0;
			pCH->ch_ppcache  = ppCt;
		}
		else {
			Free(pCH);
			pCH = NULL;
		}
	}
	return pCH;
}

/*void static _cache_free(unsigned char ucOpt,tdtCacheCtl *pCt)*/
void static _cache_free(ucOpt, pCt)
unsigned char ucOpt;
tdtCacheCtl *pCt;
{
	if (pCt->ca_key ) Free(pCt->ca_key );
	if (pCt->ca_cmp1) Free(pCt->ca_cmp1);
	if (pCt->ca_cmp2) Free(pCt->ca_cmp2);
	if (ucOpt & AKX_CACHE_DATASAVE) {
		if (pCt->ca_data) Free(pCt->ca_data);
	}
/*
	pCt->ca_key  = NULL;
	pCt->ca_cmp1 = NULL;
	pCt->ca_cmp2 = NULL;
	pCt->ca_data = NULL;
*/
	memset(pCt,0,sizeof(tdtCacheCtl));
}

/*int akxs_cache_free(tdtCacheCtlHead *pCH)*/
int akxs_cache_free(pCH)
tdtCacheCtlHead *pCH;
{
	tdtCacheCtl **ppCt, *pCt, **ppCt0;
	int i;

	if (!pCH) return -1;
	if (!(ppCt0 = ppCt = pCH->ch_ppcache)) return -2;
	for (i=0;i<pCH->ch_smax;i++,ppCt++) {
		if (pCt = *ppCt) {
			_cache_free(pCH->ch_opt,pCt);
			Free(pCt);
		}
	}
	Free(ppCt0);
	if (pCH->ch_prls) {
		_cache_free(pCH->ch_opt,pCH->ch_prls);
		Free(pCH->ch_prls);
	}
	Free(pCH);
	
	return 0;
}

/*int static _cache_srch(tdtCacheCtlHead *pCH,char *cpKey)*/
int static _cache_srch(pCH, cpKey)
tdtCacheCtlHead *pCH;
char *cpKey;
{
	tdtCacheCtl **ppCt, *pCt;
	int i,iKeyLen;
	unsigned char ucOpt;

	if (!pCH) return -1;
	pCH->ch_entry = 0;
	if (!cpKey) return -3;	/* add 1998.8.13 Koba */
	if (!(ppCt = pCH->ch_ppcache)) return -2;
	ucOpt   = pCH->ch_opt;
	iKeyLen = pCH->ch_keylen;
	for (i=0;i<pCH->ch_smax;i++,ppCt++) {
		if (pCt = *ppCt) {
			if (pCt->ca_key) {	/* add 1998.8.13 Koba */
				if (ucOpt&AKX_CACHE_FIXEDKEYL) {
					if (!memcmp(cpKey,pCt->ca_key,iKeyLen)) {
						pCH->ch_entry = ++i;
						return i;
					}
				}
				else {
/*
printf("_cache_srch: cpKey=[%s] i=%d pCt=%08x cpCKey=%08x CKey=[%s]\n",
cpKey,i,pCt,pCt->ca_key,pCt->ca_key);
*/
					if (!strcmp(cpKey,pCt->ca_key)) {
						pCH->ch_entry = ++i;
						return i;
					}
				}
			}
		}
	}
	return 0;
}

/*int akxs_cache_del(tdtCacheCtlHead *pCH,char *cpKey)*/
int akxs_cache_del(pCH, cpKey)
tdtCacheCtlHead *pCH;
char *cpKey;
{
	int iRc;

	if ((iRc=_cache_srch(pCH,cpKey))>0) {
		_cache_free(pCH->ch_opt,pCH->ch_ppcache[iRc-1]);
		if (iRc>=1 && iRc==pCH->ch_smax) pCH->ch_smax--;
	}
	return iRc;
}

int akxs_cache_lock(pCH, cpKey, lock_sec)
tdtCacheCtlHead *pCH;
char *cpKey;
int lock_sec;
{
	int iRc,t;
	tdtCacheCtl *pCt;

	if ((iRc=_cache_srch(pCH,cpKey))>0) {
		pCt = pCH->ch_ppcache[iRc-1];
		if (lock_sec > 0) t = pCt->ca_tval.tv_sec + lock_sec;
		else t = 0;
		pCt->ca_locktime = t;
	}
	return iRc;
}

/***************************************************/
/*  ret < 0 : error                                */
/*      = 0 : 登録エントリ空きなし                 */
/*      > 0 : 登録エントリ番号                     */
/***************************************************/
/*int akxs_cache_set(tdtCacheCtlHead *pCH,tdtCacheCtl *pCt0)*/
int akxs_cache_set(pCH, pCt0)
tdtCacheCtlHead *pCH;
tdtCacheCtl *pCt0;
{
	tdtCacheCtl **ppCt, *pCt, *pOldCt;
	int iRc, i, iKeyLen, iCmpLen, iMax, iCshLen, iOldTime, iOldCt;
	unsigned char ucOpt;
	struct stat tStat;
	char *p;
	int t;
	struct timeval tval0;

	if (!pCt0) return -25;	/* add 2001.1.26 Koba */
	if (iRc=_cache_srch(pCH,pCt0->ca_key)) {
		if (iRc>0) {
			ucOpt   = pCH->ch_opt;
			iCshLen = pCH->ch_cshlen;
			pCt     = pCH->ch_ppcache[iRc-1];
			if (ucOpt & AKX_CACHE_DATASAVE) {	/* malloc cache data area */
				if (!pCt0->ca_data) return -7;
				if (!pCt->ca_data) return -13;
			/* 2000.10.5 Koba
				if (iCshLen<0) return -14;
				if (!iCshLen) {
			*/
				if (iCshLen <= 0) {
					if (iCshLen) {
						memcpy(&iCshLen,pCt0->ca_data,sizeof(INT4));
						iCshLen += sizeof(INT4);
					}
					else iCshLen = strlen(pCt0->ca_data) + 1;
					if (!(p=Realloc(pCt->ca_data,iCshLen))) {
						return -15;
					}
					pCt->ca_data = p;
				}
				memcpy(pCt->ca_data,pCt0->ca_data,iCshLen);
			}
			else pCt->ca_data = pCt0->ca_data;
#if 0	/* 2001.1.23 Koba */
			if (pCH->ch_interval > 0) {
				pCt->ca_cmpnext = akxe_get_msec(NULL,NULL);
/*
printf("akxs_cache_set:Key=[%s] CmpNext=%d\n",pCt->ca_key,pCt->ca_cmpnext);
*/
			}
#endif
			if (ucOpt & AKX_CACHE_NOAUTOEXT) {
			/*	pCt->ca_chktime = */_get_msec(pCH,pCt);
/*
printf("akxs_cache_set:ChkTime=%d\n",pCt->ca_chktime);
*/
			}
		}
		return iRc;
	}
	else {
		ppCt = pCH->ch_ppcache;
		iOldTime = 0x7fffffff;
		tval0.tv_sec  = INT_MAX;
		tval0.tv_usec = INT_MAX;
		pCt = pOldCt = NULL;
		t = akxe_get_msec(NULL,NULL);
		for (i=0;i<pCH->ch_smax;i++) {
			if (pCt=ppCt[i]) {
				if (!pCt->ca_key) break;
				if (pCt->ca_locktime < t) {
				/*	if (pCt->ca_chktime < iOldTime) {
						iOldTime = pCt->ca_chktime;	*/
					if (akxe_timer_cmp(&pCt->ca_tval,&tval0) < 0) {
						tval0 = pCt->ca_tval;
						pOldCt = pCt;
						iOldCt = i;
					}
				}
				pCt = NULL;
			}
			else break;
		}
/*
printf("Srch:i=%d\n",i);
*/
		if (i>=pCH->ch_imax) {
			if (!(pCH->ch_opt & AKX_CACHE_NOAUTOEXT)) {
				iMax = pCH->ch_imax + AKX_CACHE_EXTENT;
				if (ppCt=(tdtCacheCtl **)Realloc(ppCt,sizeof(tdtCacheCtl *)*iMax)){
					for (i=pCH->ch_imax;i<iMax;i++) ppCt[i] = NULL;
					pCH->ch_imax = iMax;
					pCH->ch_ppcache   = ppCt;
					return akxs_cache_set(pCH,pCt0);
				}
			}
			else if (pOldCt) {
				if (pCH->ch_prls) {
					_cache_free(pCH->ch_opt,pCH->ch_prls);
					Free(pCH->ch_prls);
				}
				pCH->ch_prls = pOldCt;
				ppCt[iOldCt] = NULL;
				return akxs_cache_set(pCH,pCt0);
			}
			return 0;
		}
		else {
			if (!pCt) {
				if (!(pCt=(tdtCacheCtl *)Malloc(sizeof(tdtCacheCtl)))) return -3;
			/*
				pCt->ca_key  = NULL;
				pCt->ca_cmp1 = NULL;
				pCt->ca_cmp2 = NULL;
				pCt->ca_data = NULL;
			*/
				memset(pCt,0,sizeof(tdtCacheCtl));
				ppCt[i] = pCt;
			}
			if (i>=pCH->ch_smax) pCH->ch_smax++;
		}
	}

	ucOpt   = pCH->ch_opt;
	iKeyLen = pCH->ch_keylen;
	iCmpLen = pCH->ch_cmplen;
	iCshLen = pCH->ch_cshlen;
	pCt     = pCH->ch_ppcache[i];
	if (iCmpLen<=0) return -4;
	if (!pCt0->ca_key) return -5;
	if (!pCt0->ca_cmp1) return -6;
	if (ucOpt & AKX_CACHE_DATASAVE) {	/* malloc cache data area */
		if (!pCt0->ca_data) return -7;
	/* 2000.10.5 Koba
		if (iCshLen<0) return -14;
	*/
	}

	if (ucOpt & AKX_CACHE_FIXEDKEYL) {	/* fixed len */
		if (iKeyLen<=0) return -8;
		if (!(pCt->ca_key=Malloc(iKeyLen))) return -9;
		memcpy(pCt->ca_key,pCt0->ca_key,iKeyLen);
	}
	else {	/* SZ */
		if (!(pCt->ca_key=Malloc(strlen(pCt0->ca_key)+1))) return -10;
		strcpy(pCt->ca_key,pCt0->ca_key);
/*
XLOGFOUT("xdebug_log",pCt0->ca_key,strlen(pCt0->ca_key));
*/
	}
	if (!(pCt->ca_cmp2=Malloc(iCmpLen*2))) {
			_cache_free(ucOpt,pCt);
			return -11;
	}
	if (ucOpt & AKX_CACHE_MEMCMP) {	/* memory */
		pCt->ca_cmp1 = pCt0->ca_cmp1;
		memcpy(pCt->ca_cmp2,pCt->ca_cmp1,iCmpLen);
	}
	else {	/* file */
		if (!(pCt->ca_cmp1=Malloc(strlen(pCt0->ca_cmp1)+1))) {
			_cache_free(ucOpt,pCt);
			return -12;
		}
		strcpy(pCt->ca_cmp1,pCt0->ca_cmp1);	/* file name */
		if (stat(pCt->ca_cmp1,&tStat)) {
			if (ucOpt & AKX_CACHE_IGNCHKERR) {
				tStat.st_mtime = tStat.st_size = 0;
			}
			else {
				_cache_free(ucOpt,pCt);
				return -21;
			}
		}
		tMod.my_mtime = tStat.st_mtime;
		tMod.st_size  = tStat.st_size;
		memcpy(pCt->ca_cmp2,(char *)&tMod,iCmpLen);
	}
	memset(pCt->ca_cmp2+iCmpLen,0,iCmpLen);
	if (ucOpt & AKX_CACHE_DATASAVE) {	/* malloc cache data area */
		if (iCshLen <= 0) {
			if (iCshLen) {
				memcpy(&iCshLen,pCt0->ca_data,sizeof(INT4));
				iCshLen += sizeof(INT4);
			}
			else iCshLen = strlen(pCt0->ca_data) + 1;
		}
		if (!(pCt->ca_data=Malloc(iCshLen))) {
			_cache_free(ucOpt,pCt);
			return -15;
		}
		memcpy(pCt->ca_data,pCt0->ca_data,iCshLen);
	}
	else pCt->ca_data = pCt0->ca_data;
	if (pCH->ch_interval > 0) {
		pCt->ca_cmpnext = akxe_get_msec(NULL,NULL) + pCH->ch_interval;
/*
printf("akxs_cache_set:Key=[%s] CmpNext=%d\n",pCt->ca_key,pCt->ca_cmpnext);
*/
	}
	if (ucOpt & AKX_CACHE_NOAUTOEXT) {
	/*	pCt->ca_chktime = */_get_msec(pCH,pCt);
/*
printf("akxs_cache_set:ChkTime=%d\n",pCt->ca_chktime);
*/
	}
	pCt->ca_locktime = 0;

	pCH->ch_entry = ++i;
	return i;
}

/***************************************************/
/*  ret < 0 : error                                */
/*      = 0 : キャッシュは有効                     */
/*      > 0 : キャッシュは無効                     */
/***************************************************/
/*int akxs_cache_chk(tdtCacheCtlHead *pCH,char *cpKey,tdtCacheCtl *pCt0)*/
int akxs_cache_chk(pCH, cpKey, pCt0)
tdtCacheCtlHead *pCH;
char *cpKey;
tdtCacheCtl *pCt0;
{
	tdtCacheCtl **ppCt, *pCt;
	int iRc, iKeyLen, iCmpLen;
	unsigned char ucOpt;
	struct stat tStat;
	int t,ichk;

	if (!pCt0) return -25;	/* add 2001.1.26 Koba */
	if ((iRc=_cache_srch(pCH,cpKey))>0) {
		ucOpt   = pCH->ch_opt;
		iCmpLen = pCH->ch_cmplen;
		pCt = pCH->ch_ppcache[iRc-1];
		if (pCH->ch_opt & AKX_CACHE_NOAUTOEXT) {
		/*	pCt->ca_chktime = */akxe_get_msec(NULL,&pCt->ca_tval);
/*
printf("akxsCacheCheck:Key=[%s] ChkTime=%d\n",pCt->ca_key,pCt->ca_chktime);
*/
		}
		iRc = 0;
		if (ucOpt & AKX_CACHE_MEMCMP) {	/* memory */
			if (memcmp(pCt->ca_cmp2,pCt->ca_cmp1,iCmpLen)) {
				iRc = 1;
				memcpy(pCt->ca_cmp2+iCmpLen,pCt->ca_cmp2,iCmpLen);
				memcpy(pCt->ca_cmp2,pCt->ca_cmp1,iCmpLen);
			}
		}
		else {	/* file */
			ichk = 1;
			if (pCH->ch_interval > 0) {
				t = akxe_get_msec(NULL,NULL);
				if (t >= pCt->ca_cmpnext) {
/*
printf("akxsCacheCheck:Key=[%s] t=%d\n",pCt->ca_key,t);
*/
					pCt->ca_cmpnext = t + pCH->ch_interval;
				}
				else ichk = 0;
			}
			if (ichk) {
				if (stat(pCt->ca_cmp1,&tStat)) {
					if (ucOpt & AKX_CACHE_IGNCHKERR) {
						tStat.st_mtime = tStat.st_size = 0;
					}
					else return -21;
				}
				tMod.my_mtime = tStat.st_mtime;
				tMod.st_size  = tStat.st_size;
/*
printf("akxsCacheCheck:my_mtime=%d st_size=%d\n",tMod.my_mtime,tMod.st_size);
*/
				if (memcmp(pCt->ca_cmp2,(char *)&tMod,iCmpLen)) {
					iRc = 1;
					memcpy(pCt->ca_cmp2+iCmpLen,pCt->ca_cmp2,iCmpLen);
					memcpy(pCt->ca_cmp2,(char *)&tMod,iCmpLen);
				}
/*
akxaxdump("cpCCmp2",pCt->ca_cmp2,iCmpLen*2);
*/
			}
		}
	/*
		pCt0->ca_data = pCt->ca_data;
		pCt0->ca_key  = pCt->ca_key;
		pCt0->ca_cmp1 = pCt->ca_cmp1;
		pCt0->ca_cmp2 = pCt->ca_cmp2;
	*/
		memcpy(pCt0,pCt,sizeof(tdtCacheCtl));
	}
	else if (!iRc) iRc = -22;
	return iRc;
}

/***************************************************/
/*  ret = 0 : 正常                                 */
/*      < 0 : エラー                               */
/***************************************************/
/*int akxs_cache_get(tdtCacheCtlHead *pCH,int iEntry,tdtCacheCtl *pCt0)*/
int akxs_cache_get(pCH, iEntry, pCt0)
tdtCacheCtlHead *pCH;
int iEntry;
tdtCacheCtl *pCt0;
{
	tdtCacheCtl **ppCt, *pCt;

	if (!pCH) return -1;
	if (!(ppCt = pCH->ch_ppcache)) return -2;
	if (!pCt0) return -25;
	if (iEntry<1 || iEntry>pCH->ch_smax) return -26;
	if (pCt=ppCt[iEntry-1]) {
		memcpy(pCt0,pCt,sizeof(tdtCacheCtl));
	}
	else {
		memset(pCt0,0,sizeof(tdtCacheCtl));
		return -22;
	}
	return 0;
}
