/****************************************************************************
**
**  Name:  heap.c
**
**  Description:
**     This module implements a simple heap manager for malloc/free style
**  memory management.  It works with FAR pointers, so pointers can be freely
**  shared between applications and DLLs.  Allocation size is limited to <64K.
**  The primary advantage to using this heap manager is that few window 
**  handles are used.
**
**  Status: TESTED
**
**  $Log:   S:/tbird/mt2_68k/malloc/heap.c_v  $
** 
**    Rev 1.0   13 Feb 1997 08:52:02   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 10:41:42   gene
** Initial revision.
** 
**    Rev 1.10   28 Jul 1993 13:33:54   ernie
** Added check for TMalloc(0).  This is illegal.
** 
**    Rev 1.9   04 Dec 1992 07:29:40   doug
** added _debugHeap command support
** 
**    Rev 1.8   12 Oct 1992 10:00:40   doug
** display error boxes when internal error and clean up error messages
** 
**    Rev 1.7   23 Sep 1992 10:19:44   courtney
** Took out old code (HeapGetErrorText, WEP stubbed out).
** 
**    Rev 1.6   08 Sep 1992 09:10:24   nghia
** Added heapDump() for Actor debug.
** 
**    Rev 1.5   30 Aug 1992 11:37:26   doug
** Added SetVerifyHeap() and GetVerifyHeap()
** 
**    Rev 1.4   29 Aug 1992 17:33:42   doug
** clean compile
** 
**    Rev 1.3   29 Aug 1992 16:52:04   kend
** Update for Sanity Checks
** 
**    Rev 1.2   14 May 1992 14:21:04   courtney
** Add call to ErrInitDLL to initialize with errtext dll.
** 
**    Rev 1.1   21 Mar 1992 12:26:32   kend
** added cleanup entry point
** 
**    Rev 1.0   19 Aug 1991 15:49:16   kend
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/malloc/heap.c_v   1.0   13 Feb 1997 08:52:02   gene  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
**  IMPLEMENTATION NOTES:
**  This implementation is a set of 64K heaps.  Within a heap, a boundary tag
**  scheme and first fit strategy is used (see "An Implementation of New and
**  Dispose using Boundary Tags", Branko J. Gerovac,  Pascal News # 19, 
**  September 1980).
**
**  Management of GlobalAlloc()'ed memory is done here.  Memory management
**  within individual 64K heap chunks is private to "chunk.c".
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#define _BORLANDC_
#ifndef  _BASEWIND_
#include "basewind.h"
#endif

#ifndef  _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef  _HEAP_
#include "heap.h"
#endif

#ifndef  _CHUNK_
#include "chunk.h"
#endif

#ifndef  _MALDEBUG_
#include "maldebug.h"
#endif

#ifndef  __STDIO_H
#include "stdio.h"
#endif


                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

				
/* Intel 64K Segments */
#define SEGMENT_SIZE	(64 * 1024L)

/* Tuning Parameter: # of 64KB segments to allocate at 1 time */
#define SEGS_PER_AREA  4
#define MAX_NUM_AREAS  8


typedef struct {
   U16          numSegsInArea ;   /* How many segments are in this area? */
   HANDLE       areaHandle ;      /* What is the "magic cookie?"         */
   U8 HUGE    * areaPtr ;         /* Where did Windows put it?           */
} AREA_INFO_TYPE ;

typedef U8 HUGE        * AREA_PTR      ;
typedef AREA_PTR         RANDOM_PTR    ;
typedef AREA_INFO_TYPE * AREA_INFO_PTR ;

#define NO_AREA_PTR     ((AREA_PTR)0)
#define NO_HANDLE	((HANDLE)0)
#define NO_SEGMENTS     0

#define ALLOC_FLAGS (GMEM_MOVEABLE | GMEM_ZEROINIT )


                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

#ifdef DEBUG
U32     amountAllocated = 0L ;
U32     amountFreed     = 0L ;
BOOLEAN dynamicVerify   = FALSE ;
BOOLEAN debugHeap       = FALSE ;
#endif

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

PRIVATE RETCODE AllocArea(   AREA_INFO_PTR areaInfoPtr ) ;

PRIVATE RETCODE FindSegment( RANDOM_PTR memAddress, 
			     U16 * areaInfoIndex,  /* which area? */
			     U16 * segmentIndex    /* which seg in area? */
		            ) ;

PRIVATE RETCODE FreeArea(    AREA_INFO_PTR areaInfoPtr ) ;

PRIVATE RETCODE HeapInit( VOID ) ;


                       /****************************
                        *                          *
                        *     LOCAL VARIABLES      *
                        *                          *
                        ****************************/

