/***************************************************************************
 *                                                                         *
 *   GALMEMDB.C                                                            *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   These are the GCOMM.LIB routines for memory allocation which also     *
 *   support debugging.                                                    *
 *                                                                         *
 *                                  - R. Stein & D. Pitchford 1/25/95      *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#ifdef GCDOSP
#include "phasedbg.h"
#endif // GCDOSP
#ifdef GCWINNT
#include <windows.h>
#endif // GCWINNT

#define FILREV "$Revision: 7 $"

#undef malloc                      /* undo #define malloc in gcomm.h       */
#undef free                        /* undo #define free in gcomm.h         */

#define GBGBYT  (0xA3)             /* garbage byte for uninitialized memory*/
#define DTHRESH 0xF000U            /* sel at which to avert specials       */

#ifdef DEBUG
GBOOL memdbg=TRUE;                 /* set FALSE to disable special sel dbg */
GBOOL alcdbg=TRUE;                 /* (memdbg) FALSE= low on selectors     */
#else
GBOOL memdbg=FALSE;                /* ditto                                */
GBOOL alcdbg=FALSE;                /* ditto                                */
#endif

ULONG nmalloc=0L;                  /* count malloc()'s                     */
ULONG nmfree=0L;                   /* count free()'s                       */
UINT lstalcsiz;                    /* size of last successful malloc()     */

#ifdef GCDOSP
ULONG nmskip=0L;                   /* skip this many initial malloc's      */
ULONG nmsctr=0L;                   /* counter up to nmskip                 */
ULONG nmspec=0L;                   /* count of special malloc() selectors  */
ULONG nmbust=0L;                   /* count of special selectors averted   */
ULONG nmlinr=0L;                   /* number of linear conv errors         */
UINT lerr;                         /* latest cause of linear conv error    */
SEL lstspec;                       /* last special selector allocated      */
struct phasedbg lstalcphs;         /* phase info from last malloc() (DEBUG)*/

extern USHORT __base;              /* first selector of heap               */
extern USHORT __top;               /* last selector of heap (inclusive)    */
extern USHORT __hincr;             /* increment between heap selectors     */
#endif // GCDOSP

VOID *                             /*   returns ptr to newly alloc'd space */
galmalloc(                         /* Galacticomm's malloc() for debugging */
UINT size)                         /*   size to malloc()                   */
{
#ifdef GCWINNT
     VOID *rc;
     static GBOOL fFirstTime=TRUE;
     HINSTANCE hInst;
     static FARPROC pFunc=NULL;
     typedef VOID *(*MALLOCTYPE)(size_t);

     ASSERTM(size != 0,"Can't allocate zero bytes with malloc()!");
     if (fFirstTime) {
          fFirstTime=FALSE;
          if ((hInst=LoadLibrary("galmemdb.dll")) != NULL) {
               pFunc=GetProcAddress(hInst,"_galDebugMalloc");
          }
     }
     if (pFunc != NULL) {
          rc=((MALLOCTYPE)pFunc)(size);
     }
     else {
          rc=malloc(size);
     }
     if (rc != NULL) {
          nmalloc++;
          lstalcsiz=size;
     }
     return(rc);
#else

     VOID *rc;
#ifdef GCDOSP
#ifdef DEBUG
     SEL asel;
     DESC segdesc;
     UINT err;
     static GBOOL catalr=FALSE;
#endif // DEBUG
#endif // GCDOSP

     ASSERTM(size != 0,"Can't allocate zero bytes with malloc()!");
     rc=malloc(size);
     if (rc != NULL) {
          nmalloc++;
          lstalcsiz=size;
#ifdef GCDOSP
#ifdef DEBUG
          setmem(rc,size,GBGBYT);
          movmem(pstphs(0),&lstalcphs,sizeof(struct phasedbg));
          if (memdbg && !catalr) {
               asel=FP_SEG(rc);
               if (nmsctr < nmskip) {
                    nmsctr++;
               }
               else if (alcdbg) {
                    err=DosGetSegDesc(asel,&segdesc);
                    if (err != 0) {
                         memdbg=FALSE;
                         catalr=TRUE;
                         catastro("DosGetSegDesc returned %u on %04X",
                                       err,asel);
                    }
                    err=DosMapLinSeg(segdesc.base+FP_OFF(rc),(ULONG)size,
                                                              &lstspec);
                    if (err == 0) {
                         rc=MK_FP(lstspec,0);
                         nmspec++;
                         if (lstspec >= DTHRESH) {
                              alcdbg=FALSE;
                         }
                    }
                    else {
                         lerr=err;
                         nmlinr++;
                    }

               }
               else {
                    nmbust++;
               }
          }
#endif // DEBUG
#endif // GCDOSP
     }
     return(rc);
#endif // GCWINNT
}

