//  These are memory allocation routines which take the place of the
//  normal malloc and free routines.  They substitute for the normal
//  routines through define statements which are in the include
//  file "memcheck.h".  They call the normal system malloc and free
//  routines.
//
//  These routines allocate extra memory on each end of the users
//  memory which is filled with check bits that allow us to detect
//  overwrites which extend past either end of the memory which
//  the main program thinks was allocated.  Some parts of these routines
//  are specific to the Aztec C routines in that we count on there
//  being one long word immediately before the pointer which is
//  returned by the normal malloc and which is somehow significant
//  to the normal malloc.  These routines insure that this word
//  does not change.
//

//  Don't include memcheck.h, or we create a circular reference through
//  the define of malloc and free
#include	<stdio.h>
#include	<stdlib.h>


//  memcheck_validate_pointer (void* ptr)
//
//  Checks to verify that a pointer passed to this routine was originally
//  returned by the debug_malloc routine.  Prints error messages if not
//  Returns non-zero if all is OK, 0 if there was a problem.
//
short memcheck_validate_pointer (void* ptr)  {
	short err;
	
	//  this adjustment must match that which was done in my_malloc
	ptr = (void*)((char*)ptr - 8);

	err = 0;
	//  check the word at each end of the users space
	if (((long*)(ptr))[1] != 0xABDFBCFE)  {
		err++;
		printf ("free 0x%08lX: low end overwrite\n", (long)((char*)ptr + 8));
	}	
	if (((long*)(ptr))[((long*)(ptr))[0] + 2] != 0xF3E4ABBC)  {
		err++;	
		printf ("free 0x%08lX: high end overwrite\n", (long)((char*)ptr + 8));
	}	
	//  check the word which (I think) the Aztec routines use to track memory	
	if (((long*)(ptr))[((long*)(ptr))[0] + 3] != ((long*)(ptr))[-1])  {
		err++;
		printf ("free 0x%08lX: lost system tracking word\n", (long)((char*)ptr + 8));
	}
		
	if (err)
		return 0;
	return 1;
}




void* debug_malloc(size_t size)  {
	void* ptr;
	long n_longs;
	//  current code only works for sizes which are multiples of 4
	if ((size%4) != 0)  {
		printf ("malloc %ld; current code requires multiple of 4\n", size);
		return 0;
	}
	n_longs = size/4;
	
//	printf ("** user wants %ld bytes; ", size);	
	//  zero size request is special
	if (size == 0)
		return NULL;
		
	//  increase size by the amount we use and get memory	
	ptr = malloc (size + 16);
	if (ptr == NULL)  {
//		printf ("unavailable.\n");
		return NULL;
	}	
	
	//  if we got memory, then fill in the check bytes at the header
	//  and footer, record the length of the allocation, and save the
	//  word which I think the normal malloc uses to track memory
	//  allocation
	((long*)(ptr))[0] = n_longs;
	((long*)(ptr))[1] = 0xABDFBCFE;
	((long*)(ptr))[n_longs+2] = 0xF3E4ABBC;
	((long*)(ptr))[n_longs+3] = ((long*)(ptr))[-1];
	
	//  adjust the pointer to the middle of the actual allocated block
	//  before returning to the user
	ptr = (void*)((char*)ptr + 8);
//	printf ("returning pointer 0x%08lX\n", (long)ptr);
	return (void*)ptr;
}



void debug_free (void *ptr)  {
//	printf ("** freeing 0x%08lX;  ", ptr);
	//  A NULL pointer is special
	if (ptr == NULL)
		return;

	//  check pointer and print any error messages
	memcheck_validate_pointer (ptr);
	
	//  Adjust pointer and pass to the normal free routine.
	//  This adjustment must match that which was done in my_malloc
	ptr = (void*)((char*)ptr - 8);
	free (ptr);
//	printf ("freed\n");
}

			