#ifndef NORCSID
static char rcsid[] = "$Header$";
#endif

#include <stdio.h>
#include "param.h"
#include "types.h"
#include "assert.h"
#include "alloc.h"
#include "line.h"
#include "lookup.h"
#include "proinf.h"

/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Hans van Staveren
 */

#ifdef USEMALLOC

short * myalloc();

#define newcore(size) myalloc(size)
#define oldcore(p,size) free(p)

#else

/* #define CORECHECK	/* if defined tests are made to insure
			   each block occurs at most once */

#define CCHUNK	1024	/* number of shorts asked from system */

short *newcore(),*freshcore();
extern char *sbrk();

#ifdef COREDEBUG
int shortsasked=0;
#endif

#endif

/*
 * The following two sizetables contain the sizes of the various kinds
 * of line and argument structures.
 * Care has been taken to make this table implementation independent,
 * but if you think very hard you might find a compiler failing the
 * assumptions made.
 * A wasteful but safe approach is to replace every line of them by
 *  sizeof(line_t)
 * and
 *  sizeof(arg_t)
 * respectively.
 */

#define LBASE (sizeof(line_t)-sizeof(un_l_a))

int lsizetab[] = {
	LBASE,
	LBASE+sizeof(short),
	LBASE+sizeof(offset),
	LBASE+sizeof(num_p),
	LBASE+sizeof(sym_p),
	LBASE+sizeof(s_la_sval),
	LBASE+sizeof(s_la_lval),
	LBASE+sizeof(arg_p),
	LBASE
};

#define ABASE (sizeof(arg_t)-sizeof(un_a_a))

int asizetab[] = {
	ABASE+sizeof(offset),
	ABASE+sizeof(num_p),
	ABASE+sizeof(sym_p),
	ABASE+sizeof(s_a_val),
	ABASE+sizeof(argb_t),
	ABASE+sizeof(s_a_con),
	ABASE+sizeof(s_a_con),
	ABASE+sizeof(s_a_con),
};

/*
 * alloc routines:
 * Two parts:
 *   1)	typed alloc and free routines
 *   2) untyped raw core allocation
 */

/*
 * PART 1
 */

line_p	newline(optyp) int optyp; {
	register line_p lnp;
	register kind=optyp;

	if (kind>OPMINI)
		kind = OPMINI;
	lnp = (line_p) newcore(lsizetab[kind]);
	lnp->l_optyp = optyp;
	return(lnp);
}

oldline(lnp) register line_p lnp; {
	register kind=lnp->l_optyp&BMASK;

	if (kind>OPMINI)
		kind = OPMINI;
	if (kind == OPLIST)
		oldargs(lnp->l_a.la_arg);
	oldcore((short *) lnp,lsizetab[kind]);
}

arg_p newarg(kind) int kind; {
	register arg_p ap;

	ap = (arg_p) newcore(asizetab[kind]);
	ap->a_typ = kind;
	return(ap);
}

oldargs(ap) register arg_p ap; {
	register arg_p	next;

	while (ap != (arg_p) 0) {
		next = ap->a_next;
		switch(ap->a_typ) {
		case ARGSTR:
			oldargb(ap->a_a.a_string.ab_next);
			break;
		case ARGICN:
		case ARGUCN:
		case ARGFCN:
			oldargb(ap->a_a.a_con.ac_con.ab_next);
			break;
		}
		oldcore((short *) ap,asizetab[ap->a_typ]);
		ap = next;
	}
}

oldargb(abp) register argb_p abp; {
	register argb_p next;

	while (abp != (argb_p) 0) {
		next = abp->ab_next;
		oldcore((short *) abp,sizeof (argb_t));
		abp = next;
	}
}

reg_p newreg() {

	return((reg_p) newcore(sizeof(reg_t)));
}

oldreg(rp) reg_p rp; {

	oldcore((short *) rp,sizeof(reg_t));
}

num_p newnum() {

	return((num_p) newcore(sizeof(num_t)));
}

oldnum(lp) num_p lp; {

	oldcore((short *) lp,sizeof(num_t));
}

offset *newrom() {

	return((offset *) newcore(MAXROM*sizeof(offset)));
}

sym_p newsym(len) int len; {
	/*
	 * sym_t includes a 2 character s_name at the end
	 * extend this structure with len-2 characters
	 */
	return((sym_p) newcore(sizeof(sym_t) - 2 + len));
}

argb_p newargb() {

	return((argb_p) newcore(sizeof(argb_t)));
}

#ifndef USEMALLOC

/******************************************************************/
/******   Start of raw core management package    *****************/
/******************************************************************/

#define MAXSHORT 30	/* Maximum number of shorts one can ask for */

short *freelist[MAXSHORT];

typedef struct coreblock {
	struct coreblock *co_next;
	short co_size;
} core_t,*core_p;

#define SINC	(sizeof(core_t)/sizeof(short))
#ifdef COREDEBUG
coreverbose() {
	register size;
	register short *p;
	register sum;

	sum = 0;
	for(size=1;size<MAXSHORT;size++)
		for (p=freelist[size];p!=0;p = *(short **) p)
			sum += size;
	fprintf(stderr,"Used core %u\n",(shortsasked-sum)*sizeof(short));
}
#endif

#ifdef SEPID

