/* $Header$ */
/*	M E M O R Y  A L L O C A T I O N  R O U T I N E S	*/

/*	The allocation of memory in this program, which plays an important
	role in reading files, replacing macros and building expression
	trees, is not performed by malloc etc.  The reason for having own
	memory allocation routines (malloc(), realloc() and free()) is
	plain: the garbage collection performed by the library functions
	malloc(), realloc() and free() costs a lot of time, while in most
	cases (on a VAX) the freeing and reallocation of memory is not
	necessary.  The only reallocation done in this program is at
	building strings in memory.  This means that the last
	(re-)allocated piece of memory can be extended.

	The (basic) memory allocating routines offered by this memory
	handling package are:

	char *malloc(n)		: allocate n bytes
	char *realloc(ptr, n)	: reallocate buffer to n bytes
					(works only if ptr was last allocated)
	free(ptr)		: if ptr points to last allocated
					memory, this memory is re-allocatable
	Salloc(str, sz)		: save string in malloc storage
*/

#include	<system.h>
#include	"myalloc.h"	/* UF */
#include	"debug.h"	/* UF */

#include	"alloc.h"
#include	"assert.h"

#ifdef	OWNALLOC
char *sys_break();
/* the following variables are used for book-keeping		 */
static int nfreebytes = 0;	/* # free bytes in sys_break space */
static char *freeb;		/* pointer to first free byte	 */
static char *lastalloc;		/* pointer to last malloced sp	 */
static int lastnbytes;		/* nr of bytes in last allocated */
				/* space			 */
static char *firstfreeb = 0;

#endif	OWNALLOC

char *
Salloc(str, sz)
	register char str[];
	register int sz;
{
	/*	Salloc() is not a primitive function: it just allocates a
		piece of storage and copies a given string into it.
	*/
	char *res = Malloc(sz);
	register char *m = res;

	while (sz--)
		*m++ = *str++;
	return res;
}

#ifdef	OWNALLOC

#define	ALIGN(m)	(ALIGNSIZE * (((m) - 1) / ALIGNSIZE + 1))

char *
malloc(n)
	unsigned n;
{
	/*	malloc() is a very simple malloc().
	*/
	n = ALIGN(n);
	if (nfreebytes < n)	{
		register nbts = (n <= ALLOCSIZ) ? ALLOCSIZ : n;

		if (!nfreebytes)	{
			if ((freeb = sys_break(nbts)) == ILL_BREAK)
				fatal("out of memory");
		}
		else	{
			if (sys_break(nbts) == ILL_BREAK)
				fatal("out of memory");
		}
		nfreebytes += nbts;
	}
	lastalloc = freeb;
	freeb = lastalloc + n;
	lastnbytes = n;
	nfreebytes -= n;
	return lastalloc;
}

/*ARGSUSED*/
char *
realloc(ptr, n)
	char *ptr;
	unsigned n;
{
	/*	realloc() is designed to append more bytes to the latest
		allocated piece of memory. However reallocation should be
		performed, even if the mentioned memory is not the latest
		allocated one, this situation will not occur. To do so,
		realloc should know how many bytes are allocated the last
		time for that piece of memory. ????
	*/
	register int nbytes = n;

	ASSERT(ptr == lastalloc);	/* security		*/
	nbytes -= lastnbytes;		/* # bytes required	*/
	if (nbytes == 0)		/* no extra bytes	*/
		return lastalloc;

	/*	if nbytes < 0: free last allocated bytes;
		if nbytes > 0: allocate more bytes
	*/
	if (nbytes > 0)
		nbytes = ALIGN(nbytes);
	if (nfreebytes < nbytes)	{
		register int nbts = (nbytes < ALLOCSIZ) ? ALLOCSIZ : nbytes;
		if (sys_break(nbts) == ILL_BREAK)
			fatal("out of memory");
		nfreebytes += nbts;
	}
	freeb += nbytes;	/* less bytes			*/
	lastnbytes += nbytes;	/* change nr of last all. bytes	*/
	nfreebytes -= nbytes;	/* less or more free bytes	*/
	return lastalloc;
}

/* to ensure that the alloc library package will not be loaded:	*/
/*ARGSUSED*/
free(p)
	char *p;
{}

init_mem()
{
	firstfreeb = sys_break(0);
	/* align the first memory unit to ALIGNSIZE ???	*/
	if ((long) firstfreeb % ALIGNSIZE != 0) {
		register char *fb = firstfreeb;

		fb = (char *)ALIGN((long)fb);
		firstfreeb = sys_break(fb - firstfreeb);
		firstfreeb = fb;
		ASSERT((long)firstfreeb % ALIGNSIZE == 0);
	}
}

#ifdef	DEBUG
mem_stat()
{
	extern char options[];

	if (options['m'])
		print("Total nr of bytes allocated: %d\n",
			sys_break(0) - firstfreeb);
}
#endif	DEBUG
#endif	OWNALLOC