/* Area information */
PRIVATE AREA_INFO_TYPE  areaInfo[ MAX_NUM_AREAS ] ;

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
/*
 *  STRATEGY:
 *
 *  The public interface to Malloc/Free is here (only).  When this
 * DLL is loaded, it GlobalAlloc()'s an Area.  Areas consist of 
 * SEGS_PER_AREA heaps of size SEGMENT_SIZE.  Memory is allocated out
 * of the 1st heap possible.  When CMalloc fails for all heaps within
 * an area, the next area is tried.  If no further areas exist, and
 * fewer than MAN_NUM_AREAS are allocated, a new area gets allocated.
 * If the new area cannot be allocated in full, we reduce the number
 * of segments and try again (until 1 segment).  If we still can't 
 * alloc, we are out of memory and the allocation fails.
 *
 *  Freeing memory is done by finding the proper heap/segment and giving
 * the task to CFree() {see chunk.c}.
 *
 *  To avoid stack scanning, etc. all memory GlobalAlloc()'ed here is
 * locked once and remains locked.  As we are only executing in the
 * "expanded" memory model, this should be no great hardship (Win 3.0).
 *
 *  Note that the maximum memory we can allocate is currently (see chunk.c)
 * 64K - (7 * 4) =  65508 bytes.
 *
 *  Area pointers are HUGE to avoid segment arithmetic problems, but all
 * pointers are cast to FAR (i.e. only FAR pointers are seen externally).
 *
 */


/****************************************************************************
**
**  AllocArea -- Allocate a maximal area of SEGS_PER_AREA..1 or fail.
**               N.B. also initialize the segments!
**
*****************************************************************************/
PRIVATE RETCODE AllocArea( AREA_INFO_PTR areaInfoPtr ) {
   U16 segsToAlloc ;
   U16 segIndex ;

   /* allocate maximally within range */

   for ( segsToAlloc = SEGS_PER_AREA; segsToAlloc > 0; segsToAlloc--) {

      areaInfoPtr->areaHandle =
	      GlobalAlloc(  ALLOC_FLAGS, (SEGMENT_SIZE * segsToAlloc) ) ;

      if ( areaInfoPtr->areaHandle != NULL ) { /* successful globAlloc */

        if ( ( areaInfoPtr->areaPtr = GlobalLock(areaInfoPtr->areaHandle) )
	     == NULL ) {

           GlobalFree( areaInfoPtr->areaHandle ) ;
           ErrDisplayError(ER_WINDOWS_MEMLOCK, FORCE_POPUP);
	   return( ER_WINDOWS_MEMLOCK ) ;
	}

	/* successful globLock -- init the segments */

	areaInfoPtr->numSegsInArea = segsToAlloc ;

	for ( segIndex = 0 ; segIndex < segsToAlloc; segIndex++ ) {

	   InitChunk( (LPSTR)(areaInfoPtr->areaPtr
					 + (segIndex * SEGMENT_SIZE))
		    ) ;
        }

        return( SUCCESS ) ;
     }
   }
   
   ErrDisplayError(ER_HEAP_OUT_OF_MEMORY, FORCE_POPUP);
   return( ER_HEAP_OUT_OF_MEMORY ) ;
}


/****************************************************************************
**
**  FindSegment -- Given a long pointer, find it's home heap segment.
**
*****************************************************************************/
PRIVATE RETCODE FindSegment( RANDOM_PTR memAddress, 
			     U16 * areaInfoIndex,  /* which area? */
			     U16 * segmentIndex    /* which seg in area? */
		            ) {
   U16      areaIndex ;
   AREA_PTR areaPtr   ;
   U32      minAddr   ;
   U32      memAddr   ;
   U32      maxAddr   ;

   memAddr = (U32)memAddress ;
   
   for ( areaIndex = 0; areaIndex < MAX_NUM_AREAS; areaIndex++ ) {
      if ( (areaPtr = areaInfo[areaIndex].areaPtr) != NO_AREA_PTR ) {

	 minAddr = (U32)areaPtr ;
	 maxAddr = (U32)(areaPtr +
			(SEGMENT_SIZE * areaInfo[areaIndex].numSegsInArea)) ;

	 /* Nota Bene: comparison of huge pointers fails!!! */

	 if ( (minAddr < memAddr) && (memAddr < maxAddr) ) {

	    /* found the proper area, return the segment */
	    *areaInfoIndex = areaIndex ;
	    *segmentIndex  = ( /* N.B.: div => "integer division" */
		    (memAddr - minAddr) / (SEGMENT_SIZE * 8)
			     ) ;
	    return( SUCCESS ) ;
	 }
      }
   }

   return( ER_HEAP_BAD_POINTER ) ;
}


