/*===========================================================================
 *
 * File:	DL_Mem.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Sunday, April 01, 2001
 *
 * Implements memory specific routines for Dave's Library of common code.
 *
 *=========================================================================*/

	/* Include Files */
#include "dl_mem.h"
#include <string.h>
#include <time.h>
#include <ctype.h>

#ifndef _MSC_VER
  #include <alloc.h>
#endif

#if defined(_WIN32)
  #include <windows.h>
#endif


/*===========================================================================
 *
 * Begin Local Variables
 *
 *=========================================================================*/
  DEFINE_FILE();
/*===========================================================================
 *		End of Local Variables
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void* AllocateMemory (const size_t Size);
 *
 * For use when you always want a valid block of new memory returned. 
 * Attempts to allocate the given number of bytes (0 is an invalid size)
 * and return the pointer to the new block of memory.  On allocation
 * error the function throws an exception.  
 *
 *=========================================================================*/
void* AllocateMemory (const size_t Size) { 
  DEFINE_FUNCTION("AllocateMemory(size_t)");
  void* pNewObject;

	/* Ensure valid input */
  ASSERT(Size != 0);

	/* Attempt to allocate memory */
  CreateArrayPointer(pNewObject, char, Size);

	/* Initialize memory in debug mode */	
  #if defined(_DEBUG)
    memset(pNewObject, GARBAGE_CHAR, Size);
  #endif

  return (pNewObject);
 }
/*===========================================================================
 *		End of Function AllocateMemory()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - char* CreateString (pString);
 *
 * For use when you always want a valid new string created. 
 * Attempts to allocate a new string and copies the contents of the given
 * string into it.  Throws an exception on allocation error and outputs
 * error message. Assumes the input string is valid (asserts if it isn't).
 *
 *=======================================================================*/
char* CreateString (const char* pString) {
  DEFINE_FUNCTION("CreateString(char*)");
  char*  pNewString;
  size_t NewSize;

	/* Ensure valid input */
  ASSERT(pString != NULL);

	/* Get the length of the new string and allocate it */
  NewSize = strlen(pString) + 1;
  CreateArrayPointer(pNewString, char, NewSize);

	/* Copy the string to the new location */
  strcpy(pNewString, pString);
  return (pNewString);
 }
/*=========================================================================
 *		End of Function CreateString()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - char* CreateString (StringSize);
 *
 * For use when you always want a valid new string created. 
 * Same as CreateString() above but creates a string pointer of 
 * the given length (plus one for the terminating NULL).  String is
 * initially set to the empty string.
 *
 *=======================================================================*/
char* CreateString (const size_t StringSize) {
  //DEFINE_FUNCTION("CreateString(size_t)");
  char* pNewString;

	/* Allocate the new string pointer */
  pNewString = (char *) AllocateMemory(StringSize + 1);

  *pNewString = NULL_CHAR;
  pNewString[StringSize] = NULL_CHAR;
  return (pNewString);
 }
/*=========================================================================
 *		End of Function CreateString()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - boolean CreateString (ppNewString, pSourceString);
 *
 * Attempts to create a new string from the input string, returning the
 * result in the ppNewString pointer.  Returns TRUE on success or FALSE
 * on any error (source string is NULL).  Throws an exception if the
 * string could not be allocated.
 *
 *=========================================================================*/
boolean CreateString (char** ppNewString, const char* pSourceString) {
  DEFINE_FUNCTION("CreateString(char**, char*)");

	/* Ensure valid input */
  ASSERT(ppNewString != NULL);

	/* Special case for a NULL input string */
  if (pSourceString == NULL) {
    *ppNewString = NULL;
    return (FALSE);
   }

	/* Create the new string */
  *ppNewString = CreateString(pSourceString);
  return (TRUE);
 }
/*===========================================================================
 *		End of Function CreateString()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean CreateString (ppNewString, StringSize);
 *
 * Attempts to create a new string from the given size, returning the
 * result in the ppNewString pointer.  Returns TRUE on success or FALSE
 * on any error (source string is NULL).  Throws an exception if the
 * string could not be allocated.
 *
 *=========================================================================*/
boolean CreateString (char** ppNewString, const size_t StringSize) {
  DEFINE_FUNCTION("CreateString(char**, size_t)");

	/* Ensure valid input */
  ASSERT(ppNewString != NULL);

	/* Create the new string */
  *ppNewString = CreateString(StringSize);
  return (TRUE);
 }
