// **************************************************************************
// **************************************************************************
// **                                                                      **
// ** MEMLIST.C                                                     MODULE **
// **                                                                      **
// ** Replacements for the standard ANSI C memory management functions.    **
// **                                                                      **
// **************************************************************************
// **************************************************************************

#include "generic.h"

#define  HEAP_DEBUG 0
#include "heap.h"



// **************************************************************************
// **************************************************************************
// **************************************************************************
//	GLOBAL VARIABLES
// **************************************************************************
// **************************************************************************
// **************************************************************************



// **************************************************************************
// **************************************************************************
// **************************************************************************
//	STATIC VARIABLES
// **************************************************************************
// **************************************************************************
// **************************************************************************



// **************************************************************************
// **************************************************************************
//	 # DEFINE MACROS
// **************************************************************************
// **************************************************************************

#define	LFPMEM_HEAD         0x15624390u
#define	LFPMEM_LIST         0x73469528u
#define	LFPMEM_FREE         0xE2E2E2E2u
#define	LFPMEM_HEADMARKER   0x21EF935Du
#define	LFPMEM_TAILMARKER   0xD539FE12u

#define	SIZE_ADD   3L
#define	SIZE_AND  ~3L


// **************************************************************************
// **************************************************************************
//	STATIC STRUCTURE DEFINITIONS
// **************************************************************************
// **************************************************************************

static	MEMLIST_T *         pcl__sGlobalList = NULL;

static	UL                  ul___ListBlockSize;

// **************************************************************************
// * MemInitPackage ()                                                      *
// **************************************************************************
// * Initialize the memory package                                          *
// **************************************************************************
// * Inputs  -                                                              *
// *                                                                        *
// * Output  SI  0 if OK, -ve if an error occurred.                         *
// **************************************************************************

global	SI MemInitPackage (void)

	{

	// Local variables.

	MEMHEAD_T *         pcl__Head;

	//

//	if (pcl__GlobalList != NULL) {
//		DIAGNOSE(("MemInitPackage - Package already initialized"));
//		goto errorExit;
//		}

	sl___CurMemUsed = 0;
	sl___MaxMemUsed = 0;

	// Allocate the global list header and then set its MEMHEAD size to zero
	// to indicate that it is special.

	pcl__GlobalList = (MEMLIST_T *) HeapMalloc(sizeof(MEMLIST_T), MEMTYPE_ANY);

	if (pcl__GlobalList == NULL) {
		DIAGNOSE(("MemInitPackage - Error allocating global list"));
		goto errorExit;
		}

	pcl__Head = ((MEMHEAD_T *) pcl__GlobalList) - 1;

	ul___ListBlockSize          = pcl__Head->ul___mhBlockSize;
	pcl__Head->ul___mhBlockSize = 0;

	// Initialize the list and link it into the list of lists.

	pcl__GlobalList->pcl__mlNextBlock = NULL;
	pcl__GlobalList->pcl__mlPrevBlock = NULL;
	pcl__GlobalList->ul___mlBlockSize = 0;
	pcl__GlobalList->ui___mlMagic     = LFPMEM_LIST;
	pcl__GlobalList->pcz__mlFile      = __FILE__;
	pcl__GlobalList->ui___mlLine      = __LINE__;
	pcl__GlobalList->pcl__mlNextList  = NULL;
	pcl__GlobalList->pcl__mlPrevList  = NULL;

	// All done, return OK.

	return (0);

	// Error Handler.

	errorExit:

		return (-1);

	}



// **************************************************************************
// * MemTermPackage ()                                                      *
// **************************************************************************
// * Terminate the memory package                                           *
// **************************************************************************
// * Inputs  -                                                              *
// *                                                                        *
// * Output  SI  0 if OK, -ve if an error occurred.                         *
// **************************************************************************

