/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: allocate.c 1.2 90/05/21 $"

#include <stdio.h>
#include "config.h"			/* get definition of fatal() */
#include "expression.h"			/* expression struct definitions */

/*
 *  Allocate permanent data structures PERM_CLUMPSIZE bytes at a time.
 *  This number should be big, but not too big (see perm_alloc()).
 */
#define	PERM_CLUMPSIZE	16384

extern char *malloc();			/* allocate from heap */
extern size_t strlen();			/* get length of string */

typedef long aligntype;			/* alignment data type */
struct reuseable_list {			/* reuseable list element */
	unsigned l_allocated_len;	/* allocated length for text */
	struct list l_list;		/* list structure */
};

/*
 *  Allocate permanent storage.  This function is like malloc(), 
 *  except that the allocated storage can never be freed.
 *
 *  This function tries to reduce the number of times we call sbrk() by
 *  allocating a large piece of memory, and then doling out portions of the
 *  large piece until it is completely consumed.  Another large piece is
 *  then allocated when the first piece is consumed, and so on.
 *
 *  Since the allocated storage is never to be freed, we also skip all of
 *  the book-keeping work that malloc() does to support the later deallo-
 *  cation of memory.
 */
char *
perm_alloc(request_size)
register unsigned request_size;
{
	static unsigned nfree = 0;	/* #of free bytes in "clump" */
	static char *next_free;		/* next free byte */
	register unsigned fragment;
	register char *storage;

	fragment = request_size % sizeof(aligntype);
	if (fragment)			/* Keep things aligned */
		request_size += sizeof(aligntype) - fragment;
	if (request_size > nfree) {
		if ( (request_size > PERM_CLUMPSIZE) ||
		     (nfree > 0 && request_size > PERM_CLUMPSIZE/20) ) {
			/*
			 *  There is not sufficient memory left in the
			 *  current block to handle the request, and the
			 *  request is larger than 5% of original block
			 *  size.  Such requests are not allocated from
			 *  a new block, since a lot of memory would
			 *  be wasted in the old block.  We'll wait for
			 *  other smaller requests to consume the current
			 *  block, and then allocate another.
			 */
			return (malloc(request_size));
		}
		/*
		 *  The current block is full.  Allocate another.
		 */
		if ((next_free = malloc(PERM_CLUMPSIZE)) == (char *)NULL) {
			fatal("perm_alloc: unable to allocate storage\n");
			exit(1);
		}
		nfree = PERM_CLUMPSIZE;
	}
	storage = next_free;
	next_free += request_size;
	nfree -= request_size;
	return (storage);
}

/*
 *  Allocate a permanent string.
 *  The string can never be freed.
 */
char *
perm_str(str)
register char *str;
{
	register int len;
	register char *cp;

	len = strlen(str) + 1;		/* +1 for trailing null byte */
	cp = perm_alloc(len);
	memcpy(cp, str, len);
	return(cp);
}

static struct list *reuse_list = (struct list *)NULL;
static struct {				/* list statistics */
	unsigned permanent_allocated;	/* #permanent list structs allocated */
	unsigned reuseable_allocated;	/* #reuseable list structs allocated */
	unsigned recycled;		/* #recycled (reused) */
	unsigned free_calls;		/* #of free calls */
	unsigned freed;			/* #freed */
} list_stats;

/*
 *  Allocate a permanent list structure to contain the text: 'text' and
 *  add it to the head of the permanent list defined by 'list_headp.'
 *  The list element added to the head of the list cannot ever be freed.
 */
void
perm_to_head(list_headp, text)
register struct list_head *list_headp;
char *text;			/* Name has NOT been permanently allocated! */
{
	register struct	list *listp;
	register int len;

	len = strlen(text) + 1;		/* +1 for trailing null byte */
	listp = (struct list *)
		 perm_alloc(sizeof(struct list) - sizeof(listp->l_text) + len);
	/*
	 *   list_headp->l_head is the first on the list.
	 *   list_headp->l_last is the last on the list.
	 */
	listp->l_next = list_headp->l_head;
	list_headp->l_head = listp;
	if (list_headp->l_last == (struct list *)NULL)
		list_headp->l_last = listp;
	memcpy(listp->l_text, text, len);
	list_stats.permanent_allocated++;
}

/*
 *  Allocate a permanent list structure to contain the text: 'text' and
 *  add it to the tail of the permanent list defined by 'list_headp.'
 *  The list element added to the tail of the list cannot ever be freed.
 */
void
perm_to_tail(list_headp, text)
register struct list_head *list_headp;
char *text;			/* Name has NOT been permanently allocated! */
{
	register struct	list *listp;
	register int len;

	len = strlen(text) + 1;		/* +1 for trailing null byte */
	listp = (struct list *)
		 perm_alloc(sizeof(struct list) - sizeof(listp->l_text) + len);
	/*
	 *   list_headp->l_head is the first on the list.
	 *   list_headp->l_last is the last on the list.
	 */
	listp->l_next = (struct list *)NULL;
	if (list_headp->l_head == (struct list *)NULL)
		list_headp->l_head = listp;
	else
		list_headp->l_last->l_next = listp;
	list_headp->l_last = listp;
	memcpy(listp->l_text, text, len);
	list_stats.permanent_allocated++;
}