/*===========================================================================
 *		End of Function CreateString()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - boolean GetFreeMemory (Memory);
 *
 * Returns the number of bytes available for allocation.
 * Returns FALSE on any error (if the amount of memory could not 
 * be retrieved).
 *
 *=======================================================================*/
boolean GetFreeMemory (long& Memory) {
  DEFINE_FUNCTION("GetFreeMemory()");

/*---------- Borland 16 bit ---------------------------------------------*/
#if defined (__BCPLUSPLUS__)
  Memory = 0;
  return (TRUE);

/*---------- DOS real mode implementation -------------------------------*/
#elif defined (__MSDOS__)
  Memory = farcoreleft();
  return (TRUE);


/*---------- Windows implementation -------------------------------------*/
#elif defined(_WIN32)
  MEMORYSTATUS Status;

  GlobalMemoryStatus(&Status);
  Memory = (long) Status.dwAvailVirtual; 
  return (TRUE);

	
/*---------- Any unknown system implementation --------------------------*/
#else
  ASSERT(FALSE);
  return (FALSE);
#endif
 }
/*=========================================================================
 *		End of Function GetFreeMemory()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean GetTotalMemory (Memory)
 *
 * Returns the total amount of memory on the system in bytes. 
 * On error the function returns FALSE.
 *
 *=======================================================================*/
boolean GetTotalMemory (long& Memory) {
  DEFINE_FUNCTION("GetTotalMemory()");

/*---------- Borland 16 bit ---------------------------------------------*/
#if defined (__BCPLUSPLUS__)
  Memory = 0;
  return (TRUE);

/*---------- DOS real mode implementation -------------------------------*/
#elif defined(__TURBOC__)
  struct heapinfo    HeapInfo;
  long               MemorySize = 0l;


	/* Make sure the heap is not corrupt */
  if (heapcheck() != _HEAPOK) return(FALSE);
  GetFreeMemory(MemorySize);

	/* Walk through the heap counting used and free blocks */
  HeapInfo.ptr = NULL;

  while (heapwalk(&HeapInfo) == _HEAPOK) {
    if (HeapInfo.in_use) MemorySize += HeapInfo.size;
   }

  return (TRUE);

/*---------- Windows implementation -------------------------------------*/
#elif defined(_WIN32)
  MEMORYSTATUS Status;

  GlobalMemoryStatus(&Status);
  Memory = (long) Status.dwAvailVirtual; 
  return (TRUE);

/*---------- Any unknown system implementation --------------------------*/
#else
  ASSERT(FALSE);
  return (FALSE);
#endif
 }
/*=========================================================================
 *		End of Function GetTotalMemory()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean GetUsedMemory (Memory);
 *
 * Returns the amount of allocated memory in bytes.  On error the function
 * returns FALSE.
 *
 *=======================================================================*/
boolean GetUsedMemory (long& Memory) {
  //DEFINE_FUNCTION("GetUserMemory()");
  long    MemoryFree = 0;
  long    MemoryTotal;
  boolean Result;

	/* Get the total/free memory on the system, ensuring they are valid */
  Result = GetTotalMemory(MemoryTotal);
  if (Result) Result = GetFreeMemory(MemoryFree);
  if (!Result) return (FALSE);

	/* Compute the difference in memory */
  Memory = MemoryTotal - MemoryFree;
  return (TRUE);
 }
/*=========================================================================
 *		End of Function GetUsedMemory()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - int GetHeapStatus (void);
 *
 * Checks and returns the validity of the heap.  Return values include
 *	HEAP_OK		- Heap is ok.
 *      HEAP_EMPTY	- Heap doesn't exist or not initialized..
 *	HEAP_CORRUPT	- Heap is corrupt.
 *	HEAP_NOTDEFINED - No defined heap status function on current system.
 *
 * Not all values are defined in all systems.
 *
 *=======================================================================*/