compactcore() {
	register core_p corelist=0,tp,cl;
	int size;

#ifdef COREDEBUG
	fprintf(stderr,"Almost out of core\n");
#endif
	for(size=SINC;size<MAXSHORT;size++) {
		while ((tp = (core_p) freelist[size]) != (core_p) 0) {
			freelist[size] = (short *) tp->co_next;
			tp->co_size = size;
			if (corelist==0 || tp<corelist) {
				tp->co_next = corelist;
				corelist = tp;
			} else {
				for(cl=corelist;cl->co_next != 0 && tp>cl->co_next;
							cl = cl->co_next)
					;
				tp->co_next = cl->co_next;
				cl->co_next = tp;
			}
		}
	}
	while (corelist != 0) {
		while ((short *) corelist->co_next ==
		    (short *) corelist + corelist->co_size) {
			corelist->co_size += corelist->co_next->co_size;
			corelist->co_next =  corelist->co_next->co_next;
		}
		assert(corelist->co_next==0 ||
			(short *) corelist->co_next >
			    (short *) corelist + corelist->co_size);
		while (corelist->co_size >= MAXSHORT+SINC) {
			oldcore((short *) corelist + corelist->co_size-(MAXSHORT-1),
				sizeof(short)*(MAXSHORT-1));
			corelist->co_size -= MAXSHORT;
		}
		if (corelist->co_size >= MAXSHORT) {
			oldcore((short *) corelist + corelist->co_size-SINC,
				sizeof(short)*SINC);
			corelist->co_size -= SINC;
		}
		cl = corelist->co_next;
		oldcore((short *) corelist, sizeof(short)*corelist->co_size);
		corelist = cl;
	}
}

short *grabcore(size) int size; {
	register short *p;
	register trysize;

	/*
	 * Desperate situation, can't get more core from system.
	 * Postpone giving up just a little bit by splitting up
	 * larger free blocks if possible.
	 * Algorithm is worst fit.
	 */

	assert(size<2*MAXSHORT);
	for(trysize=2*MAXSHORT-2; trysize>size; trysize -= 2) {
		p = freelist[trysize/sizeof(short)];
		if ( p != (short *) 0) {
			freelist[trysize/sizeof(short)] = *(short **) p;
			oldcore(p+size/sizeof(short),trysize-size);
			return(p);
		}
	}

	/*
	 * Can't get more core from the biggies, try to combine the
	 * little ones. This is expensive but probably better than
	 * giving up.
	 */

	compactcore();
	if ((p=freelist[size/sizeof(short)]) != 0) {
		freelist[size/sizeof(short)] = * (short **) p;
		return(p);
	}
	for(trysize=2*MAXSHORT-2; trysize>size; trysize -= 2) {
		p = freelist[trysize/sizeof(short)];
		if ( p != (short *) 0) {
			freelist[trysize/sizeof(short)] = *(short **) p;
			oldcore(p+size/sizeof(short),trysize-size);
			return(p);
		}
	}

	/*
	 * That's it then. Finished.
	 */

	return(0);
}
#endif	/* SEPID */

short *newcore(size) int size; {
	register short *p,*q;

	size = (size + sizeof(int) - 1) & ~(sizeof(int) - 1);
	if( size < 2*MAXSHORT ) {
		if ((p=freelist[size/sizeof(short)]) != (short *) 0)
			freelist[size/sizeof(short)] = *(short **) p;
		else {
			p = freshcore(size);
#ifdef SEPID
			if (p == (short *) 0)
				p = grabcore(size);
#endif
		}
	} else
		p = freshcore(size);
	if (p == 0)
		error("out of memory");
	for (q=p; size > 0 ; size -= sizeof(short))
		*q++ = 0;
	return(p);
}

#ifdef NOMALLOC

/*
 * stdio uses malloc and free.
 * you can use these as substitutes
 */

char *malloc(size) int size; {

	/*
	 * malloc(III) is called by stdio,
	 * this routine is a substitute.
	 */

	return( (char *) newcore(size));
}

free() {

}
#endif

oldcore(p,size) short *p; int size; {
#ifdef CORECHECK
	register short *cp;
#endif

	assert(size<2*MAXSHORT);
#ifdef CORECHECK
	for (cp=freelist[size/sizeof(short)]; cp != (short *) 0;
	    cp = *(short **) cp)
		assert(cp != p);
#endif
	*(short **) p = freelist[size/sizeof(short)];
	freelist[size/sizeof(short)] = p;
}

short *ccur,*cend;

coreinit(p1,p2) short *p1,*p2; {

	/*
	 * coreinit is called with the boundaries of a piece of
	 * memory that can be used for starters.
	 */

	ccur = p1;
	cend = p2;
}

short *freshcore(size) int size; {
	register short *temp;
	static int cchunk=CCHUNK;
	
	while(&ccur[size/sizeof(short)] >= cend && cchunk>0) {
		do {
			temp = (short *) sbrk(cchunk*sizeof(short));
			if (temp == (short *) -1)
				cchunk >>= 1;
			else if (temp != cend)
				ccur = cend = temp;
		} while (temp == (short *) -1 && cchunk>0);
		cend += cchunk;
#ifdef COREDEBUG
		shortsasked += cchunk;
#endif
	}
	if (cchunk==0)
		return(0);
	temp = ccur;
	ccur = &ccur[size/sizeof(short)];
	return(temp);
}

#else	/* USEMALLOC */

coreinit() {

	/*
	 * Empty function, no initialization needed
	 */
}

short *myalloc(size) register size; {
	register short *p,*q;
	extern char *malloc();

	p = (short *)malloc(size);
	if (p == 0)
		error("out of memory");
	for(q=p;size>0;size -= sizeof(short))
		*q++ = 0;
	return(p);
}
#endif