untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
    Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

/* configure all options BEFORE including system stuff. */
#include    "config.h"
#include    "mud.h"

/*
This is by far the most complex and kinky code in UnterMUD. You should
never need to mess with anything in here - if you value your sanity.
*/


typedef struct cache {
  char *onm;
  Obj *op;
  int flg;
  struct cache *nxt;
  struct cache *prv;
} Cache;


/* flag states */
#define C_NOFLG 00
#define C_DIRTY 01
#define C_DEAD  02


typedef struct {
  Cache *ahead;
  Cache *ohead;
  Cache *atail;
  Cache *otail;
} CacheLst;

/* initial settings for cache sizes */
static int cwidth = CACHE_WIDTH;
static int cdepth = CACHE_DEPTH;


/* ntbfw - main cache pointer and list of things to kill off */
static CacheLst *sys_c;

static int cache_initted = 0;

/* cache stats gathering stuff. you don't like it? comment it out */
static time_t cs_ltime;
static int cs_writes = 0;       /* total writes */
static int cs_reads = 0;        /* total reads */
static int cs_dbreads = 0;      /* total read-throughs */
static int cs_dbwrites = 0;     /* total write-throughs */
static int cs_dels = 0;         /* total deletes */
static int cs_checks = 0;       /* total checks */
static int cs_rhits = 0;        /* total reads filled from cache */
static int cs_ahits = 0;        /* total reads filled active cache */
static int cs_whits = 0;        /* total writes to dirty cache */
static int cs_fails = 0;        /* attempts to grab nonexistent */
static int cs_resets = 0;       /* total cache resets */
static int cs_syncs = 0;        /* total cache syncs */
static int cs_objects = 0;      /* total cache size */



int cache_init ()
{
  int x;
  int y;
  Cache *np;
  static char *ncmsg = "cache_init: cannot allocate cache: ";

  if (cache_initted || sys_c != (CacheLst *) 0)
    return (0);

  sys_c = (CacheLst *) malloc ((unsigned) cwidth * sizeof (CacheLst));
  if (sys_c == (CacheLst *) 0) {
    fprintf (stderr, "dbchk: cache malloc failure\n");
    return (-1);
  }

  for (x = 0; x < cwidth; x++) {
    sys_c[x].ahead = sys_c[x].ohead = (Cache *) 0;
    sys_c[x].atail = sys_c[x].otail = (Cache *) 0;

    for (y = 0; y < cdepth; y++) {

      np = (Cache *) malloc (sizeof (Cache));
      if (np == (Cache *) 0) {
        fprintf (stderr, "%s\n", ncmsg);
        return (-1);
      }

      if ((np->nxt = sys_c[x].ohead) != (Cache *) 0)
        np->nxt->prv = np;
      else
        sys_c[x].otail = np;

      np->prv = (Cache *) 0;
      np->flg = C_NOFLG;
      np->onm = (char *) 0;
      np->op = (Obj *) 0;

      sys_c[x].ohead = np;
      cs_objects++;
    }
  }

  /* mark caching system live */
  cache_initted++;

  time (&cs_ltime);

  return (0);
}





void cache_reset ()
{
  int x;

  if (!cache_initted)
    return;

  /* unchain and rechain each active list at head of old chain */
  for (x = 0; x < cwidth; x++) {
    if (sys_c[x].ahead != (Cache *) 0) {
      assert (sys_c[x].otail || !sys_c[x].ohead);
      sys_c[x].atail->nxt = sys_c[x].ohead;

      if (sys_c[x].ohead != (Cache *) 0)
        sys_c[x].ohead->prv = sys_c[x].atail;

      sys_c[x].ohead = sys_c[x].ahead;
      if (sys_c[x].otail == (Cache *) 0)
        sys_c[x].otail = sys_c[x].atail;
      sys_c[x].ahead = (Cache *) 0;
      sys_c[x].atail = (Cache *) 0;
    }
  }
  cs_resets++;
}