int GetHeapStatus (void) {
  DEFINE_FUNCTION("GetHeapStatus()");

/*---------- Borland 16 bit ---------------------------------------------*/
#if defined (__BCPLUSPLUS__)
  return (HEAP_NOTDEFINED);

/*---------- DOS real mode implementation -------------------------------*/
#elif defined(__MSDOS__)
  return (heapcheck());

/*---------- Windows implementation -------------------------------------*/	
#elif defined(_WIN32)

  #if defined(_DEBUG)
    if (_CrtCheckMemory()) return (HEAP_OK);
    return (HEAP_CORRUPT);
  #else
    return (HEAP_NOTDEFINED);
  #endif

/*---------- Any unknown system implementation --------------------------*/
#else
  ASSERT(FALSE);
  return (HEAP_NOTDEFINED);
#endif
 }
/*=========================================================================
 *		End of Function GetHeapStatus()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - char* GetHeapStatusString (void);
 *
 * Returns a string based on the status of the heap.  Always returns a
 * valid string, never NULL.
 *
 *=======================================================================*/
char *GetHeapStatusString (void) {

  switch (GetHeapStatus()) {
    case HEAP_OK:	  return ("Ok");
    case HEAP_CORRUPT:    return ("Corrupt");
    case HEAP_EMPTY:      return ("Empty or not Initialized" );
    case HEAP_NOTDEFINED: return ("Status Not Defined in Current System");
    default:              return ("Error, Invalid Heap Status");
   }
 }
/*=========================================================================
 *		End of Function GetHeapStatusString()
 *=======================================================================*/


#if defined(_WIN32) && defined(_DEBUG) && !defined(__BCPLUSPLUS__)
/*===========================================================================
 *
 * Function - void DebugHeapDumpHook (pUserData, Size);
 *
 * The hook function for dumping a block of the debug heap.  Only defined
 * in DEBUG and WIN32 builds.  Outputs the block information to the
 * SystemLog.
 *
 *=========================================================================*/
void __cdecl DebugHeapDumpHook (void* pUserData, size_t Size) {
  //DEFINE_FUNCTION("__cdecl DebugHeapDumpHook()");
  SystemLog.Printf ("Block %p (%u bytes)", pUserData, Size);
 }
/*===========================================================================
 *		End of Function __cdecl DebugHeapDumpHook()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int DebugHeapReportHook (ReportType, pMessage, pReturnValue);
 *
 * The hook function for reporting information of the debug heap.  Only 
 * defined in DEBUG and WIN32 builds.  Outputs the block information to the
 * SystemLog.
 *
 *=========================================================================*/
int DebugHeapReportHook (int ReportType, char* pMessage, int* pReturnValue) {
  //DEFINE_FUNCTION("DebugHeapReportHook()");
  static char* ReportStrings[] = {"Warning", "Error", "Assertion"};
  boolean      Result;
 
	/* Set return value to continue program operation.  Set this
	 * value to 1 to start the debugger. */
  *pReturnValue = 0;

	/* Attempt to output message to log file */
  Result = SystemLog.Printf("Debug Heap Report %s: %s", ReportStrings[ReportType], pMessage);
  return (Result);
 }
/*===========================================================================
 *		End of Function DebugHeapReportHook()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void fnInitDebugHeap (void);
 *
 * Initializes the use of the debug heap for error checking.  Only defined
 * in DEBUG and WIN32 builds.  
 *
 *=========================================================================*/
void fnInitDebugHeap (void) {
  //DEFINE_FUNCTION("fnInitDebugHeap()");

	/* Set the default debug heap options */
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF);

	/* Set the debug heap hook functions */
  _CrtSetDumpClient( DebugHeapDumpHook );
  _CrtSetReportHook( DebugHeapReportHook );
 }
/*===========================================================================
 *		End of Function fnInitDebugHeap()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void fnSetDebugHeapFreeMem (Flag);
 *
 * Sets or clears the option of keeping freed blocks allocated for testing
 * of block overwrites.  Only defined in DEBUG and WIN32 builds.
 *
 *=========================================================================*/
void fnSetDebugHeapFreeMem (const boolean Flag) {
  //DEFINE_FUNCTION("fnSetDebugHeapFreeMem()");
  
	/* Clear or set the storing of freed memory blocks */
  if (Flag)
    SET_CRT_DEBUG_FIELD(_CRTDBG_DELAY_FREE_MEM_DF);
  else
    CLEAR_CRT_DEBUG_FIELD(_CRTDBG_DELAY_FREE_MEM_DF);

 }
/*===========================================================================
 *		End of Function fnSetDebugHeapFreeMem()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void fnSetDebugHeapCheckCRT (Flag);
 *
 * Sets or clears the option of including CRT allocated blocks when checking
 * the status of the debug heap. Only defined in DEBUG and WIN32 builds.
 *
 *=========================================================================*/