global	SI MemTermPackage (void)

	{

	// Local Variables.

	MEMLIST_T *         pcl__List;
	MEMHEAD_T *         pcl__Head;

	// Is the package initialized ?

	if (pcl__GlobalList == NULL) {
		DIAGNOSE(("MemTermPackage - Package not initialized"));
		goto errorExit;
		}

	// Print up anything still allocated to show memory leaks.

	// *** Not yet added printout of line # where allocated ***

	while ((pcl__List = pcl__GlobalList->pcl__mlPrevList) != NULL)
		{
		DIAGNOSE(("MemTermPackage - Unfreed Memory List ..."));
		while ((pcl__Head = pcl__List->pcl__mlPrevBlock) != NULL)
			{
			DIAGNOSE(("MemTermPackage - Unfreed Memory Block ..."));
			MemFree_N(pcl__Head+1);
			}
		MemFreeList_F(pcl__List, __FILE__, __LINE__);
		}

	if (pcl__GlobalList->pcl__mlPrevBlock != NULL)
		{
		DIAGNOSE(("MemTermPackage - Unfreed Global Memory ..."));
		while ((pcl__Head = pcl__GlobalList->pcl__mlPrevBlock) != NULL)
			{
			DIAGNOSE(("MemTermPackage - Unfreed Memory Block ..."));
			MemFree_N(pcl__Head+1);
			}
		}

	MemFreeList_F(pcl__GlobalList, __FILE__, __LINE__);

	pcl__GlobalList = NULL;

	// Any memory still allocated ?

	#if INCL_MEM_STATS
	if (sl___CurMemUsed != 0) {
		DIAGNOSE(("MemTermPackage - Some unlinked blocks still allocated"));
		goto errorExit;
		}
	#endif

	// All done, return OK.

	return (0);

	// Error Handler.

	errorExit:

		return (-1);

	}



// **************************************************************************
// * MemCreateList ()                                                       *
// **************************************************************************
// * Create a memory list using memory from the global heap                 *
// **************************************************************************
// * Inputs  char *       Name of file where allocated.                     *
// *         UI           Line of file where allocated.                     *
// *                                                                        *
// * Output  MEMLIST_T *  Pointer to the memory list allocated. NULL is     *
// *                      returned if not enough memory is available, or    *
// *                      and error occurred.                               *
// **************************************************************************

global	MEMLIST_T * MemCreateList (void)
	{
	return (MemCreateListfl(NULL, 0));
	}

global	MEMLIST_T * MemCreateListfl (char * pcz__File, UI ui___Line)

	{

	// Local Variables.

	MEMLIST_T *         pcl__List;
	MEMLIST_T *         pcl__Prev;

	// Abort if package not initialized.

	if (pcl__GlobalList == NULL) {
		DIAGNOSE(("MemCreateList - Package not initialized"));
		goto errorExit;
		}

	// Allocate the list header and then set its MEMHEAD size to zero
	// to indicate that it is special.

	pcl__List = (MEMLIST_T *) MemAlloc_LF
		(sizeof(MEMLIST_T), MEMTYPE_ANY, pcl__GlobalList, __FILE__, __LINE__);

	if (pcl__List == NULL) {
		#if DEBUG
			DumpHeap();
			DIAGNOSE(("MemCreateList - Unable to allocate memory"));
		#endif
		goto errorExit;
		}

	(((MEMHEAD_T *) pcl__List) - 1)->ul___mhBlockSize = 0;

	// Initialize the list and link it into the list of lists.

	pcl__List->pcl__mlNextBlock = NULL;
	pcl__List->pcl__mlPrevBlock = NULL;
	pcl__List->ul___mlBlockSize = 0;
	pcl__List->ui___mlMagic     = LFPMEM_LIST;
	pcl__List->pcz__mlFile      = pcz__File;
	pcl__List->ui___mlLine      = ui___Line;

	pcl__Prev = pcl__GlobalList->pcl__mlPrevList;
	pcl__GlobalList->pcl__mlPrevList = pcl__List;

	pcl__List->pcl__mlNextList = pcl__GlobalList;
	pcl__List->pcl__mlPrevList = pcl__Prev;

	if (pcl__Prev != NULL) {
		pcl__Prev->pcl__mlNextList = pcl__List;
		}

	// All done, return OK.

	return (pcl__List);

	// Error Handler.

	errorExit:

		return (NULL);

	}