/*
search the cache for an object, and if it is not found, thaw it.
this code is probably a little bigger than it needs be because I
had fun and unrolled all the pointer juggling inline.
*/
Obj *cache_get (nam)
char *nam;
{
  Cache *cp;
  int hv = 0;
  Obj *ret;

  /* firewall */
  if (nam == (char *) 0 || *nam == '\0' || !cache_initted) {
#ifdef  CACHE_VERBOSE
    logf ("cache_get: NULL object name - programmer error\n", (char *) 0);
#endif
    abort ();
    return ((Obj *) 0);
  }
#ifdef  CACHE_DEBUG
  printf ("get %s\n", nam);
#endif

  /* is some fool trying to get "nowhere"? */
  if (!strcmp (nam, "nowhere"))
    return ((Obj *) 0);

  cs_reads++;

  hv = objid_hash (nam, cwidth);

  /* search active chain first */
  for (cp = sys_c[hv].ahead; cp != (Cache *) 0; cp = cp->nxt) {
    if (cp->onm != (char *) 0 && !(cp->flg & C_DEAD) &&
      !strcmp (cp->onm, nam)) {
      cs_rhits++;
      cs_ahits++;
#ifdef  CACHE_DEBUG
      printf ("return %s active cache %d\n", cp->onm, cp->op);
#endif
      return (cp->op);
    }
  }

  /* search in-active chain second */
  for (cp = sys_c[hv].ohead; cp != (Cache *) 0; cp = cp->nxt) {
    if (cp->onm != (char *) 0 && !(cp->flg & C_DEAD) &&
      !strcmp (cp->onm, nam)) {

      /* dechain from in-active chain */
      if (cp->nxt == (Cache *) 0)
        sys_c[hv].otail = cp->prv;
      else
        cp->nxt->prv = cp->prv;
      if (cp->prv == (Cache *) 0)
        sys_c[hv].ohead = cp->nxt;
      else
        cp->prv->nxt = cp->nxt;

      /* insert at head of active chain */
      cp->nxt = sys_c[hv].ahead;
      if (sys_c[hv].ahead == (Cache *) 0)
        sys_c[hv].atail = cp;
      cp->prv = (Cache *) 0;
      if (cp->nxt != (Cache *) 0)
        cp->nxt->prv = cp;
      sys_c[hv].ahead = cp;

      /* done */
      cs_rhits++;
#ifdef  CACHE_DEBUG
      printf ("return %s old cache %d\n", cp->onm, cp->op);
#endif
      return (cp->op);
    }
  }

  /* DARN IT - at this point we have a certified, type-A cache miss */

  /* thaw the object from wherever. */
  if ((ret = DB_GET (nam)) == (Obj *) 0) {
    cs_fails++;
#ifdef  CACHE_DEBUG
    printf ("%s not in db\n", nam);
#endif
    return ((Obj *) 0);
  }
  cs_dbreads++;

  /* if there are no old cache object holders left, allocate one */
  if (sys_c[hv].otail == (Cache *) 0) {
    int xnm;

    assert (sys_c[hv].ohead == (Cache *) 0);

    if ((cp = (Cache *) malloc (sizeof (Cache))) == (Cache *) 0) {
      fprintf (stderr, "dbchk: no memory for new cache struct\n");
      abort ();
    }

    xnm = strlen (nam) + 1;
    if ((cp->onm = (char *) malloc ((unsigned) xnm)) == (char *) 0) {
      fprintf (stderr, "dbchk: no memory for new cache name\n");
      abort ();
    }

    /* sanity chex */
    if (xnm > MAXOID)
      fprintf (stderr, "dbchk: cache_get: name %s too long!\n", nam);

    (void) strcpy (cp->onm, nam);
    cp->flg = C_NOFLG;

    /* linkit at head of active chain */
    cp->nxt = sys_c[hv].ahead;
    if (sys_c[hv].ahead == (Cache *) 0)
      sys_c[hv].atail = cp;
    cp->prv = (Cache *) 0;
    if (cp->nxt != (Cache *) 0)
      cp->nxt->prv = cp;
    sys_c[hv].ahead = cp;
    cs_objects++;
#ifdef  CACHE_DEBUG
    printf ("return %s loaded into cache %d\n", cp->onm, cp->op);
#endif
    return (cp->op = ret);
  }

  /* unlink old cache chain tail */
  cp = sys_c[hv].otail;
  if (cp->prv != (Cache *) 0) {
    sys_c[hv].otail = cp->prv;
    cp->prv->nxt = cp->nxt;
  } else                        /* took last one */
    sys_c[hv].ohead = sys_c[hv].otail = (Cache *) 0;

  /* if there is a dirty object still in memory, write it */
  if ((cp->flg & C_DIRTY) && cp->onm != (char *) 0 && cp->op != (Obj *) 0) {
#ifdef  CACHE_DEBUG
    printf ("clean %s from cache %d\n", cp->onm, cp->op);
#endif
    fprintf (stderr, "dbchk: dirty object in cache to be flushed.\n");
    abort ();
    cs_dbwrites++;
  }

  /* free object's data */
  if (cp->op != (Obj *) 0)
    objfree (cp->op);
  cp->op = ret;

  /* free old name */
  if (cp->onm != (char *) 0)
    deferfree ((mall_t) cp->onm);

  /* if this fails, boy are we in trouble! */
  if ((cp->onm = (char *) malloc ((unsigned) strlen (nam) + 1)) == (char *) 0) {
    fprintf (stderr, "dbchk: death point 1\n");
    abort ();
  }

  /* sanity chex */
  if (strlen (nam) + 1 > MAXOID)
    fprintf (stderr, "dbchk: cache_get: name %s too long!\n", nam);

  (void) strcpy (cp->onm, nam);
  cp->flg = C_NOFLG;

  /* relink at head of active chain */
  cp->nxt = sys_c[hv].ahead;
  if (sys_c[hv].ahead == (Cache *) 0)
    sys_c[hv].atail = cp;
  cp->prv = (Cache *) 0;
  if (cp->nxt != (Cache *) 0)
    cp->nxt->prv = cp;
  sys_c[hv].ahead = cp;

#ifdef  CACHE_DEBUG
  printf ("return %s loaded into cache %d\n", cp->onm, cp->op);
#endif
  return (ret);
}