void fnSetDebugHeapCheckCRT (const boolean Flag) {
  //DEFINE_FUNCTION("fnSetDebugHeapCheckCRT()");
  
	/* Clear or set the checking of CRT allocated blocks */
  if (Flag)
    SET_CRT_DEBUG_FIELD(_CRTDBG_CHECK_CRT_DF);
  else
    CLEAR_CRT_DEBUG_FIELD(_CRTDBG_CHECK_CRT_DF);

 }
/*===========================================================================
 *		End of Function fnSetDebugHeapCheckCRT()
 *=========================================================================*/
 

/*===========================================================================
 *
 * Function - void fnSetDebugHeapAlwaysCheck (Flag);
 *
 * Sets or clears the option of checking the heap status whenever an 
 * allocation is made.  Setting this flag can slow the application down
 * considerably but can help in pinpointing difficult to locate memory
 * problems. Only defined in DEBUG and WIN32 builds.
 *
 *=========================================================================*/
void fnSetDebugHeapAlwaysCheck (const boolean Flag) {
  //DEFINE_FUNCTION("fnSetDebugHeapAlwaysCheck()");
  
	/* Clear or set the checking of the heap when allocated blocks */
  if (Flag)
    SET_CRT_DEBUG_FIELD(_CRTDBG_CHECK_ALWAYS_DF);
  else
    CLEAR_CRT_DEBUG_FIELD(_CRTDBG_CHECK_ALWAYS_DF);

 }
/*===========================================================================
 *		End of Function fnSetDebugHeapAlwaysCheck()
 *=========================================================================*/
#endif


/*=========================================================================
 *
 * Function - void* memsearch (pBuffer, pSearchBuffer, BufferLength, 
			       SearchLength, StartIndex);
 *
 * Searches for the search bytes in given string using a 'binary' search
 * using the given lengths.  Starts searching in the target memory at
 * the given index.  Returns a pointer to the position of the search bytes
 * in the target bytes.  ASSERTs if given invalid input.
 *
 *=======================================================================*/
char* memsearch (const char* pBuffer, const char* pSearchBuffer, 
	         const size_t BufferLength, const size_t SearchLength, 
		 const size_t StartIndex) {
  DEFINE_FUNCTION("memsearch()");
  size_t BufferIndex;		/* Loop counters */
  size_t SearchIndex;

	/* Ensure valid input */
  ASSERT(pBuffer != NULL && pSearchBuffer != NULL);
  
	/* Ensure string lengths allow a possible match */
  if (BufferLength < StartIndex ||
      SearchLength == 0		||
      SearchLength > BufferLength - StartIndex) return (NULL);

  BufferIndex = StartIndex;
  SearchIndex = 0;

	/* The main search loop */
  while (BufferIndex < BufferLength) {

    if (pBuffer[BufferIndex] == pSearchBuffer[SearchIndex]) {
      SearchIndex++;
      if (SearchIndex >= SearchLength) return (char*)(pBuffer + BufferIndex - SearchLength + 1);
     }
    else if (SearchIndex != 0) {
      BufferIndex -= SearchIndex;
      SearchIndex = 0;
     }

    BufferIndex++;
   }

	/* Nothing found... */
  return (NULL);
 }
/*=========================================================================
 *		End of Function memsearch()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - void* memisearch (pBuffer, pSearchBuffer, BufferLength, 
				SearchLength, StartIndex);
 *
 * Searches for the search bytes in given string using a 'binary' search
 * using the given lengths.  Starts searching in the target memory at
 * the given index.  Returns a pointer to the position of the search bytes
 * in the target bytes.  ASSERTs if given invalid input. Case insensitive.
 *
 *=======================================================================*/
