/**********************************************************************/
/*   This  file  contains  routines  which  act as front ends to the  */
/*   malloc   family.   These   routines   aid   in  debugging,  and  */
/*   performance.  These  code  in  this  file  is part of the CRISP  */
/*   package  which  is  (C) P  Fox. This code may be freely used in  */
/*   any  product  but  the  copyright  remains  that of the author.  */
/*   This  copyright  notice  is  present  to  avoid  a  conflict of  */
/*   interest  and  to  ensure  that CRISP can continue to be a part  */
/*   of the public domain.					      */
/*   								      */
/*   (C) 1991 Paul Fox						      */
/**********************************************************************/

# include	<stdio.h>
# include	"chkalloc.h"
extern char	*malloc();

# define	TAIL_MAGIC 0x5441494c	/* TAIL */
# define	MAGIC	0x464f5859L	/* FOXY */
# define	FREED	0x46524545L	/* FREE */

# define	MAX_FILE	80

int	cnt_alloc = 0;
struct tail {
	long	magic;
	struct info *next;
	};
	
struct info {
	long	magic;
# if	TAIL
	struct tail *tail;
# endif
# if	WHERE
	char	file[MAX_FILE];
# endif
	};
# if	TAIL
void	check_tail();
static void tail_remove();
static void tail_add();
# endif
void	check_failed PROTO((char *));
void	free();
void	abort();

void *
check_alloc(len, file, line)
int	len;
char	*file;
int	line;
{	register char	*cp;
	register struct info *ip;
	int	n = len;
	
# if	TAIL
	check_tail();
	n += sizeof (struct tail);
# endif
	n += sizeof(struct info);
	cp = malloc(n);
	ip = (struct info *) cp;

	if (ip) {
		ip->magic = MAGIC;
# if	TAIL
		tail_add(ip, n);
# endif
# if	WHERE
		sprintf(ip->file, "FILE: %s LINE: %d", file, line);
# endif
		cp = (char *) (ip + 1);
		cnt_alloc++;
# if FILL_MEMORY
		memset(cp, 'A', len);
# endif
		}

	return cp;
}
void	*
check_realloc(ptr, n)
char	*ptr;
int	n;
{	char	*realloc();
	struct info *ip = (struct info *) ptr;
	struct info *new_ip;

	ip--;
	if (ip->magic != MAGIC)
		check_failed("Realloc non-alloced memory.");
	n += sizeof(struct info);
# if	TAIL
	n += sizeof(struct tail);
	check_tail();
	tail_remove(ip);
# endif
	new_ip = (struct info *) realloc((char *) ip, n);
# if	TAIL
	tail_add(new_ip, n);
# endif
	return (char *) (new_ip + 1);
}
void
check_free(ptr)
char	*ptr;
{	struct info *ip = (struct info *) ptr;

# if	TAIL
	check_tail();
# endif
	ip--;
	if (ip->magic == FREED)
		check_failed("Trying to free already freed memory.");
	if (ip->magic != MAGIC)
		check_failed("Freeing non-alloced memory.");
	cnt_alloc--;
# if	TAIL
	tail_remove(ip);
# endif
	ip->magic = FREED;
	free((char *) ip);
}
void
check_failed(str)
char	*str;
{
	fprintf(stderr, "CHK_ALLOC: %s\r\n", str);
	abort();
}
# if	TAIL
static struct info *alloc_chain = NULL;
/**********************************************************************/
/*   Walk  down  the  list  of  allocated  things  looking to see if  */
/*   anybodies trampled on our magic number.			      */
/**********************************************************************/
void
check_tail()
{	register struct info *ip = alloc_chain;
	struct tail *tl;
	struct tail tail_buf;
	
	while (ip) {
# if BYTE_ALIGNED == 0
		if (((long) ip->tail) & 3) {
			memcpy((char *) &tail_buf, (char *) ip->tail, sizeof tail_buf);
			tl = &tail_buf;
			}
		else
# endif
			tl = ip->tail;
		if (tl->magic != TAIL_MAGIC)
			check_failed("Overwritten past end of malloc() area.");
		ip = tl->next;
		}
}
/**********************************************************************/
/*   Fill  in  the  tail  information.  We  have to be careful about  */
/*   non-byte aligned machines.					      */
/**********************************************************************/
static void
tail_add(ip, n)
struct info *ip;
int	n;
{	struct tail *tl = (struct tail *) ((char *) ip + n - sizeof(struct tail));
# if	BYTE_ALIGNED
	ip->tail = tl;
# else
	struct tail tailbuf;
	ip->tail = tl;
	tl = &tailbuf;
# endif

	tl->magic = TAIL_MAGIC;
	tl->next = alloc_chain;
# if BYTE_ALIGNED == 0
	memcpy((char *) ip->tail, (char *) &tailbuf, sizeof tailbuf);
# endif
	alloc_chain = ip;
}
/**********************************************************************/
/*   Free  an  entry  from  the  chain  of tail pointers. We need to  */
/*   linearly scan, but what the heck.				      */
/**********************************************************************/
static void
tail_remove(ip)
struct info *ip;
{	register struct info *ip1;
	struct info *next;
	struct tail *tl;
	struct tail tailbuf;
	struct tail tailbuf1;
	