/****************************************************************************
**
**  FreeArea -- GlobalFree() & otherwise scrub area clean.
**
*****************************************************************************/
PRIVATE RETCODE FreeArea( AREA_INFO_PTR areaInfoPtr ) {
   RETCODE rc ;
   
   
   rc = GlobalFree( areaInfoPtr->areaHandle ) ;

   /* reset in all cases -- how would we recover? */
   areaInfoPtr->numSegsInArea = NO_SEGMENTS ;
   areaInfoPtr->areaHandle    = NO_HANDLE   ;
   areaInfoPtr->areaPtr       = NO_AREA_PTR ;

   return( rc ) ;
}


/****************************************************************************
**
**  HeapDump -- provide heap usage information for debug.
**
*****************************************************************************/
RETCODE EXPORT HeapDump(U32 *memAllocated, U32 *memFreed) {

    *memAllocated = amountAllocated;
    *memFreed = amountFreed;
    return(GOOD);
}

/****************************************************************************
**
**  GetVerifyHeap
**
*****************************************************************************/
RETCODE EXPORT GetVerifyHeap(BOOLEAN *verify) {
   *verify = dynamicVerify;
   return( SUCCESS ) ;
}


/****************************************************************************
**
**  GetDebugHeap
**
*****************************************************************************/
RETCODE EXPORT GetDebugHeap(BOOLEAN *debug) {
   *debug = debugHeap;
   return( SUCCESS ) ;
}


/****************************************************************************
**
** FUNCTION: LibMain(HANDLE, WORD, WORD, LPSTR)
**
** PURPOSE:  Called by Windows when the DLL is loaded to perform
**           Library initialization.
**           In this case, it initializes the AreaInfo array and
**           GlobalAlloc()'s 1 area.
**
*****************************************************************************/

int FAR PASCAL LibMain(hModule, wDataSeg, cbHeapSize, lpszCmdLine)
HANDLE	hModule;
WORD    wDataSeg;
WORD    cbHeapSize;
LPSTR   lpszCmdLine;
{
   /* register with error text dll */
   ErrInitDLL(MODULE_HEAP, "malloc.dll");

   if ( HeapInit() == SUCCESS) {
       return( 1 ) ; /* success */
   } else {
       return( 0 ) ; /* failure */
   }
}
   

/****************************************************************************
**
**  HeapInit -- initialize the heap (internal)
**
*****************************************************************************/
PRIVATE RETCODE HeapInit( ) {
   U16 index;
    
    amountAllocated = 0L ;
    amountFreed     = 0L ;
    dynamicVerify   = FALSE ;
    debugHeap       = FALSE ;

    for (index = 0; index < MAX_NUM_AREAS; index++) {
	    
       areaInfo[ index ].numSegsInArea = NO_SEGMENTS ;
       areaInfo[ index ].areaHandle    = NO_HANDLE   ;
       areaInfo[ index ].areaPtr       = NO_AREA_PTR ;
    }

    return( AllocArea( &areaInfo[0] ) ) ;
}

/****************************************************************************
**
**  HeapClean -- free memory alloc'ed & reset
**
*****************************************************************************/
RETCODE EXPORT HeapClean( ) {
    
    U16 index;
    
    for (index = 0; index < MAX_NUM_AREAS; index++) {

       if ( areaInfo[ index ].areaHandle != NO_HANDLE ) {

          FreeArea( &areaInfo[ index ] ) ;
	  
       }
    }
   
    return( HeapInit() ) ;
}


/****************************************************************************
**
**  HeapVerify -- Check all areas for sanity
**
*****************************************************************************/
RETCODE EXPORT HeapVerify( ) {
   
   U16      areaIndex ;
   U16      segIndex  ;
   RETCODE  rc ;

   for ( areaIndex = 0; areaIndex < MAX_NUM_AREAS; areaIndex++ ) {
      if ( areaInfo[areaIndex].areaPtr != NO_AREA_PTR ) {
         for ( segIndex = 0; 
               segIndex < areaInfo[areaIndex].numSegsInArea;
	       segIndex++ ) {

	     if ((rc = CVerify(
		        (LPSTR)(areaInfo[areaIndex].areaPtr
				   + (segIndex * SEGMENT_SIZE))
	         )                )
  	         != SUCCESS) {
		     return( rc ) ;  /* error! */
	     }
         }
      }
   }

    return( SUCCESS ) ;
}