int cache_sync ()
{
  int x;
  Cache *cp;

  cs_syncs++;

  if (!cache_initted)
    return (1);

  for (x = 0; x < cwidth; x++) {
    for (cp = sys_c[x].ahead; cp != (Cache *) 0; cp = cp->nxt) {
      if (cp->flg & C_DIRTY) {
        fprintf (stderr, "dbchk: panic: dirty cache\n");
        abort ();
      }
    }
    for (cp = sys_c[x].ohead; cp != (Cache *) 0; cp = cp->nxt) {
      if (cp->flg & C_DIRTY) {
        fprintf (stderr, "dbchk: panic: dirty cache\n");
        abort ();
      }
    }
  }
  return (0);
}




/*
probe the cache and the database (if needed) for the existence of an
object. return nonzero if the object is in cache or database
*/
int cache_check (nam)
char *nam;
{
  Cache *cp;
  int hv = 0;

  if (nam == (char *) 0 || !cache_initted)
    return (0);

  cs_checks++;

  hv = objid_hash (nam, cwidth);

  for (cp = sys_c[hv].ahead; cp != (Cache *) 0; cp = cp->nxt)
    if (cp->onm != (char *) 0 && !(cp->flg & C_DEAD) &&
      !strcmp (cp->onm, nam))
      return (1);

  for (cp = sys_c[hv].ohead; cp != (Cache *) 0; cp = cp->nxt)
    if (cp->onm != (char *) 0 && !(cp->flg & C_DEAD) &&
      !strcmp (cp->onm, nam))
      return (1);

  /* no ? */
  return (DB_CHECK (nam));
}

void cache_stats ()
{
  printf ("cache stats, last %d sec.:\n", (int) (time (0) - cs_ltime));
  printf ("writes=%d reads=%d\n", cs_writes, cs_reads);
  printf ("dbwrites=%d dbreads=%d\n", cs_dbwrites, cs_dbreads);
  printf ("read hits=%d active hits=%d\n", cs_rhits, cs_ahits);
  printf ("write hits to dirty cache=%d\n", cs_whits);
  printf ("deletes=%d checks=%d\n", cs_dels, cs_checks);
  printf ("resets=%d syncs=%d objects=%d\n", cs_resets, cs_syncs, cs_objects);
}