
#ifdef MSDOS
/* memory allocation routines
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Adapted from alloc routine in K&R; memory statistics and interrupt
 * protection added for use with net package. Must be used in place of
 * standard Turbo-C library routines because the latter check for stack/heap
 * collisions. This causes erroneous failures because process stacks are
 * allocated off the heap.
 *
 * Mods by G1EMM , PA0GRI, KO4KS
 */

#define __dj_include_stdlib_h_
#include "global.h"
#define FP_OFF( fp )( (unsigned )( fp ))
#include "proc.h"
#include "socket.h"
#include "commands.h"
#include <dpmi.h>

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: alloc.c,v 1.20 1997/07/31 00:44:20 root Exp $";
#endif

static unsigned long Memfail = 0;	/* Count of allocation failures */
static unsigned long Allocs = 0;	/* Total allocations */
static unsigned long Frees = 0;		/* Total frees */
static unsigned long Invalid = 0;	/* Total calls to free with garbage arg */
static unsigned long Yellows = 0;	/* Yellow alert garbage collections */
static unsigned long Reds = 0;		/* Red alert garbage collections */
static int Memwait;			/* Number of tasks waiting for memory */
static unsigned long Availmem = 0;	/* Heap memory, ABLKSIZE units */
static unsigned long Morecores = 0;
static int Memdebug = 0;		/* 0 = normal, 1 = call logstat() */
static char freewarn[] ="free: WARNING! %s (%08x) pc = %04x:%04x proc %s%c";
static char freewarn1[] = "invalid pointer";
static char HeapSizeStr[] = "heap size %lu, avail %lu (%lu%%), morecores %lu";
#ifdef Kelvdebug
static unsigned long Overuse = 0;	/* Total calls to free with overused arg */
static char freewarn2[] = "overused buffer";
static char AllocStr[] = "allocs %lu, frees %lu (diff %lu), alloc fails %lu, invalid frees %lu, overused %lu";
#else
static char AllocStr[] = "allocs %lu, frees %lu (diff %lu), alloc fails %lu, invalid frees %lu";
#endif
static char GarbageStr[] = "garbage collections yellow %lu, red %lu";
static char ThreshStr[] = "threshold %lu";

static unsigned long Sizes[16] = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L,
				  0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L };

static int logstat (void);

static unsigned long availmem (void);
int domemstat (int argc,char *argv[],void *p);
static int dofreelist (int argc,char *argv[],void *p);
static int dothresh (int argc,char *argv[],void *p);
static int dosizes (int argc,char *argv[],void *p);
static int domemdebug (int argc,char *argv[],void *p);
static int dogcollect (int argc,char *argv[],void *p);