/****************************************************************************
**
**  TFree -- free memory alloc'ed via Malloc()
**
*****************************************************************************/
RETCODE EXPORT TFree( LPSTR randomPointer ) {
    U16     segIndex, areaIndex ;
    RETCODE rc ;
    
    if(dynamicVerify && ((rc = HeapVerify()) != SUCCESS)) return(rc);
    rc = FindSegment((RANDOM_PTR)randomPointer, &areaIndex, &segIndex);
    if(rc!=SUCCESS) {
      ErrDisplayError(rc, FORCE_POPUP);
      return( rc ) ;
    }
    rc = CFree((LPSTR)(areaInfo[areaIndex].areaPtr+(segIndex * SEGMENT_SIZE)),
               (LPSTR)randomPointer);
    if(debugHeap) {
       S8 buff[150];
       sprintf(buff, "TFree(%p), retcode %ld", randomPointer, rc);
       SendMessageToCli((LPSTR)buff);
    }
    return(rc);
}


/****************************************************************************
**
**  TMalloc -- allocate numBytes of memory or fail
**
*****************************************************************************/

LPSTR EXPORT TMalloc( U32 numBytes ) {
   U16      areaIndex ;
   U16      segmentIndex ;
   LPSTR    aPtr ;
   if( !numBytes ) return((LPSTR)0); // numBytes==0 is an invalid request.
   if(dynamicVerify && (HeapVerify() != SUCCESS)) return((LPSTR)0);
   if( numBytes>MAX_ALLOC_SIZE ) { /* MAX_ALLOC_SIZE is def'ed in chunk.h */
      ErrDisplayError(ER_HEAP_REQUEST_TOO_BIG, FORCE_POPUP);
      return((LPSTR)0); 
   }

   /* See if we can find some memory for our client */
   
   for ( areaIndex = 0; areaIndex < MAX_NUM_AREAS; areaIndex++ ) {
      if ( areaInfo[areaIndex].areaPtr != NO_AREA_PTR ) {
         for ( segmentIndex = 0; 
               segmentIndex < areaInfo[areaIndex].numSegsInArea;
               segmentIndex++ ) {

            aPtr = CMalloc( (LPSTR)(areaInfo[areaIndex].areaPtr
                            + (SEGMENT_SIZE * segmentIndex)),
                            (U16)numBytes ) ;

            if(aPtr!=NULL) goto TMALLOC_SUCCESS;
         }
      }
   }

   /* No memory found.  See if we can get some more */

   for ( areaIndex = 0; areaIndex < MAX_NUM_AREAS; areaIndex++ ) {
      if ( areaInfo[areaIndex].areaPtr == NO_AREA_PTR ) {

         /* found a free slot */
         if ( AllocArea( &areaInfo[areaIndex] ) != SUCCESS ) {
            ErrDisplayError(ER_HEAP_OUT_OF_MEMORY, FORCE_POPUP);
            goto TMALLOC_FAILURE;
         }
         /* allocated at least 1 segment ...
          * If the user can't get mem here, he is out of luck! 
          */
         aPtr = CMalloc((LPSTR)(areaInfo[areaIndex].areaPtr),(U16)numBytes);
         goto TMALLOC_DONE;
      }
   }
   ErrDisplayError(ER_HEAP_OUT_OF_MEMORY, FORCE_POPUP);
   goto TMALLOC_FAILURE;
TMALLOC_FAILURE:
   aPtr = (LPSTR)0; /* drop through */
TMALLOC_SUCCESS:
TMALLOC_DONE:
   if(debugHeap) {
       S8 buff[150];
       sprintf(buff, "TMalloc(%ld), return pointer %p", numBytes, aPtr);
       SendMessageToCli((LPSTR)buff);
    }
    return(aPtr);
}


/****************************************************************************
**
**  SetVerifyHeap
**
*****************************************************************************/
RETCODE EXPORT SetVerifyHeap(BOOLEAN verify) {
   dynamicVerify = verify ;
   return (SUCCESS) ;
}

/****************************************************************************
**
**  SetDebugeap
**
*****************************************************************************/
RETCODE EXPORT SetDebugHeap(BOOLEAN debug) {
   debugHeap = debug ;
   return (SUCCESS) ;
}


#ifndef _BORLANDC_
/****************************************************************************
**
**  FUNCTION:  WEP(int)  {Windows Exip Procedure}
**
**  PURPOSE:  Performs cleanup tasks when the DLL is unloaded.  WEP() is
**            said on the net to be useless (crashes Windows if used for
**            anything).  We shall see!
**
*****************************************************************************/
int FAR PASCAL WEP (bSystemExit)
int  bSystemExit;
{
   return( 1 ) ;	/* 1 => success; but what else can we do? */
}
#endif


/******************************** E O F ***********************************/