char* memisearch (const char* pBuffer, const char* pSearchBuffer, 
	          const size_t BufferLength, const size_t SearchLength, 
		  const size_t StartIndex) {
  DEFINE_FUNCTION("memisearch()");
  size_t BufferIndex;		/* Loop counters */
  size_t SearchIndex;

	/* Ensure valid input */
  ASSERT(pBuffer != NULL && pSearchBuffer != NULL);
  
	/* Ensure string lengths allow a possible match */
  if (BufferLength < StartIndex || 
      SearchLength == 0		||
      SearchLength > BufferLength - StartIndex) return (NULL);

  BufferIndex = StartIndex;
  SearchIndex = 0;

	/* The main search loop */
  while (BufferIndex < BufferLength) {

    if (tolower(pBuffer[BufferIndex]) == tolower(pSearchBuffer[SearchIndex])) {
      SearchIndex++;
      if (SearchIndex >= SearchLength) return (char*)(pBuffer + BufferIndex - SearchLength + 1);
     }
    else if (SearchIndex != 0) {
      BufferIndex -= SearchIndex;
      SearchIndex = 0;
     }

    BufferIndex++;
   }

	/* Nothing found... */
  return (NULL);
 }
/*=========================================================================
 *		End of Function memisearch()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - boolean ReplaceString (ppNewString, pSourceString);
 *
 * Same as CreateString(char**, char*) except that the destrination string
 * is deleted if it is non-NULL.  Returns TRUE on success or FALSE
 * on any error (source string is NULL).  Throws an exception if the
 * string could not be allocated.  Ignores if the input and output
 * strings point to the same object.
 *
 *=========================================================================*/
boolean ReplaceString (char** ppNewString, const char* pSourceString) {
  DEFINE_FUNCTION("ReplaceString(char**, char*)");

	/* Ensure valid input */
  ASSERT(ppNewString != NULL);

  	/* Check for NULL inputs */
  if (*ppNewString == NULL && pSourceString == NULL) return (FALSE);

	/* Check for same input/output objects */
  if (*ppNewString == pSourceString) return (TRUE);

	/* Delete the destination string if required */
  DestroyPointer(*ppNewString);

	/* Attempt to allocate the new stirng */
  return CreateString(ppNewString, pSourceString);
 }
/*===========================================================================
 *		End of Function ReplaceString()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean ReplaceString (ppNewString, Length);
 *
 * Same as CreateString(char**, size_t) except that the destrination string
 * is deleted if it is non-NULL.  Returns TRUE on success or FALSE
 * on any error (source string is NULL).  Throws an exception if the
 * string could not be allocated.
 *
 *=========================================================================*/
boolean ReplaceString (char** ppNewString, const size_t Length) {
  DEFINE_FUNCTION("ReplaceString(char**, size_t)");

	/* Ensure valid input */
  ASSERT(ppNewString != NULL);

	/* Delete the destination string if required */
  DestroyPointer(*ppNewString);

	/* Attempt to allocate the new stirng */
  return CreateString(ppNewString, Length);
 }
/*===========================================================================
 *		End of Function ReplaceString()
 *=========================================================================*/


#if defined(__TURBOC__) && !defined(__BCPLUSPLUS__)
/*===========================================================================
 *
 * Function - void _DosMemDumpHeap (void);
 *
 * Dumps the heap blocks to the SystemLog under TurboC.
 *
 *=========================================================================*/
void _DosMemDumpHeap (void) {
  struct heapinfo  HeapInfo;
  long             MemorySize = 0l;
  long             UnusedSize = 0l;
  long		   Index = 1l;

  SystemLog.Printf ("Outputting DOS heap block information...");

	/* Make sure the heap is not corrupt */
  if (heapcheck() != _HEAPOK) {
    SystemLog.Printf ("\t\tHeap is corrupt, cannot output block information!");
    return;
   }

	/* Walk through the heap counting used and free blocks */
  HeapInfo.ptr = NULL;

  while (heapwalk(&HeapInfo) == _HEAPOK) {
    if (HeapInfo.in_use)
      MemorySize += HeapInfo.size;
    else
      UnusedSize += HeapInfo.size;

    SystemLog.Printf ("\t\t%3ld) %p: %8ld bytes,  Used=%1d ", Index, HeapInfo.ptr, HeapInfo.size, HeapInfo.in_use);
    Index++;
   }

  SystemLog.Printf ("\tOutput %ld bytes of used and %ld bytes of unused heap blocks.", MemorySize, UnusedSize);

	/* Chain to our custom block handler in debug builds */
  #if defined(_DEBUG)
    OutputBlockInfo();
  #endif

  return;
 }
/*===========================================================================
 *		End of Function _DosMemDumpHeap()
 *=========================================================================*/
#endif


/*===========================================================================
 *
 * Begin File Test Routines (Debug Builds Only)
 *
 *=========================================================================*/
#if defined(_DEBUG)