static struct cmds Memcmds[] = {
	{ "debug",	domemdebug,	0, 0, NULLCHAR },
	{ "freelist",	dofreelist,	0, 0, NULLCHAR },
	{ "gcollect",	dogcollect,	0, 0, NULLCHAR },
	{ "sizes",	dosizes,	0, 0, NULLCHAR },
	{ "status",	domemstat,	0, 0, NULLCHAR },
	{ "thresh",	dothresh,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


struct header {
	struct header *ptr;
	unsigned long size;
};

typedef struct header HEADER;
#define	NULLHDR	(HEADER *)NULL

#define	ABLKSIZE	(sizeof (HEADER))

static HEADER *morecore (unsigned nu);

static HEADER Base;
static HEADER *Allocp = NULLHDR;
static unsigned long Heapsize = 0;

#ifdef Kelvdebug
#define MARKER		0x766c654bL /* Kelv in reverse */
#endif


static void *_malloc (unsigned nb);

#if 0
/* Allocate block of 'nb' bytes */
void *
malloc(nb)
unsigned nb;
{
void * retval;

	retval = _malloc (nb);
	return (retval);
}
#else
/* Allocate block of 'nb' bytes */
void *
malloc(nb)
unsigned nb;
{
	return mallocw(nb);
}
#endif

/* Allocate block of 'nb' bytes */
static void *
_malloc(nb)
unsigned nb;
{
	register HEADER *p, *q;
	register unsigned nu;
	int i;

	if(nb == 0)
		return NULL;

	/* Record the size of this request */
	if((i = log2((int16) nb)) >= 0)
		Sizes[i]++;
	
#ifndef Kelvdebug
 	/* Round up to full block, then add one for header */

 	nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 2;	/* force allocated memory  */
	nu &= 0xfffffffeL;				/* to be on offset 0x0008 */
#else
 	/* Round up to full block, then add one for header and one for debug */

 	nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 4;	/* force allocated memory  */
	nu &= 0xfffffffeL;				/* to be on offset 0x0008 */
#endif

	if ((q = Allocp) == NULLHDR){
		Base.ptr = Allocp = q = &Base;
		Base.size = 1;
	}

	for (p = q->ptr; ; q = p, p = p->ptr){
		if (p->size >= nu){
			/* This chunk is at least as large as we need */
			if (p->size <= nu + 1){
				/* This is either a perfect fit (size == nu)
				 * or the free chunk is just one unit larger.
				 * In either case, alloc the whole thing,
				 * because there's no point in keeping a free
				 * block only large enough to hold the header.
				 */
				q->ptr = p->ptr;
			} else {
				/* Carve out piece from end of entry */
				p->size -= nu;
				p += p->size;
				p->size = nu;
			}
			p->ptr = p;	/* for auditing */
#ifdef Kelvdebug
 			p->l[(p->size * 2) - 2] = (long)p;	/* debug */
 			p->l[(p->size * 2) - 1] = MARKER;	/* debug */
#endif
			Allocs++;
			Availmem -= p->size;
			p++;
			/* On the brain-damaged Intel CPUs in
			 * "large data" model, make sure the offset field
			 * in the pointer we return isn't null.
			 * The Turbo C compiler and certain
			 * library functions like strrchr() assume this.
			 */
			if(FP_OFF(p) == 0)	/* Return denormalized but equivalent pointer */
				return (void *)(p - 1);	/*lint !e413 */

			return (void *)p;
		}
		if (p == Allocp && ((p = morecore(nu)) == NULLHDR)){
			Memfail++;
			return NULL;
		}
	}
}
/* Get more memory from the system and put it on the heap */
static HEADER *
morecore(nu)
unsigned nu;
{
	void *cp;
	HEADER *up;
	unsigned size;
	void *sbrk(int);	/***/

	Morecores++;
	size = nu * ABLKSIZE;
	/* First try to expand our main memory block */
	if((int)(cp = (void *)sbrk((int) size)) != -1){
		up = (HEADER *)cp;
		up->size = nu;
		up->ptr = up;	/* satisfy audit */
		free(up + 1);
		Heapsize += size;
		Frees--;	/* Nullify increment inside free() */
		return Allocp;
	}
	return NULL;
}


/* Put memory block back on heap */
void
free(blk)
const void *blk;
{
	register HEADER *p, *q;
	unsigned short *ptr;

	if(blk == NULL)
		return;		/* Required by ANSI */
	p = (HEADER *)blk - 1;
	/* Audit check */
	if(p->ptr != p){
		ptr = (unsigned short *)&blk;
		tprintf(freewarn,freewarn1,blk,ptr[-1],ptr[-2],Curproc->name,'\n');
		(void) fflush(stdout);
		Invalid++;
		log(-1,freewarn,freewarn1,blk,ptr[-1],ptr[-2],Curproc->name,' ');
		(void) logstat();
		return;
	}
#ifdef Kelvdebug
 	if(p->l[(p->size * 2) - 2] != (long)p || p->l[(p->size * 2) - 1] != MARKER){
 		ptr = (unsigned short *)&blk;
		tprintf(freewarn,freewarn2,blk,ptr[-1],ptr[-2],Curproc->name,'\n');
		fflush(stdout);
 		Overuse++;
		log(-1,freewarn,freewarn2,blk,ptr[-1],ptr[-2],Curproc->name,' ');
		(void) logstat();
 		return;
 	}
#endif
	Availmem += p->size;
	/* Search the free list looking for the right place to insert */
	for(q = Allocp; !(p > q && p < q->ptr); q = q->ptr){
		/* Highest address on circular list? */
		if(q >= q->ptr && (p > q || p < q->ptr))
			break;
	}
	if(p + p->size == q->ptr){
		/* Combine with front of this entry */
		p->size += q->ptr->size;
		p->ptr = q->ptr->ptr;
	} else {
		/* Link to front of this entry */
		p->ptr = q->ptr;
	}
	if(q + q->size == p){
		/* Combine with end of this entry */
		q->size += p->size;
		q->ptr = p->ptr;
	} else {
		/* Link to end of this entry */
		q->ptr = p;
	}

	Frees++;
	if(Memwait != 0)
		(void) ksignal(&Memwait,0);
}

#ifdef	notdef	/* Not presently used */
/* Move existing block to new area */
void *
realloc(area,size)
void *area;
unsigned size;
{
	unsigned osize;
	HEADER *hp;
	char *cp;

	hp = ((HEADER *)area) - 1;
	osize = (hp->size -1) * ABLKSIZE;

	free(area);	/* Hopefully you have your interrupts off , Phil. */
	if((cp = _malloc(size)) != NULL && cp != area)
		memcpy((char *)cp,(char *)area,size>osize? osize : size);
	return cp;
}
/* Allocate block of cleared memory */
void *
calloc(nelem,size)
unsigned nelem;	/* Number of elements */
unsigned size;	/* Size of each element */
{
	register unsigned i;
	register char *cp;

	i = nelem * size;
	if((cp = _malloc(i)) != NULL)
		memset(cp,0,i);
	return cp;
}
#endif
/* Version of malloc() that waits if necessary for memory to become available */
void *
mallocw(nb)
unsigned nb;
{
	register void *p;

	while((p = _malloc(nb)) == NULL){
		Memwait++;
		kwait(&Memwait);
		Memwait--;
	}
	return p;
}
/* Version of calloc that waits if necessary for memory to become available */
void *
callocw(nelem,size)
unsigned nelem;	/* Number of elements */
unsigned size;	/* Size of each element */
{
	register unsigned i;
	register char *cp;

	i = nelem * size;
	cp = mallocw(i);
	memset(cp,0,i);
	return cp;
}
/* Return available memory on our heap plus available system memory */
static unsigned long
availmem()
{
void *p;

	if(Availmem*ABLKSIZE >= (uint32) Memthresh)
		return 0;	/* We're clearly OK */

	/* There's not enough on the heap; try calling malloc to see if
	 * it can get more from the system
	 */
	if((p = malloc((int16) Memthresh)) != NULL){
		free(p);
		return 0;	/* Okay */
	}
	if((p = malloc((int16)(Memthresh/2))) != NULL){
		free(p);
		return 1;	/* Yellow alert */
	}
	return 2;		/* Red alert */
}

/* Log heap stats */
static int
logstat()
{
	if(Memdebug)	{
		log (-1, "Memory status :");
		log (-1, HeapSizeStr, Heapsize, Availmem * ABLKSIZE, \
			100L * Availmem * ABLKSIZE / Heapsize, Morecores);
		log (-1, AllocStr, Allocs, Frees, Allocs - Frees, Memfail, Invalid
#ifdef Kelvdebug
			, Overuse
#endif
			);
		log (-1, GarbageStr, Yellows, Reds);
		log (-1, ThreshStr, Memthresh);
	}
	return 0;
}

void
mbmemory (void)
{
	tputs ("tnos: ");
	tprintf(HeapSizeStr,Heapsize,Availmem*ABLKSIZE, \
		100L*Availmem*ABLKSIZE/Heapsize,Morecores);
	tputc ('\n');
}



static void
dpmistat (void)
{
_go32_dpmi_meminfo info;
__dpmi_version_ret vers;
int needcomma = 0;
unsigned long pagesize;

	(void) __dpmi_get_version (&vers);
	tprintf ("\ndpmi: version %d.%d - ", vers.major, vers.minor);
	tprintf ("%-d bit host, %s mode, virtual memory %ssupported\n",
		(vers.flags & 1) ? 32 : 16, (vers.flags & 2) ? "real" : "V86",
		(vers.flags & 4) ? "" : "not ");
	if (__dpmi_get_page_size (&pagesize) != 0)
		pagesize = 0;
	if (_go32_dpmi_get_free_memory_information (&info) == 0)	{
		tprintf ("      avail mem %lu", info.available_memory);
		if (info.linear_space != (unsigned long) -1)
			tprintf (", linear mem %lu", (pagesize) ? info.linear_space * pagesize : info.linear_space);
		if (info.free_linear_space != (unsigned long) -1)
			tprintf (", free linear mem %lu", (pagesize) ? info.free_linear_space * pagesize : info.free_linear_space);
		tputc ('\n');
		if (pagesize)	{
			tprintf ("      page size %lu", pagesize);
			needcomma = 1;
		}
		if (info.total_physical_pages != (unsigned long) -1)	{
			tprintf ("%sphy pages %lu", (needcomma) ? ", " : "      ", info.total_physical_pages);
			needcomma = 1;
		}
		if (info.available_physical_pages != (unsigned long) -1)	{
			tprintf ("%savail phy pages %lu", (needcomma) ? ", " : "      ", info.available_physical_pages);
			needcomma = 1;
		}
		if (needcomma)
			tputc ('\n');
		needcomma = 0;
		if (info.available_pages != (unsigned long) -1)	{
			tprintf ("      avail pages %lu", info.available_pages);
			needcomma = 1;
		}
		if (info.available_lockable_pages != (unsigned long) -1)	{
			tprintf ("%savail lockable pages %lu", (needcomma) ? ", " : "      ", info.available_lockable_pages);
			needcomma = 1;
		}
		if (info.unlocked_pages != (unsigned long) -1)	{
			tprintf ("%savail unlock pages %lu", (needcomma) ? ", " : "      ", info.unlocked_pages);
			needcomma = 1;
		}
		if (needcomma)
			tputc ('\n');
	}
}



/* Print heap stats */
int
domemstat(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
	mbmemory ();
	tputs ("      ");
	tprintf (AllocStr, Allocs, Frees, Allocs - Frees, Memfail, Invalid
#ifdef Kelvdebug
		, Overuse
#endif
		);
	tputc ('\n');
	tputs ("      ");
	tprintf (GarbageStr, Yellows, Reds);
	dpmistat ();
	return 0;
}

/* Print heap free list */
static int
dofreelist(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
	HEADER *p;
	int i = 0;

	for(p = Base.ptr;p != (HEADER *)&Base;p = p->ptr){
		tprintf("%4.4x %6lu", p, p->size * ABLKSIZE);
		if(++i == 5){
			i = 0;
			if(tputc('\n') == EOF)
				return 0;
		} else
			(void) tputs(" | ");
	}
	if(i != 0)
		tputc('\n');
	return 0;
}
static int
dosizes(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;

	for (i = 0; i <= 12; i += 4)	{
		tprintf ("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n",
		 1<<i, Sizes[i], 2<<i,Sizes[i+1],
		 4<<i, Sizes[i+2], 8<<i,Sizes[i+3]);
	}
	return 0;
}
int
domem(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Memcmds,argc,argv,p);
}

static int
dothresh(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setlong(&Memthresh,"Free memory threshold (bytes)",argc,argv);
}

static int
dogcollect(argc,argv,p)
int argc;
char *argv[];
void *p;
{
void (**fp)(int);

	Yellows++;
	for(fp = Gcollect;*fp != NULL;fp++)
		(**fp)(0);
	(void) tputs ("Garbage collection forced at Yellow level!\n");
	return 0;
}


void
gcollect(i,v1,v2)
int i;		/* Args not used */
char *v1;
void *v2;
{
void (**fp)(int);
int red = 0;

	server_disconnect_io ();
	for ( ; ; ) {
		kpause (1000);
		switch(availmem())	{
			case 0:
				continue;	/* All is well */
			case 1:
				red = 0;
				Yellows++;
				break;
			case 2:
				red = 1;
				Reds++;
				break;
			default:
				break;
		}
		for(fp = Gcollect;*fp != NULL;fp++)
			(**fp)(red);
	}
}

static int
domemdebug(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setbool(&Memdebug,"\"Mem stat\" to log after failures",argc,argv);
}

#endif /* !UNIX */
