btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * alloc.c - memory allocation subsystem 
 */

/*
 * $Id: alloc.c,v 1.2 2005/08/08 09:43:05 murrayma Exp $ 
 */
#include "copyright.h"
#include "config.h"

#include "db.h"
#include "alloc.h"
#include "mudconf.h"
#include "externs.h"

typedef struct pool_header {
    int magicnum;		/*
				 * For consistency check 
				 */
    int pool_size;		/*
				 * For consistency check 
				 */
    struct pool_header *next;	/*
				 * Next pool header in chain 
				 */
    struct pool_header *nxtfree;	/*
					 * Next pool header in freelist 
					 */
    char *buf_tag;		/*
				 * Debugging/trace tag 
				 */
} POOLHDR;

typedef struct pool_footer {
    int magicnum;		/*
				 * For consistency check 
				 */
} POOLFTR;

typedef struct pooldata {
    int pool_size;		/*
				 * Size in bytes of a buffer 
				 */
    POOLHDR *free_head;		/*
				 * Buffer freelist head 
				 */
    POOLHDR *chain_head;	/*
				 * Buffer chain head 
				 */
    int tot_alloc;		/*
				 * Total buffers allocated 
				 */
    int num_alloc;		/*
				 * Number of buffers currently allocated 
				 */
    int max_alloc;		/*
				 * Max # buffers allocated at one time 
				 */
    int num_lost;		/*
				 * Buffers lost due to corruption 
				 */
} POOL;

POOL pools[NUM_POOLS];
const char *poolnames[] =
    { "Sbufs", "Mbufs", "Lbufs", "Bools", "Descs", "Qentries", "Pcaches" };

#define POOL_MAGICNUM 0xdeadbeef

void pool_init(poolnum, poolsize)
int poolnum, poolsize;
{
    pools[poolnum].pool_size = poolsize;
    pools[poolnum].free_head = NULL;
    pools[poolnum].chain_head = NULL;
    pools[poolnum].tot_alloc = 0;
    pools[poolnum].num_alloc = 0;
    pools[poolnum].max_alloc = 0;
    pools[poolnum].num_lost = 0;
    return;
}

static void pool_err(logsys, logflag, poolnum, tag, ph, action, reason)
int logflag, poolnum;
const char *logsys, *tag, *action, *reason;
POOLHDR *ph;
{
    if (!mudstate.logging) {
	STARTLOG(logflag, logsys, "ALLOC") {
	    sprintf(mudstate.buffer, "%s[%d] (tag %s) %s at %lx. (%s)",
		action, pools[poolnum].pool_size, tag, reason, (long) ph,
		mudstate.debug_cmd);
	    log_text(mudstate.buffer);
	    ENDLOG;
    }} else if (logflag != LOG_ALLOCATE) {
	sprintf(mudstate.buffer, "\n***< %s[%d] (tag %s) %s at %lx. >***",
	    action, pools[poolnum].pool_size, tag, reason, (long) ph);
	log_text(mudstate.buffer);
    }
}

static void pool_vfy(poolnum, tag)
int poolnum;
const char *tag;
{
    POOLHDR *ph, *lastph;
    POOLFTR *pf;
    char *h;
    int psize;

    lastph = NULL;
    psize = pools[poolnum].pool_size;
    for (ph = pools[poolnum].chain_head; ph; lastph = ph, ph = ph->next) {
	h = (char *) ph;
	h += sizeof(POOLHDR);
	h += pools[poolnum].pool_size;
	pf = (POOLFTR *) h;

	if (ph->magicnum != POOL_MAGICNUM) {
	    pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph, "Verify",
		"header corrupted (clearing freelist)");

	    /*
	     * Break the header chain at this point so we don't * 
	     * 
	     * *  * * generate an error for EVERY alloc and free, 
	     * * * also  * we can't continue the scan because the
	     * * next * * pointer might be trash too. 
	     */

	    if (lastph)
		lastph->next = NULL;
	    else
		pools[poolnum].chain_head = NULL;
	    return;		/*
				 * not safe to continue 
				 */
	}
	if (pf->magicnum != POOL_MAGICNUM) {
	    pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph, "Verify",
		"footer corrupted");
	    pf->magicnum = POOL_MAGICNUM;
	}
	if (ph->pool_size != psize) {
	    pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph, "Verify",
		"header has incorrect size");
	}
    }
}