/*
 *  Allocate a reuseable list structure to contain the text: 'text' and
 *  add it to the tail of the reuseable list defined by 'list_headp.'
 *  The list element added to the tail of the list cannot ever be freed,
 *  except by using: free_list().
 */
void
reuseable_to_tail(list_headp, text)
register struct list_head *list_headp;
char *text;			/* Name has NOT been permanently allocated! */
{
	register struct reuseable_list *reuseable_listp;
	register int len;
	register int allocated_len;
	register int fragment;

	len = strlen(text) + 1;		/* +1 for trailing null byte */
	allocated_len = sizeof(struct reuseable_list)
		      - sizeof(reuseable_listp->l_list.l_text) + len;
	reuseable_listp = (struct reuseable_list *)reuse_list;
					/* first list struct from reuse list */
	if (reuseable_listp != (struct reuseable_list *)NULL) {
		/*
		 *  Step back to the reuseable_list structure beginning.
		 */
		reuseable_listp = (struct reuseable_list *)
				 ((char *)reuseable_listp
					- (  (char*)&reuseable_listp->l_list
					   - (char*)reuseable_listp         ) );
		if (reuseable_listp->l_allocated_len < allocated_len) 
			goto new;	/* Allocate a new element */
		/* 
		 *  Recycling a previously allocated list element.
		 */
		list_stats.recycled++;	/* one more list recycled */
		reuse_list = reuseable_listp->l_list.l_next;
					/* remove from free list */
	} else {
		/*
		 *  Allocating new reuseable list structure for the first time.
		 */
new:		fragment = allocated_len % sizeof(aligntype);
		if (fragment)		/* don't waste alignment bytes */
			allocated_len += sizeof(aligntype) - fragment;
		reuseable_listp = (struct reuseable_list *)
				  perm_alloc(allocated_len);
		reuseable_listp->l_allocated_len = allocated_len;
		list_stats.reuseable_allocated++;
	}
	/*
	 *   list_headp->l_head is the first on the list.
	 *   list_headp->l_last is the last on the list.
	 */
	reuseable_listp->l_list.l_next = (struct list *)NULL;
	if (list_headp->l_head == (struct list *)NULL)
		list_headp->l_head = &reuseable_listp->l_list;
	else
		list_headp->l_last->l_next = &reuseable_listp->l_list;
	list_headp->l_last = &reuseable_listp->l_list;
	memcpy(reuseable_listp->l_list.l_text, text, len);
}

/*
 *  Free a linked list of list structures that were allocated
 *  via: reuseable_list().
 */
void
free_list(listp)
register struct list *listp;
{
	register struct list *nextlistp;
	register struct list *firstlistp;

	firstlistp = listp;
	nextlistp = listp->l_next;
	list_stats.freed++;			/* one more list freed */
	list_stats.free_calls++;		/* one more recycle call */
	while (nextlistp!=(struct list *)NULL) {/* while there is another... */
		list_stats.freed++;		/* one more list freed */
		listp = nextlistp;
		nextlistp = nextlistp->l_next;
	}
	listp->l_next = reuse_list;		/* LIFO recycling.... */
	reuse_list = firstlistp;
}

static struct expr *reuse_expr = (struct expr *)NULL;
static struct {				/* expression statistics */
	unsigned allocated;		/* #expr structs allocated */
	unsigned recycled;		/* #recycled (reused) */
	unsigned free_calls;		/* #of free calls */
	unsigned freed;			/* #freed */
} expr_stats;

/*
 *  Allocate an expr structure.
 *  The expr element allocated cannot ever be freed,
 *  except by using: free_expr().
 */
struct expr *
new_expr()
{
	register struct expr *exprp;

	exprp = reuse_expr;
	if (exprp != (struct expr *)NULL) {
		/* 
		 *  Recycling a previously allocated expr element.
		 */
		expr_stats.recycled++;	/* one more expr recycled */
		reuse_expr = exprp->ex_u.expr.lexpr;
					/* remove from free expr */
	} else {
		/*
		 *  Allocating new expr structure for the first time.
		 */
		exprp = (struct expr *) perm_alloc(sizeof(struct expr));
		expr_stats.allocated++;
	}
	return (exprp);
}

/*
 *  Free an expression tree whose elements were allocated
 *  via: new_expr().
 */
void
free_expr(exprp)
register struct expr *exprp;
{
	static void free_exprchain();

	expr_stats.free_calls++;		/* one more recycle call */
	free_exprchain(exprp);
}

/*
 *  Internal function to free an expression tree whose elements
 *  were allocated via: new_expr().
 */