#define CREATESTRING1_BUFFERSIZE 11000

	/* Turn off compiler warning options */
#if defined(__BCPLUSPLUS__)
  #pragma warn -rch
  #pragma warn -ccc
#endif

/*===========================================================================
 *
 * Test the CreateString (char*) function
 *
 * 1. Tests the allocation of set strings.
 * 2. Tests the allocation of random sized strings (from 1 to 10000 bytes).
 *
 *=========================================================================*/
void Test_CreateString1 (void) {
  DEFINE_FUNCTION("Test_CreateString1()");
  char*  Buffer;
  char*  pTestStrings[4] = { "", "\0", "testing", "asdadasd" };
  char*  pTestPtr;
  int    LoopCounter;
  size_t RandomSize;

	/* Test the set string allocation */
  SystemLog.Printf (stderr, "========== Testing CreateString(char*) with set strings ==========");

  	/* Allocate because large stack strings mess up in Borland 16-bit */
  Buffer = CreateString(CREATESTRING1_BUFFERSIZE);

  for (LoopCounter = 0; LoopCounter < 4; LoopCounter++) {
    pTestPtr = CreateString(pTestStrings[LoopCounter]);

		/* Ensure the string was properly allocated */
    ASSERT(pTestPtr != NULL);
    ASSERT(strcmp(pTestStrings[LoopCounter], pTestPtr) == 0);
    ASSERT(IsValidPointer(pTestPtr, strlen(pTestStrings[LoopCounter]) + 1));

		/* Delete the test string */
    DestroyPointer(pTestPtr);
    SystemLog.Printf (stderr, "\t\tSuccessfully allocated string '%s'.", pTestStrings[LoopCounter]);
   }

	/* Test the random string allocation */
  SystemLog.Printf (stderr, "========== Testing CreateString(char*) with random length strings ==========");
  srand( (unsigned)time( NULL ) );
  memset(Buffer, 'a', CREATESTRING1_BUFFERSIZE);

  for (LoopCounter = 0; LoopCounter < 1000; LoopCounter++) {
    RandomSize = (size_t) ((float)rand() * 10000 / RAND_MAX);
    Buffer[RandomSize] = NULL_CHAR;
    pTestPtr = CreateString(Buffer);

		/* Ensure the string was properly allocated */
    ASSERT(pTestPtr != NULL);
    ASSERT(strcmp(pTestPtr, Buffer) == 0);
    ASSERT(IsValidPointer(pTestPtr, RandomSize + 1));

		/* Delete the test string */
    DestroyPointer(pTestPtr);
    SystemLog.Printf (stderr, "\t\t%7d) Successfully allocated string of length %u bytes.", LoopCounter, RandomSize);
    Buffer[RandomSize] = 'a';
   }

  ASSERT(DebugHeapCheckMemory());
  DestroyPointer(Buffer);
 }
/*===========================================================================
 *		End of testing the CreateString(char*) function
 *=========================================================================*/


/*===========================================================================
 *
 * Test the CreateString(size_t) function
 *
 * 1. Tests the allocation of strings of set sizes.
 * 2. Tests the allocation of random sized strings (from 1 to
 *    TEST_MAXSTRING_SIZE bytes).
 *
 *=========================================================================*/
void Test_CreateString2 (void) {
  DEFINE_FUNCTION("Test_CreateString2()");
  size_t TestSizes[4] = { 10, 1, 2, 200 };
  size_t RandomSize;
  int    LoopCounter;
  char*  pTestPtr;

	/* Test the allocation of set sized strings */
  SystemLog.Printf(stderr, "========== Testing CreateString(size_t) with set sized strings ==========");

  for (LoopCounter = 0; LoopCounter < 4; LoopCounter++) {
    pTestPtr = CreateString(TestSizes[LoopCounter]);

		/* Ensure the string was properly allocated */
    ASSERT(pTestPtr != NULL);
    ASSERT(IsValidPointer(pTestPtr, TestSizes[LoopCounter] + 1));

    DestroyPointer(pTestPtr);
    SystemLog.Printf (stderr, "\t\tSuccessfully allocated string of %u bytes.", TestSizes[LoopCounter]);
   }

	/* Test the allocation of randomly sized strings */
  SystemLog.Printf (stderr, "========== Testing CreateString(size_t) with randomly sized strings ==========");
  srand( (unsigned)time( NULL ) );


  for (LoopCounter = 0; LoopCounter < 1000; LoopCounter++) {
    RandomSize = (size_t) ((float)((size_t)rand()) * TEST_MAXSTRING_SIZE / RAND_MAX);
    pTestPtr = CreateString(RandomSize);

		/* Ensure the string was properly allocated */
    ASSERT(pTestPtr != NULL);
    ASSERT(IsValidPointer(pTestPtr, RandomSize + 1));

    DestroyPointer(pTestPtr);
    SystemLog.Printf (stderr, "\t\t%7d) Successfully allocated string of %u bytes.", LoopCounter+1, RandomSize);
   }

  ASSERT(DebugHeapCheckMemory());
 }