void pool_check(tag)
const char *tag;
{
    pool_vfy(POOL_LBUF, tag);
    pool_vfy(POOL_MBUF, tag);
    pool_vfy(POOL_SBUF, tag);
    pool_vfy(POOL_BOOL, tag);
    pool_vfy(POOL_DESC, tag);
    pool_vfy(POOL_QENTRY, tag);
}

char *pool_alloc(poolnum, tag)
int poolnum;
const char *tag;
{
    int *p;
    char *h;
    POOLHDR *ph;
    POOLFTR *pf;

    if (mudconf.paranoid_alloc)
	pool_check(tag);
    do {
	if (pools[poolnum].free_head == NULL) {
	    h = (char *) malloc(pools[poolnum].pool_size +
		sizeof(POOLHDR) + sizeof(POOLFTR));
	    if (h == NULL)
		abort();
	    ph = (POOLHDR *) h;
	    h += sizeof(POOLHDR);
	    p = (int *) h;
	    h += pools[poolnum].pool_size;
	    pf = (POOLFTR *) h;
	    ph->next = pools[poolnum].chain_head;
	    ph->nxtfree = NULL;
	    ph->magicnum = POOL_MAGICNUM;
	    ph->pool_size = pools[poolnum].pool_size;
	    pf->magicnum = POOL_MAGICNUM;
	    *p = POOL_MAGICNUM;
	    pools[poolnum].chain_head = ph;
	    pools[poolnum].max_alloc++;
	} else {
	    ph = (POOLHDR *) (pools[poolnum].free_head);
	    h = (char *) ph;
	    h += sizeof(POOLHDR);
	    p = (int *) h;
	    h += pools[poolnum].pool_size;
	    pf = (POOLFTR *) h;
	    pools[poolnum].free_head = ph->nxtfree;

	    /*
	     * If corrupted header we need to throw away the * *
	     * * freelist as the freelist pointer may be corrupt. 
	     */

	    if (ph->magicnum != POOL_MAGICNUM) {
		pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph, "Alloc",
		    "corrupted buffer header");

		/*
		 * Start a new free list and record stats 
		 */

		p = NULL;
		pools[poolnum].free_head = NULL;
		pools[poolnum].num_lost +=
		    (pools[poolnum].tot_alloc - pools[poolnum].num_alloc);
		pools[poolnum].tot_alloc = pools[poolnum].num_alloc;
	    }
	    /*
	     * Check for corrupted footer, just report and * fix
	     * * * it 
	     */

	    if (pf->magicnum != POOL_MAGICNUM) {
		pool_err("BUG", LOG_ALWAYS, poolnum, tag, ph, "Alloc",
		    "corrupted buffer footer");
		pf->magicnum = POOL_MAGICNUM;
	    }
	}
    } while (p == NULL);

    ph->buf_tag = (char *) tag;
    pools[poolnum].tot_alloc++;
    pools[poolnum].num_alloc++;

    pool_err("DBG", LOG_ALLOCATE, poolnum, tag, ph, "Alloc", "buffer");

    /*
     * If the buffer was modified after it was last freed, log it. 
     */

    if ((*p != POOL_MAGICNUM) && (!mudstate.logging)) {
	pool_err("BUG", LOG_PROBLEMS, poolnum, tag, ph, "Alloc",
	    "buffer modified after free");
    }
    *p = 0;
    return (char *) p;
}

