/*
 * DEXPIRESCORING.C    - dreaderd cache scoring expire.
 *
 * (c)Copyright 2002, Francois Petillon, All Rights Reserved.  Refer to
 *    the COPYRIGHT file in the base directory of this distribution 
 *    for specific rights granted.
 *
 */

#include "defs.h"
#include <math.h>

int CacheHitsFD=-1;
void *CacheHits;

void Usage(char *progname)
{
    fprintf(stderr, "Expire the cache scoring\n");
    fprintf(stderr, "dexpirescoring [-H halflife]\n");
    exit(1);
}

char *
allocTmpCopy(const char *buf, int bufLen)
{
    static char *SaveAry[8];
    static int SaveCnt;
    char **pptr;

    SaveCnt = (SaveCnt + 1) % arysize(SaveAry);
    pptr = &SaveAry[SaveCnt];
    if (*pptr)
	free(*pptr);
    *pptr = malloc(bufLen + 1);
    memcpy(*pptr, buf, bufLen);
    (*pptr)[bufLen] = 0;
    return(*pptr);
}

struct CacheHitEntry*
FindCacheHitEntry(struct CacheHash_t *ch) {
    uint32 *i=NULL;
    struct CacheHitEntry *che=NULL;
    struct CacheHitHead *chh=(struct CacheHitHead *)CacheHits;
    i = (uint32*) (CacheHits+sizeof(struct CacheHitHead)+((ch->h1^ch->h2)%chh->chh_hashSize)*sizeof(uint32));

    while (*i != 0) {
	che = (struct CacheHitEntry*) (CacheHits + (*i));
	if (memcmp(ch, &(che->che_hash), sizeof(struct CacheHash_t))==0) {
	    return che;
	}
	i = &(che->che_Next);
    }
    return NULL;
}

int
main(int ac, char **av) {
    struct CacheHitHead chh;
    double expire=0;
    int i,r,halflife=0;
    int verbose=0;

    LoadDiabloConfig(ac, av);

    CacheHitsFD = open(PatDbExpand(CacheHitsPat), O_RDWR, 0644);
    if (CacheHitsFD<0) {
	fprintf(stderr, "Can not open cache hits file (%s)\n", PatDbExpand(CacheHitsPat));
	exit(1);
    }
    hflock(CacheHitsFD, 0, XLOCK_EX);
    r = read(CacheHitsFD, &chh, sizeof(struct CacheHitHead));
    if ( (r < sizeof(struct CacheHitHead)) || (chh.chh_magic != CHMAGIC) || (chh.chh_version != CHVERSION) ) {
	fprintf(stderr, "Wrong version, can not expire scoring\n");
	hflock(CacheHitsFD, 0, XLOCK_UN);
	exit(1);
    }
    CacheHits = mmap(NULL, chh.chh_end, PROT_READ|PROT_WRITE, MAP_SHARED, CacheHitsFD, 0);
    if (CacheHits == NULL) {
	fprintf(stderr, "Error on cache hits mmap (%s)", strerror(errno));
	hflock(CacheHitsFD, 0, XLOCK_UN);
	close(CacheHitsFD);
	exit(1);
    }

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];
	if (*ptr != '-') {
	    fprintf(stderr, "Unexpected argument: %s\n", ptr);
	    Usage(av[0]);
	}
	ptr += 2;
	switch(ptr[-1]) {
	case 'H':
	    halflife = strtol((*ptr) ? ptr : av[++i], NULL, 0);
	    break;
	case 'v':
	    verbose++;
	    break;
	default:
	    fprintf(stderr, "Unknown option: %s\n", ptr - 2);
	    Usage(av[0]);
	}
    }

    if (halflife>0) {
	time_t delay, now;
	struct CacheHitHead *chh = (struct CacheHitHead *) CacheHits;

	now = time(NULL);
	delay = now - chh->chh_lastExpired;
	chh->chh_lastExpired = now;
	expire = pow(2, ((double)delay/halflife));

	if (verbose) {
	    printf("delay : %i (%f)\n", (int)delay, expire);
	}
    }

    {
	KPDB  *KDBActive;
	int recOff, recLen;
	KDBActive = KPDBOpen(PatDbExpand(ReaderDActivePat), O_RDWR);
	if (KDBActive == NULL) {
	    fprintf(stderr, "Unable to open dactive.kp\n");
	    exit(1);
	}
	for (recOff = KPDBScanFirst(KDBActive, 0, &recLen);
		recOff;
		recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen)
	) {
	    int groupLen;
	    struct CacheHash_t ch;
	    struct CacheHitEntry *che;
	    const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL);
	    const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL);
	    int endNo = strtol(KPDBGetField(rec, recLen, "NE", NULL, "-1"), NULL, 10);
	    int iter = (int)strtoul(KPDBGetField(rec, recLen, "ITER", NULL, "0"), NULL, 16);
	    
	    if (group)
		group = allocTmpCopy(group, groupLen);

	    SetCacheHash(&ch, group, iter, &DOpts.ReaderGroupHashMethod);
	    che = FindCacheHitEntry(&ch);
	    if (che != NULL) {
		int new;
		new = che->che_NewArt+endNo-che->che_LastHi;
		che->che_LastHi = endNo;
		if (expire>1) {
		    che->che_ReadArt = (int)((float)che->che_ReadArt/expire);
		    che->che_Hits = (int)((float)che->che_Hits/expire);
		    che->che_NewArt = (int)((float)new/expire);
		    new = che->che_NewArt+endNo-che->che_LastHi;
		} else {
		    che->che_NewArt = new;
		}
		if (verbose) {
		    if (new<1) new=1;
		    if (che->che_Hits) {
			printf("read:%7i (hits:%6.2f%%) new:%6i (r/n=%6.2f) %s\n", che->che_ReadArt+che->che_Hits, 100*(float)che->che_Hits/(che->che_ReadArt+che->che_Hits), new, ((float)(che->che_ReadArt+che->che_Hits)/new), group);
		    } else {
			printf("read:%7i                new:%6i (r/n=%6.2f) %s\n", che->che_ReadArt+che->che_Hits, new, ((float)che->che_ReadArt/new), group);
		    }
		}
	    }
	}
    }

    
    hflock(CacheHitsFD, 0, XLOCK_UN);
    close(CacheHitsFD);
    exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1