game/bin/
game/data/
/*
 * alloc.c - memory allocation subsystem 
 */
/*
 * $Id: alloc.c,v 1.3 1995/11/22 23:33:17 root Exp $ 
 */
#include "copyright.h"
#include "autoconf.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,
				(int)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,
			(int)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]);
}