	if (ip == alloc_chain) {
# if BYTE_ALIGNED
		alloc_chain = ip->tail->next;
# else
		memcpy((char *) &tailbuf, (char *) ip->tail, sizeof tailbuf);
		alloc_chain = tailbuf.next;
# endif
		return;
		}
		
	ip1 = alloc_chain;
	while (ip1) {
		tl = ip1->tail;
# if BYTE_ALIGNED == 0
		if (((long) tl) & 3) {
			memcpy((char *) &tailbuf, (char *) tl, sizeof tailbuf);
			next = tailbuf.next;
			}
		else
# endif
			next = tl->next;
		if (next == ip) {
# if BYTE_ALIGNED
			tl->next = ip->tail->next;
# else
			memcpy((char *) &tailbuf1, (char *) ip->tail, sizeof tailbuf1);
			tailbuf.next = tailbuf1.next;
			memcpy((char *) tl, &tailbuf, sizeof tailbuf);
# endif
			break;
			}
		ip1 = next;
		}
}
# endif

/**********************************************************************/
/*   Following   handles   code   compiled   without  including  the  */
/*   chk_alloc.h file.						      */
/**********************************************************************/

# undef	chk_alloc
# undef chk_realloc
# undef chk_free

void *
chk_alloc(size)
int	size;
{
	return check_alloc(size, "DON'T KNOW", 1962);
}
void *
chk_realloc(ptr, size)
char *ptr;
int	size;
{
	return check_realloc(ptr, size);
}
void
chk_free(ptr)
void	*ptr;
{
	check_free(ptr);
}

/**********************************************************************/
/*   Following  code  can be used to find out how slow malloc really  */
/*   is  !!.  On  V.3  Unix  running  Microport  I  got  a 6 minutes  */
/*   elapsed  time  versus  4  seconds  for  this  test !!. On a Sun  */
/*   3/260 + 8MB of memory I got a 5x improvement. 		      */
/**********************************************************************/
# if 0
# include	<stdio.h>
char *malloc();
extern void free();
char *(*fn_malloc)() = malloc;
void (*fn_free)() = free;

# define	chk_alloc malloc
int mem_count = 0;
char	*freelist;
main(argc, argv)
char **argv;
{
	int	i;
	char	*ptrs[100000];
	if (argc > 1) {
		char *vm_alloc();
		void vm_free();
		fn_malloc = vm_alloc;
		fn_free = vm_free;
		}
# define	SIZE 		(sizeof ptrs / sizeof ptrs[0])
# define	MALLOC(a,b) 	(*fn_malloc)(a, b)
# define	FREE(a, b) 	(*fn_free)(a, b)
	
	for (i = 0; i < SIZE; i++)
		ptrs[i] = MALLOC(16, &freelist); 
	printf("Freeing %d bytes\n", mem_count); fflush(stdout);
	for (i = 0; i < SIZE; i++)
		FREE(ptrs[i], &freelist);
	printf("Finished...\n");
}
# endif
/************************************************************************
 *    Set  of  routines  to improve virtual memory characteristics of	*
 *    certain  data  structures.  This  is done by allocating a large	*
 *    number  of  the  things  such  that they'll all fit in at least	*
 *    one  MMU  page  of  memory  and when the things are freed, just	*
 *    put  them  on  a  free  list.  We  assume  the  things  we  are	*
 *    allocating are at least large enough to contain a pointer.	*
 *    									*
 *    We  never  actually  free  the memory but that shouldn't be a 	*
 *    concern.  Also  we  can't  ever  use  realloc  on these things.	*
 *    These  routines  are  ideal  for  smallish data structures like	*
 *    linked list headers, etc.						*
 ************************************************************************/

# define	MMU_PAGESIZE	(3 * 8192)/* Must be at least as big as   */
					/* real MMU page size.		*/
# define	NEXT(vp)	((VM *) ((char *) vp + size))
# define	NULL	0

typedef struct VM {
		struct VM *next;
		} VM;
# undef	vm_alloc
# undef	vm_free
void *
vm_alloc(size, fp)
int	size;
void **fp;
{	register VM	*vp1, *vp;
	register int i;
	int	entries;
	VM	**freeptr = (VM **) fp;

	if (*freeptr) {
		vp = *freeptr;
		*freeptr = vp->next;
		return (void *) vp;
		}
	entries = MMU_PAGESIZE / size;
	vp = (VM *) chk_alloc(entries * size);
	vp1 = *freeptr = NEXT(vp);
	for (i = 1; i++ < entries - 1; ) {
		vp1->next = NEXT(vp1);
		vp1 = vp1->next;
		}
	vp1->next = NULL;
	return (void *) vp;
}
void
vm_free(p, fp)
void	*p;
void	**fp;
{	VM *vp = (VM *) p;
	VM **freeptr = (VM **) fp;

	vp->next = *freeptr;
	*freeptr = vp;
}