// **************************************************************************
// * MemTestList ()                                                         *
// **************************************************************************
// * Test a memory list for bad blocks                                      *
// **************************************************************************
// * Inputs  void *       Ptr to memory lick to check.                      *
// *                                                                        *
// * Output  void *       Ptr to bad block, or NULL if all OK.              *
// **************************************************************************

global	void * MemTestList (MEMLIST_T * pcl__List)

	{

	// Local Variables.

	MEMHEAD_T *         pcl__Head;
	size_t              ul___Size;

	// Pass NULL to test the global list.

	if (pcl__List == NULL) {
		pcl__List  = pcl__GlobalList;
		}

	// Check the list block itself.

	if ((pcl__List->ui___mlMagic != LFPMEM_LIST) ||
		(pcl__List->ul___mlBlockSize != 0)) {
		DIAGNOSE(("MemTestList - Invalid or corrupt list block"));
		goto errorExit;
		}

	// Test the blocks in the reverse order that they were allocated.

	pcl__Head = pcl__List->pcl__mlPrevBlock;

	while (pcl__Head != NULL)
		{
		if (pcl__Head->ui___mhMagic != LFPMEM_HEAD) {
			DIAGNOSE(("MemTestList - Invalid or corrupt memory block"));
			goto errorExit;
			}
		pcl__Head = pcl__Head->pcl__mhPrevBlock;
		}

	// All blocks check out OK.

	return (NULL);

	// Error Handler.

	errorExit:

		return (pcl__Head);

	}



// **************************************************************************
// * MemFreeList ()                                                         *
// **************************************************************************
// * Free up a memory list structure                                        *
// **************************************************************************
// * Inputs  MEMLIST_T *  Ptr to memory list to free.                       *
// *                                                                        *
// * Output  SI           0 if OK, -ve if an error occurred.                *
// *                                                                        *
// * N.B.    An error will be signalled is the list is not empty. This can  *
// *         be used to detect memory leakage. You can use the function     *
// *         MemKillList() to empty the list before calling this routine.   *
// **************************************************************************

global	SI MemFreeList (MEMLIST_T * pcl__List)

	{

	// Local Variables.

	MEMHEAD_T *         pcl__Head;
	MEMLIST_T *         pcl__Next;
	MEMLIST_T *         pcl__Prev;

	// Abort if package not initialized.



	if (pcl__GlobalList == NULL) {
		DIAGNOSE(("MemFreeList - Package not initialized"));
		goto errorExit;
		}

	// Is the list both valid and empty ?

	if (pcl__List == NULL) return (0);

	if ((pcl__List->ui___mlMagic != LFPMEM_LIST) ||
		(pcl__List->ul___mlBlockSize != 0)) {
		DIAGNOSE(("MemFreeList - Invalid or corrupt list block"));
		goto errorExit;
		}

	if ((pcl__List->pcl__mlNextBlock != NULL) ||
		(pcl__List->pcl__mlPrevBlock != NULL)) {
		DIAGNOSE(("MemFreeList - List is not empty"));
		goto errorExit;
		}

	pcl__Head = ((MEMHEAD_T *) pcl__List) - 1;

	#if INCL_MEM_SUSPICIOUS
	if ((pcl__Head->ui___mhMagic != LFPMEM_HEAD) ||
		(pcl__Head->ul___mhBlockSize != 0)) {
		DIAGNOSE(("MemFreeList - Invalid or corrupt list header"));
		goto errorExit;
		}
	#endif

	// Unlink it from the list of lists.

	pcl__Next = pcl__List->pcl__mlNextList;
	pcl__Prev = pcl__List->pcl__mlPrevList;

	if (pcl__Next != NULL) {
		pcl__Next->pcl__mlPrevList = pcl__Prev;
		}
	if (pcl__Prev != NULL) {
		pcl__Prev->pcl__mlNextList = pcl__Next;
		}

	// Free up its memory, and unlink it from the global memory list.

	pcl__Head->ul___mhBlockSize = ul___ListBlockSize;

	if (MemFree_PF(pcl__List, __FILE__, __LINE__) < 0) goto errorExit;

	// All done, return.

	return (0);

	// Error Handler.

	errorExit:

		return (-1);

	}