/*===========================================================================
 *		End of testing the CreateString(size_t) function
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_CreateString3 (void);
 *
 * Tests the CreateString(char**, char*) function.  Since this function
 * is based on the CreateString(char*) function, some tests can be skipped.
 *	1. Tests for NULL source string
 *	2. Check allocation of set strings 
 *
 *=========================================================================*/
void Test_CreateString3 (void) {
  DEFINE_FUNCTION("Test_CreateString3()");
  char* pStrings[4] = { "asldkja;lkdj;asd", "123785", "", "o[ijopiap98dfp9hsadfp98ashf9as8fh" };
  char* pTestString1 = NULL;
  int   LoopCounter;

  SystemLog.Printf(stdout, "================ Testing CreateString(char**, char*) ===================");

	/* Test case of NULL source string */
  ASSERT(CreateString(&pTestString1, (char*)NULL) == FALSE);
  ASSERT(pTestString1 == NULL);

	/* Check the allocation of set strings */
  for (LoopCounter = 0; LoopCounter < 4; LoopCounter++) {
    ASSERT(CreateString(&pTestString1, pStrings[LoopCounter]) == TRUE);
    ASSERT(strcmp(pTestString1, pStrings[LoopCounter]) == 0);
    DestroyPointer(pTestString1);
   }

  ASSERT(DebugHeapCheckMemory());
 }
/*===========================================================================
 *		End of Function Test_CreateString3()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - void Test_memsearch (void);
 *
 * Tests the memsearch() and memisearch() functions.
 *	1. Tests for typical buffers
 *	2. Tests search buffer longer than buffer
 *	3. Tests empty buffers
 *	4. Tests overlapping data buffers
 *
 *=========================================================================*/
void Test_memsearch (void) {
  DEFINE_FUNCTION("Test_memsearch()");
  char  TestBuffer[101] = "Test buffer 1234567890";
  char  TestSearch[18] = "uff";
  char* pResult;

  SystemLog.Printf(stdout, "================ Testing memsearch()/memisearch() ===================");

	/* Test typical buffers */
  pResult = memsearch(TestBuffer, TestSearch, 20, 3, 0);
  ASSERT(strncmp(pResult, "uffer", 5) == 0);
  pResult = memisearch(TestBuffer, TestSearch, 20, 3, 0);
  ASSERT(strncmp(pResult, "uffer", 5) == 0);
  pResult = memsearch(TestBuffer, "aaa", 20, 3, 0);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, "aaa", 20, 3, 0);
  ASSERT(pResult == NULL);

  	/* Test typical buffers with start index */
  pResult = memsearch(TestBuffer, TestSearch, 20, 3, 4);
  ASSERT(strncmp(pResult, "uffer", 5) == 0);
  pResult = memisearch(TestBuffer, TestSearch, 20, 3, 4);
  ASSERT(strncmp(pResult, "uffer", 5) == 0);
  pResult = memsearch(TestBuffer, TestSearch, 20, 3, 14);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, TestSearch, 20, 3, 14);
  ASSERT(pResult == NULL);

  	/* Test typical buffers with case differences */
  strcpy(TestSearch, "UfF");
  pResult = memsearch(TestBuffer, TestSearch, 20, 3, 0);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, TestSearch, 20, 3, 0);
  ASSERT(strnicmp(pResult, "uffer", 5) == 0);

	/* Test search buffer longer than main buffer */
  pResult = memsearch(TestBuffer, TestSearch, 3, 20, 0);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, TestSearch, 3, 20, 0);
  ASSERT(pResult == NULL);

  	/* Test empty buffers */
  pResult = memsearch(TestBuffer, TestSearch, 20, 0, 0);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, TestSearch, 20, 0, 0);
  ASSERT(pResult == NULL);
  pResult = memsearch(TestBuffer, TestSearch, 0, 10, 0);
  ASSERT(pResult == NULL);
  pResult = memisearch(TestBuffer, TestSearch, 0, 10, 0);
  ASSERT(pResult == NULL);
 }