void pool_free(poolnum, buf)
int poolnum;
char **buf;
{
    int *ibuf;
    char *h;
    POOLHDR *ph;
    POOLFTR *pf;

    ibuf = (int *) *buf;
    h = (char *) ibuf;
    h -= sizeof(POOLHDR);
    ph = (POOLHDR *) h;
    h = (char *) ibuf;
    h += pools[poolnum].pool_size;
    pf = (POOLFTR *) h;
    if (mudconf.paranoid_alloc)
	pool_check(ph->buf_tag);

    /*
     * Make sure the buffer header is good.  If it isn't, log the error * 
     * 
     * * and * throw away the buffer. 
     */

    if (ph->magicnum != POOL_MAGICNUM) {
	pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
	    "corrupted buffer header");
	pools[poolnum].num_lost++;
	pools[poolnum].num_alloc--;
	pools[poolnum].tot_alloc--;
	return;
    }
    /*
     * Verify the buffer footer.  Don't unlink if damaged, just repair 
     */

    if (pf->magicnum != POOL_MAGICNUM) {
	pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
	    "corrupted buffer footer");
	pf->magicnum = POOL_MAGICNUM;
    }
    /*
     * Verify that we are not trying to free someone else's buffer 
     */

    if (ph->pool_size != pools[poolnum].pool_size) {
	pool_err("BUG", LOG_ALWAYS, poolnum, ph->buf_tag, ph, "Free",
	    "Attempt to free into a different pool.");
	return;
    }
    pool_err("DBG", LOG_ALLOCATE, poolnum, ph->buf_tag, ph, "Free",
	"buffer");

    /*
     * Make sure we aren't freeing an already free buffer.  If we are, *
     * * * log an error, otherwise update the pool header and stats  
     */

    if (*ibuf == POOL_MAGICNUM) {
	pool_err("BUG", LOG_BUGS, poolnum, ph->buf_tag, ph, "Free",
	    "buffer already freed");
    } else {
	*ibuf = POOL_MAGICNUM;
	ph->nxtfree = pools[poolnum].free_head;
	pools[poolnum].free_head = ph;
	pools[poolnum].num_alloc--;
    }
}

static char *pool_stats(poolnum, text)
int poolnum;
const char *text;
{
    char *buf;

    buf = alloc_mbuf("pool_stats");
    sprintf(buf, "%-15s %5d%9d%9d%9d%9d", text, pools[poolnum].pool_size,
	pools[poolnum].num_alloc, pools[poolnum].max_alloc,
	pools[poolnum].tot_alloc, pools[poolnum].num_lost);
    return buf;
}

static void pool_trace(player, poolnum, text)
dbref player;
int poolnum;
const char *text;
{
    POOLHDR *ph;
    int numfree, *ibuf;
    char *h;

    numfree = 0;
    notify(player, tprintf("----- %s -----", text));
    for (ph = pools[poolnum].chain_head; ph != NULL; ph = ph->next) {
	if (ph->magicnum != POOL_MAGICNUM) {
	    notify(player,
		"*** CORRUPTED BUFFER HEADER, ABORTING SCAN ***");
	    notify(player, tprintf("%d free %s (before corruption)",
		    numfree, text));
	    return;
	}
	h = (char *) ph;
	h += sizeof(POOLHDR);
	ibuf = (int *) h;
	if (*ibuf != POOL_MAGICNUM)
	    notify(player, ph->buf_tag);
	else
	    numfree++;
    }
    notify(player, tprintf("%d free %s", numfree, text));
}

static void list_bufstat(player, poolnum, pool_name)
dbref player;
int poolnum;
const char *pool_name;
{
    char *buff;

    buff = pool_stats(poolnum, poolnames[poolnum]);
    notify(player, buff);
    free_mbuf(buff);
}

void list_bufstats(player)
dbref player;
{
    int i;

    notify(player,
	"Buffer Stats     Size    InUse    Total   Allocs     Lost");
    for (i = 0; i < NUM_POOLS; i++)
	list_bufstat(player, i, poolnames[i]);
}

void list_buftrace(player)
dbref player;
{
    int i;

    for (i = 0; i < NUM_POOLS; i++)
	pool_trace(player, i, poolnames[i]);
}

void pool_reset()
{
    POOLHDR *ph, *phnext, *newchain;
    int i, *ibuf;
    char *h;


    for (i = 0; i < NUM_POOLS; i++) {
	newchain = NULL;
	for (ph = pools[i].chain_head; ph != NULL; ph = phnext) {
	    h = (char *) ph;
	    phnext = ph->next;
	    h += sizeof(POOLHDR);
	    ibuf = (int *) h;
	    if (*ibuf == POOL_MAGICNUM) {
		free(ph);
	    } else {
		if (!newchain) {
		    newchain = ph;
		    ph->next = NULL;
		} else {
		    ph->next = newchain;
		    newchain = ph;
		}
		ph->nxtfree = NULL;
	    }
	}
	pools[i].chain_head = newchain;
	pools[i].free_head = NULL;
	pools[i].max_alloc = pools[i].num_alloc;
    }
}