/******************************************************************************
* All mallocs from irit modules should be piped through this allocator.       *
*									      *
*					 Written by Gershon Elber, April 1933 *
******************************************************************************/

#include <stdio.h>

#include <string.h>
#include "irit_sm.h"
#include "imalloc.h"

#define OVERWRITE_STAMP_START 1234567890L    /* A long */
#define OVERWRITE_STAMP_END   0xbd	     /* A byte */
#define OVERWRITE_STAMP_FREED -50964L        /* A long */
#define FREE_TABLE_SIZE 100000

#if defined(AMIGA) && defined(__SASC)
static VoidPtr __far AllocPtrTable[FREE_TABLE_SIZE];
#else
static VoidPtr AllocPtrTable[FREE_TABLE_SIZE];
#endif
static long
    IritDebugSearchPtr = 0;
static int
    AllocNumPtrs = 0,
    IritDebugMalloc = FALSE,
    IritDebugMallocTested = FALSE;

static void AllocError(char *Msg, VoidPtr *p);

/*****************************************************************************
* My Routine to	allocate dynamic memory. All program requests must call this *
* routine (no direct call to malloc).					     *
*****************************************************************************/
static void AllocError(char *Msg, VoidPtr *p)
{
    char s[128];

    sprintf(s, "%s, Ptr = 0x%lx", Msg, (unsigned long) p);

    fprintf(stderr, "Memory allocation error, %s\n", s);
}

/*****************************************************************************
* Free all allocated object to test for overwritten data.		     *
*****************************************************************************/
void IritTestAllDynMemory(int PrintAlloc)
{
    int i;

    for (i = 0; i < AllocNumPtrs; i++) {
	if (AllocPtrTable[i] != NULL) {
	    unsigned long
		size = *((unsigned long *) AllocPtrTable[i]);
	    VoidPtr
		p2 = ((char *) AllocPtrTable[i]) + 8 + size;

	    if (*((long *) ((char *) AllocPtrTable[i] + 4)) !=
						OVERWRITE_STAMP_START)
		AllocError("Overwritten start of dynamically allocated memory",
			   AllocPtrTable[i]);
	    else if (*((unsigned char *) p2) != OVERWRITE_STAMP_END)
		AllocError("Overwritten end of dynamically allocated memory",
			   AllocPtrTable[i]);

	    if (PrintAlloc)
		printf("Allocated 0x%08lx\n", AllocPtrTable[i]);
	}
    }
}

/*****************************************************************************
* My Routine to	allocate dynamic memory. All program requests must call this *
* routine (no direct call to malloc). Dies if no memory.		     *
*****************************************************************************/
VoidPtr IritMalloc(unsigned size)
{
    VoidPtr p;
    unsigned OldSize;

    if (!IritDebugMallocTested) {
        IritDebugMalloc = getenv("IRIT_MALLOC") != NULL;
        IritDebugSearchPtr = getenv("IRIT_MALLOC_PTR") != NULL ?
					atoi(getenv("IRIT_MALLOC_PTR")) : 0;
	IritDebugMallocTested = TRUE;
    }

    if (IritDebugMalloc) {
	IritTestAllDynMemory(0);

	OldSize = size;
	size += 16;
    }

    if ((p = malloc(size)) != NULL) {
	if (IritDebugMalloc) {
	    int i;
	    VoidPtr p2;

	    if (p != NULL && ((long) p) == IritDebugSearchPtr) {
		printf("Pointer 0x%08lx just allocated\n", p);
	    }

	    /* Save allocated pointer so we can search for it when freed. */
	    for (i = 0; i < AllocNumPtrs; i++) {
	        if (AllocPtrTable[i] == NULL) {
		    AllocPtrTable[i] = p;
		    break;
		}
	    }

	    if (i >= AllocNumPtrs) {
		if (i < FREE_TABLE_SIZE - 1)
		    AllocPtrTable[AllocNumPtrs++] = p;
		else {
		    fprintf(stderr, "Allocation table too small.\n");
		    exit(1);
		}
	    }

	    *((long *) p) = OldSize;
	    *((long *) (((char *) p) + 4)) = OVERWRITE_STAMP_START;
	    p = ((char *) p) + 8;
	    p2 = ((char *) p) + OldSize;
	    *((char *) p2) = (char) OVERWRITE_STAMP_END;

	    *((char *) p) = 0; /* In case the freed stamp is still there. */
	}
	    
	return p;
    }

    IritFatalError("Not Enough dynamic memory");

    return NULL;				    /* Make warnings silent. */
}

/*****************************************************************************
* My Routine to	free dynamic memory. All program requests must call this     *
* routine (no direct call to free).					     *
*****************************************************************************/
void IritFree(VoidPtr p)
{
    if (IritDebugMalloc) {
	int i;

	if (*((long *) p) == OVERWRITE_STAMP_FREED)
	    AllocError("Trying to free a free object again", p);

	if (p == NULL)
	    AllocError("Free a NULL pointer", p);

	IritTestAllDynMemory(0);

	*((long *) p) = OVERWRITE_STAMP_FREED;
	p = ((char *) p) - 8;

	/* Compare the freed pointer with the list of allocated ones. */
	for (i = 0; i < AllocNumPtrs; i++) {
	    if (AllocPtrTable[i] == p) {
		AllocPtrTable[i] = NULL;
		break;
	    }
	}

	if (i >= AllocNumPtrs)
	    AllocError("Free unallocated pointer", p);
    }

#ifdef __DEBUG_TC_MALLOC__
    switch (heapcheck()) {
	case _HEAPCORRUPT:
	    AllocError("Heap is corrupted", p);
	    break;
	case _BADNODE:
	    AllocError("Attempt to free a bogus pointer", p);
	    break;
	case _FREEENTRY:
	    AllocError("Attempt to free an already freed pointer", p);
	    break;
	case _HEAPOK:
	case _HEAPEMPTY:
	case _USEDENTRY:
	    break;
	default:
	    AllocError("Allocation error", p);
	    break;

    }
#endif /* __DEBUG_TC_MALLOC__ */

    free(p);
}