/*===========================================================================
 *		End of Function Test_memsearch()
 *=========================================================================*/



/*===========================================================================
 *
 * Function - void Test_ReplaceString (void);
 *
 * Tests the ReplaceString(char**, char*) function.  Is based on the 
 * CreateString(char**, char*) function which eliminates some tests.
 *	1. Tests empty/NULL destination strings 
 *	2. Check allocation of set strings 
 *
 *=========================================================================*/
void Test_ReplaceString (void) {
  DEFINE_FUNCTION("Test_ReplaceString()");
  char* pStrings[4] = { "asldkja;lkdj;asd", "123385", "", "o[ijopiap98dfp9hsadfp98ashf9as8fh" };
  char* pTestString1;
  int   LoopCounter;

  SystemLog.Printf(stdout, "================ Testing ReplaceString() ===================");
  
	/* Allocate temp string */
  pTestString1 = CreateString("aadkjsdoijsadfhsdpofihdpfaioh");
  
	/* Attempt to reallocate string */
  ASSERT(ReplaceString(&pTestString1, "1113233") == TRUE);

	/* Test allocation of NULL source string with non-NULL destination */
  ASSERT(ReplaceString(&pTestString1, (char*)NULL) == FALSE);
  ASSERT(pTestString1 == NULL);

	/* Test allocation of NULL source string with NULL destination */
  ASSERT(ReplaceString(&pTestString1, (char*)NULL) == FALSE);
  ASSERT(pTestString1 == NULL);

	  /* Check the allocation of set strings */
  for (LoopCounter = 0; LoopCounter < 4; LoopCounter++) {
    ASSERT(CreateString(&pTestString1, pStrings[LoopCounter]) == TRUE);
    ASSERT(strcmp(pTestString1, pStrings[LoopCounter]) == 0);
    DestroyPointer(pTestString1);
   }

  ASSERT(DebugHeapCheckMemory());
 }
/*===========================================================================
 *		End of Function Test_ReplaceString()
 *=========================================================================*/


/*===========================================================================
 *
 * Function  - void Test_DL_Mem (void);
 *
 * Runs the test routines for this file.
 *	- Tests the CreateString(char*) function
 *	- Tests the CreateString(size_t) function
 *	- Tests the CreateString(char**, char*) function
 *	- Tests the ReplaceString(char**, char*) function
 *	- Runs the GetFreeMemory(), GetUsedMemory() and GetTotalMemory() functions,
 *	  Outtputing results to log file, ASSERTing if any function returns an error.
 *	- Runs the GetHeapStatus() and GetHeapStatusString() functions, outputting
 *	  their results to the log file.
 *	- Tests the memsearch() and memisearch() functions
 *
 *=========================================================================*/
void Test_DL_Mem (void) {
  DEFINE_FUNCTION("Test_DL_Mem()");  
  long Memory;
  
  Test_CreateString1();
  Test_CreateString2();
  Test_CreateString3();
  Test_ReplaceString();
  Test_memsearch();

  ASSERT(GetFreeMemory(Memory) == TRUE);
  SystemLog.Printf("\tGetFreeMemory() returned %ld", Memory);

  ASSERT(GetUsedMemory(Memory) == TRUE);
  SystemLog.Printf("\tGetUsedMemory() returned %ld", Memory);

  ASSERT(GetTotalMemory(Memory) == TRUE);
  SystemLog.Printf("\tGetTotalMemory() returned %ld", Memory);

  SystemLog.Printf("\tGetHeapStatus() returned %ld", GetHeapStatus());
  SystemLog.Printf("\tGetHeapStatusString() returned '%s'", GetHeapStatusString());
 }
/*===========================================================================
 *		End of Function Test_DL_Mem()
 *=========================================================================*/

	/* Restore compiler warning options */
#if defined(__BCPLUSPLUS__)
  #pragma warn .rch
  #pragma warn .ccc
#endif

#endif
/*===========================================================================
 *		End of File Test Routines (Debug Builds Only)
 *=========================================================================*/