// **************************************************************************
// * MemKillList ()                                                         *
// **************************************************************************
// * Free up all the memory blocks linked into a memory list                *
// **************************************************************************
// * Inputs  MEMLIST_T *  Ptr to memory list to free.                       *
// *                                                                        *
// * Output  SI           0 if OK, -ve if an error occurred.                *
// *                                                                        *
// * N.B.    An error will be signalled is the list is not empty. This can  *
// *         be used to detect memory leakage. You can use the function     *
// *         MemKillList() to empty the list before calling this routine.   *
// **************************************************************************

global	SI MemKillList (MEMLIST_T * pcl__List)

	{

	// Local Variables.

	MEMHEAD_T *         pcl__Head;

	// Abort if package not initialized.

	#if DEBUG
		if (pcl__GlobalList == NULL) {
			DIAGNOSE(("MemKillList - Package not initialized"));
			goto errorExit;
			}
	#endif

	// Is the list valid ?

	if (pcl__List == NULL) {
		pcl__List = pcl__GlobalList;
		}

	if (pcl__List == NULL) return (0);

	if ((pcl__List->ui___mlMagic != LFPMEM_LIST) ||
		(pcl__List->ul___mlBlockSize != 0)) {
		DIAGNOSE(("MemKillList - Invalid or corrupt list block"));
		goto errorExit;
		}

	// Free up the blocks in the reverse order that they were allocated.

	while ((pcl__Head = pcl__List->pcl__mlPrevBlock) != NULL)
		{
		if (MemListFree(pcl__Head+1) <  0) goto errorExit;
		}

	// All done, return.

	return (0);

	// Error Handler.

	errorExit:

		return (-1);

	}



// **************************************************************************
// * MemListAlloc ()                                                        *
// **************************************************************************
// * Allocate memory from the global heap - add to specified list           *
// **************************************************************************
// * Inputs  MEMLIST_T *  Ptr to memory list (or NULL for global list).     *
// *         size_t       Number of bytes to allocate.                      *
// *         UI           Type of memory to allocate.                       *
// *                                                                        *
// * Output  void *       Pointer to the memory block allocated. NULL is    *
// *                      returned if not enough memory is available, or    *
// *                      if numbytes is 0.                                 *
// *                                                                        *
// * N.B.    Because of the header, this CANNOT be used to allocate page    *
// *         aligned memory.                                                *
// **************************************************************************

global	void * MemListAlloc (MEMLIST_T * pcl__List, size_t ui___Size, UI ui___Type)
	{
	return (MemListAllocfl(ui___Size, ui___Type, NULL, 0));
	}