VOID
galfree(                           /* Galacticomm's free() for debugging   */
VOID *block)                       /*   ptr to block to free()             */
{
#ifdef GCWINNT
     static GBOOL fFirstTime=TRUE;
     HINSTANCE hInst;
     static FARPROC pFunc=NULL;
     typedef VOID (*FREETYPE)(VOID *);

     ASSERTM(block != NULL,"Can't free() a NULL pointer!");
     if (fFirstTime) {
          fFirstTime=FALSE;
          if ((hInst=LoadLibrary("galmemdb.dll")) != NULL) {
               pFunc=GetProcAddress(hInst,"_galDebugFree");
          }
     }
     if (pFunc != NULL) {
          ((FREETYPE)pFunc)(block);
     }
     else {
          free(block);
     }
     nmfree++;
#else

#ifdef DEBUG
#ifdef GCDOSP
     UINT err;
     DESC segdesc,heapdesc;
     SEL dsel,hsel;
     static GBOOL catalr=FALSE;

     if (catalr) {
          return;
     }
#endif // GCDOSP
     ASSERTM(block != NULL,"Can't free() a NULL pointer!");
#ifdef GCDOSP
     if (FP_OFF(block) == 0) {
          err=DosGetSegDesc(dsel=FP_SEG(block),&segdesc);
          if (err != 0) {
               memdbg=FALSE;
               catalr=TRUE;
               catastro("Special DosGetSegDesc(%04X) returned %d",
                        dsel,err);
          }
          for (hsel=__base ; hsel <= __top ; hsel+=__hincr) {
               err=DosGetSegDesc(hsel,&heapdesc);
               if (err != 0) {
                    memdbg=FALSE;
                    catalr=TRUE;
                    catastro("Heap DosGetSegDesc(%04X) returned %d",
                             hsel,err);
               }
               if (segdesc.base >= heapdesc.base
                && segdesc.base <  heapdesc.base+0x10000UL) {
                    block=MK_FP(hsel,
                                (unsigned)(segdesc.base-heapdesc.base));
                    err=DosFreeSeg(dsel);
                    if (err != 0) {
                         memdbg=FALSE;
                         catalr=TRUE;
                         catastro("DosFreeSeg(%04X) returned %d",
                                  dsel,err);
                    }
                    break;
               }
          }
     }
#endif // GCDOSP
#endif // DEBUG
     free(block);
     nmfree++;
#endif // GCWINNT
}

VOID
memdbgrpt(                         /* report on special memory debugging   */
FILE *fp)                          /*   open file to report to             */
{
     fprintf(fp,"%lu memory blocks allocated.\n",nmalloc);
#ifdef GCDOSP
     if (nmsctr > 0L) {
          fprintf(fp,"%lu early allocations skipped high scrutiny.\n",
                     nmsctr);
     }
     if (nmspec > 0L) {
          fprintf(fp,"%lu memory allocations subjected to high scrutiny.\n",
                     nmspec);
          fprintf(fp,"last special selector: %04X\n",lstspec);
     }
     if (nmbust > 0L) {
          fprintf(fp,"%lu later allocations not scrutinized "
                     "(to avoid selectors over %04X).\n",nmbust,DTHRESH);
     }
     if (nmlinr > 0L) {
          fprintf(fp,"%lu allocations not scrutinized "
                     "(DosMapLinSeg errors, e.g. %d).\n",nmlinr,lerr);
     }
#endif // GCDOSP
     fprintf(fp,"%lu memory blocks freed.\n",nmfree);
}
