CAP/applications/aufs/afpgc.c

252 lines
6.5 KiB
C

/*
* $Author: djh $ $Date: 91/02/15 21:07:18 $
* $Header: afpgc.c,v 2.1 91/02/15 21:07:18 djh Rel $
* $Revision: 2.1 $
*/
/*
* afpgc.c - Appletalk Filing Protocol General Cache Manager
*
* AppleTalk package for UNIX (4.2 BSD).
*
* Copyright (c) 1986, 1987 by The Trustees of Columbia University in the
* City of New York.
*
* Edit History:
*
*
* Apr 3, 1987 Schilit Created.
*
*/
/*
* General Cache Routines.
*
* These routines may be used for building a cache based on LRU
* replacement.
*
* The caller controls the cache by supplying routines for loading,
* purging, comparing, and validating entries.
*
* The cache is initialized and the size is defined by a call to
* GCNew, all the other work is performed by the lookup routine,
* GCLocate. See the comments on those routines for calling
* conventions.
*
*/
/*
* GCNew - create a new cache
* GCScan - locate cache entry
* GCLocate - locate cache entry, loading if necessary
* GCFlush - flush cache
* GCGet - get cache entry
* GCAdd - add cache entry
*/
#include "afpgc.h" /* include defs for cache */
#define TRUE 1
#define FALSE 0
/*
* GCHandle *GCNew(size,int (*valid)(), int (*comp)(),
* char *(*load)(), void (*purge)());
*
* Create a new cache of size 'size' and define the interface routines
* for validating, comparing, loading, and purging entries.
*
* int valid(ce) - returns TRUE if entry is valid.
* int comp(ce,key) - compare and return 0 if equal.
* char *load(key) - load an entry (allocate stg and/or read from disk).
* void purge(ce) - purge an entry (release stg & write to disk).
* void flush(ce) - flush entry (write to disk)
*
*
* The datatype of a cache entry is defined by the *result* of load().
* This datatype is stored in the cache and passed to purge(), valid()
* and as the first argument to comp().
*
* A second datatype may be defined for the lookup key. This is the
* argument to GCLocate and is sent to comp() to compare against an
* existing cache entry. Load() accepts a key and returns the cache
* entry for that key.
*
*/
GCHandle *
GCNew(size,cvalid,ccomp,cload,cpurge,cflush)
int size;
int (*cvalid)();
int (*ccomp)();
char *(*cload)();
void (*cpurge)();
void (*cflush)();
{
GCHandle *gch;
int i;
gch = (GCHandle *) malloc(sizeof(GCHandle));
gch->gch_clock = 0;
gch->gch_size = size;
gch->gch_valid = cvalid; /* validation routine */
gch->gch_comp = ccomp; /* compare routine */
gch->gch_load = cload; /* loading routine */
gch->gch_purge = cpurge; /* release routine */
gch->gch_flush = cflush; /* write or update */
gch->gch_lru = (int *) malloc(sizeof(int)*size);
gch->gch_ents = (GCUData **) malloc(sizeof(GCUData *) * (size));
for (i=0; i < size; i++)
gch->gch_lru[i] = -1;
return(gch);
}
/*
* boolean GCScan(GCHandle *gch, GCUData *key, int *idx)
*
* Scan the cache (gch) looking for key.
*
* Returns TRUE if the entry was found in the cache, idx is the index
* of the entry.
*
* Returns FALSE if no entry was found in the cache, idx is a free
* entry (may have called user's purge).
*
*/
/* private */ int
GCScan(gch, key, idx)
GCHandle *gch;
GCUData *key;
int *idx;
{
register GCUData **ent = gch->gch_ents;
register int *lru = gch->gch_lru;
register int i;
register int mi = 0;
register int mf = -1;
for (i=0; i < gch->gch_size; i++) { /* scan cache for entry and min */
if (lru[i] < 0) /* entry in cache? */
mf = i; /* no, remember free entry */
else {
if ((*gch->gch_comp)(ent[i],key)) /* compare */
if ((*gch->gch_valid)(ent[i])) { /* match, see if entry is valid */
lru[i] = gch->gch_clock++; /* found matching valid entry */
*idx = i; /* here is the cache index */
return(TRUE); /* aready in cache return TRUE */
} else {
mf = i; /* invalid and matching, so reuse it */
break; /* break out of for loop */
}
if (lru[i] < lru[mi]) /* no match, check for min entry */
mi = i; /* if so remember */
}
}
/* Miss. cache scan is over without locating the desired entry. */
/* if we did not find a free entry then free the min lru found */
/* and load the cache with the desired entry. */
if (mf < 0) { /* did we find a free entry */
(*gch->gch_purge)(ent[mi]); /* no, free the min entry */
mf = mi; /* now here is a slot */
}
*idx = mf; /* set index */
return(FALSE);
}
/*
* char *GCLocate(GCHandle *gch, char *key)
*
* Locate the entry matching 'key' in the cache 'gch' by calling
* the comparision routine comp() for each cache entry.
*
* If no entry is found, or the matching entry fails the user
* specified valid() check, then add a new cache entry by calling
* the load() procedure.
*
* If the cache is full, then replacement is required and the
* user specified purge() is called to release the LRU entry.
*
*/
char *
GCLocate(gch,key)
GCHandle *gch;
char *key;
{
int idx;
if (GCScan(gch,key,&idx)) /* scan for entry */
return(gch->gch_ents[idx]); /* found it, so return */
gch->gch_lru[idx] = gch->gch_clock++; /* else free entry, set clock */
gch->gch_ents[idx] = (*gch->gch_load)(key); /* load it in */
return(gch->gch_ents[idx]); /* and return it */
}
/*
* flush is called when OSFlush gets called -- could and should be
* done without releasing cache entries by using user defined flush
* routine: in other words, it is a prime opportunity to scan for "bad"
* items
*
* allows passing of userdata (single long)
*
*/
void
GCFlush(gch, udata)
GCHandle *gch;
unsigned long udata;
{
int i;
char **ent = gch->gch_ents;
int *lru = gch->gch_lru;
for (i=0; i < gch->gch_size; i++) /* scan cache for entry and min */
if (lru[i] >= 0) /* entry in cache? */
if ((*gch->gch_valid)(ent[i])) /* yes... entry valid? */
(*gch->gch_flush)(ent[i], udata); /* yes.. then flush */
}
/*
* GCUData *GCGet(GCHandle *gch, int idx)
*
* Return the cache entry at index idx.
*
*/
GCUData *
GCGet(gch,idx)
GCHandle *gch;
int idx;
{
return(gch->gch_ents[idx]);
}
/*
* int GCAdd(GCHandle *gch, GCUData *udata)
*
* Add the entry udata to the cache. Returns cache index.
*
* Adding an entry may cause user's flush routine to be called
* if the cache is full.
*
*/
int
GCAdd(gch, udata)
GCHandle *gch;
GCUData *udata;
{
int idx;
if (GCScan(gch,udata,&idx)) /* scan for entry in cache */
return(idx); /* found it, oh well.. */
gch->gch_lru[idx] = gch->gch_clock++; /* else free entry, set clock */
gch->gch_ents[idx] = udata; /* store entry */
return(idx); /* and return index */
}