static void
free_exprchain(exprp)
register struct expr *exprp;
{
	register struct expr *nextexprp;
	struct expr *firstexprp;

	firstexprp = exprp;
	for (;;) {
		expr_stats.freed++;	/* one more expr freed */
		if (exprp->ex_op == EXOPconst || exprp->ex_op == EXOPsym)
			break;
		nextexprp = exprp->ex_u.expr.rexpr;
		if (nextexprp != (struct expr *)NULL)
			free_exprchain(nextexprp);
		nextexprp = exprp->ex_u.expr.lexpr;
		if (nextexprp == (struct expr *)NULL)
			break;
		exprp = nextexprp;
	}
	exprp->ex_u.expr.lexpr = reuse_expr;
	reuse_expr = firstexprp;
}

static struct exprlist *reuse_exprlist = (struct exprlist *)NULL;
static struct {				/* expression list statistics */
	unsigned allocated;		/* #exprlist structs allocated */
	unsigned recycled;		/* #recycled (reused) */
	unsigned free_calls;		/* #of free calls */
	unsigned freed;			/* #freed */
} exprlist_stats;

/*
 *  Allocate an exprlist structure.
 *  The exprlist element allocated cannot ever be freed,
 *  except by using: free_exprlist().
 */
void
new_exprlist(liststartp, expr)
register struct exprlist **liststartp;	/* pointer to expression list start */
struct expr *expr;			/* expression to be added to list */
{
	extern char *current_product_name();
	register struct exprlist *exprlistp;
	register struct exprlist *walklistp;

	exprlistp = reuse_exprlist;
	if (exprlistp != (struct exprlist *)NULL) {
		/* 
		 *  Recycling a previously allocated exprlist element.
		 */
		exprlist_stats.recycled++;	/* 1 more exprlist recycled */
		reuse_exprlist = exprlistp->exl_next;
						/* remove from free exprlist */
	} else {
		/*
		 *  Allocating new exprlist structure for the first time.
		 */
		exprlistp = (struct exprlist *)
			     perm_alloc(sizeof(struct exprlist));
		exprlist_stats.allocated++;
	}
	exprlistp->exl_next = (struct exprlist *)NULL;
	exprlistp->exl_expr = expr;
	exprlistp->exl_file = Ffile;	/* keep track of expression origin */
	exprlistp->exl_product = current_product_name();
	walklistp = *liststartp;
	while (walklistp != (struct exprlist *)NULL) {
		liststartp = &walklistp->exl_next;
		walklistp = *liststartp;
	}
	*liststartp = exprlistp;
}

/*
 *  Free a list of expressions whose elements were allocated
 *  via new_expr() and new_exprlist().
 */
void
free_exprlist(exprlistp)
register struct exprlist *exprlistp;
{
	register struct exprlist *nextexprlistp;
	struct exprlist *firstexprlistp;

	firstexprlistp = exprlistp;
	exprlist_stats.free_calls++;		/* one more recycle call */
	for (;;) {
		exprlist_stats.freed++;		/* one more exprlist freed */
		free_exprchain(exprlistp->exl_expr);
		nextexprlistp = exprlistp->exl_next;
		if (nextexprlistp == (struct exprlist *)NULL)
			break;
		exprlistp = nextexprlistp;
	}
	exprlistp->exl_next = reuse_exprlist;
	reuse_exprlist = firstexprlistp;
}

/*
 *  Show list structure statistics.
 */
void
show_liststats()
{
	printf ("List structure usage:\n");
	printf ("  Number of permanent list structures allocated:        %d\n",
		list_stats.permanent_allocated);
	printf ("  Number of reuseable list structures allocated:        %d\n",
		list_stats.reuseable_allocated);
	printf ("  Number of reuseable list structures recycled:         %d\n",
		list_stats.recycled);
	printf ("  Number of reuseable list structures freed:            %d\n",
		list_stats.freed);
	printf ("  Number of reuseable list structure allocation calls:  %d\n",
		list_stats.reuseable_allocated + list_stats.recycled);
	printf ("  Number of reuseable list structure free calls:        %d\n",
		list_stats.free_calls);
}

/*
 *  Show expression structure statistics.
 */
void
show_exprstats()
{
	printf ("Expression structure usage:\n");
	printf ("  Number of expression structures allocated:            %d\n",
		expr_stats.allocated);
	printf ("  Number of expression structures recycled:             %d\n",
		expr_stats.recycled);
	printf ("  Number of expression structures freed:                %d\n",
		expr_stats.freed);
	printf ("  Number of expression structure allocation calls:      %d\n",
		expr_stats.allocated + expr_stats.recycled);
	printf ("  Number of expression structure free calls:            %d\n",
		expr_stats.free_calls);
}

/*
 *  Show exprlist structure statistics.
 */
void
show_exprliststats()
{
	printf ("Expression list structure usage:\n");
	printf ("  Number of expression list structures allocated:       %d\n",
		exprlist_stats.allocated);
	printf ("  Number of expression list structures recycled:        %d\n",
		exprlist_stats.recycled);
	printf ("  Number of expression list structures freed:           %d\n",
		exprlist_stats.freed);
	printf ("  Number of expression list structure allocation calls: %d\n",
		exprlist_stats.allocated + exprlist_stats.recycled);
	printf ("  Number of expression list structure free calls:       %d\n",
		exprlist_stats.free_calls);
}