global	void * MemListAllocfl (MEMLIST_T * pcl__List, size_t ui___Size, UI ui___Type,
							char * pcz__File, UI ui___Line)

	{

	// Local Variables.

	MEMHEAD_T *         pcl__Head;
	MEMHEAD_T *         pcl__Prev;

	// Abort if package not initialized.

	#if DEBUG
		if (pcl__GlobalList == NULL) {
			DIAGNOSE(("MemKillList - Package not initialized"));
			goto errorExit;
			}
	#endif

	// Is the list valid ?

	if (pcl__List == NULL) {
		pcl__List = pcl__GlobalList;
		}

	if (pcl__List->ui___mlMagic != LFPMEM_LIST) {
		DIAGNOSE(("MemListAllocfl - Invalid or corrupt list"));
		goto errorExit;
		}

	#if DEBUG
		if (pcz__File == NULL) {
			pcz__File = __FILE__;
			ui___Line = __LINE__;
			}
	#endif

	ui___Size =
		(ui___Size + sizeof(MEMHEAD_T) + SIZE_ADD) & SIZE_AND;

	if (ui___Type & MEM_CLEAR) {
		pcl__Head = (MEMHEAD_T *) HeapCalloc(ui___Size, ui___Type, pcz__File, ui___Line);
		} else {
		pcl__Head = (MEMHEAD_T *) HeapMalloc(ui___Size, ui___Type, pcz__File, ui___Line);
		}

	if (pcl__Head == NULL) {
		#if DEBUG
			HeapDump();
			DIAGNOSE(("MemListAlloc - Unable to allocate 0x%08X bytes", ui___Size));
		#endif
		goto errorExit;
		}

	pcl__Head->ul___mhBlockSize = ui___Size;

	#if INCL_MEM_SUSPICIOUS
	pcl__Head->pcz__mhFile      = pcz__File;
	pcl__Head->ui___mhLine      = ui___Line;
	pcl__Head->ui___mhMagic     = LFPMEM_HEAD;
	#endif

	pcl__Prev = pcl__List->pcl__mlPrevBlock;
	pcl__List->pcl__mlPrevBlock = pcl__Head;

	pcl__Head->pcl__mhNextBlock = (MEMHEAD_T *) pcl__List;
	pcl__Head->pcl__mhPrevBlock = pcl__Prev;

	if (pcl__Prev != NULL) {
		pcl__Prev->pcl__mhNextBlock = pcl__Head;
		}

	pcl__Head += 1;

//	#if INCL_MEM_CLRFREE
//	memset(pcl__Head, MEMLIST_WIPE, ui___Size - (sizeof(MEMHEAD_T) + MEMTAIL_SIZE));
//	#endif

	return (pcl__Head);

	// Error Handler.

	errorExit:

		return (NULL);

	}



// **************************************************************************
// * MemListFree ()                                                         *
// **************************************************************************
// * Return memory to the global heap - paranoid version                    *
// **************************************************************************
// * Inputs  void *       Ptr to memory to free.                            *
// *                                                                        *
// * Output  SI           0 if OK, -ve if an error occurred.                *
// *                                                                        *
// * N.B.    Because of the header, this CANNOT be used to allocate page    *
// *         aligned memory.                                                *
// **************************************************************************

global	SI MemListFree (void * pbf__Memory)

	{

	// Local Variables.

	MEMHEAD_T *         pcl__Head;
	size_t              ul___Size;
	MEMHEAD_T *         pcl__Next;
	MEMHEAD_T *         pcl__Prev;

	//

	if (pbf__Memory == NULL) return (0);

	pcl__Head = ((MEMHEAD_T *) pbf__Memory) - 1;

	if (pcl__Head->ui___mhMagic != MEMLIST_HEAD) {
		DIAGNOSE(("MemListFree - Invalid or corrupt memory block"));
		goto errorExit;
		}

	ul___Size = pcl__Head->ul___mhBlockSize;

	#if INCL_MEM_SUSPICIOUS
	if (ul___Size == 0) {
		DIAGNOSE(("MemListFree - Attempt to free a list block"));
		goto errorExit;
		}
	#endif

	#if INCL_MEM_PARANOID
	if (pcl__Head->ud___mhDataHead != LFPMEM_HEADMARKER) {
		DIAGNOSE(("MemListFree - Memory underrun detected"));
		goto errorExit;
		}
	#endif

	pcl__Next = pcl__Head->pcl__mhNextBlock;
	pcl__Prev = pcl__Head->pcl__mhPrevBlock;

	if (pcl__Next != NULL) {
		pcl__Next->pcl__mhPrevBlock = pcl__Prev;
		}
	if (pcl__Prev != NULL) {
		pcl__Prev->pcl__mhNextBlock = pcl__Next;
		}

//	#if INCL_MEM_CLRFREE
//	memset(pcl__Head, MEMLIST_WIPE, ul___Size);
//	#endif

	HeapFree(pcl__Head);

	return (0);

	// Error Handler.

	errorExit:

		return (-1);

	}



// **************************************************************************
// **************************************************************************
// **************************************************************************
// *************************************************************************