Metropoli BBS
VIEWER: alloc.c MODE: TEXT (ASCII)
/***
*alloc.c - disked malloc/free front-end.
*
*Copyright (c) 1993-1995, Gregg Jennings.  All wrongs reserved.
*   P O Box 200, Falmouth, MA 02541-0200
*
*Purpose:
*   Debugging support.
*
*Notice:
*   This progam may be freely used and distributed.  Any distrubution
*   with modifications must retain the above copyright statement and
*   modifications noted.
*   No pulp-publication, in whole or in part, permitted without
*   permission (magazines or books).
*******************************************************************************/

/*
   Version

   2.6   20-Dec-1994    converted error references to function calls
   2.5   18-Nov-1994    alloc_handler()
   2.4   11-Sep-1994    removed DISKED ifdefs
                        fixed heapstat()
   2.3   25-Feb-1994    hugealloc(),hugefree(), etc
   2.2   21-Feb-1994    fixed heapstat
   2.1   13-Jan-1994    Borland stuff, heaptest was heapcheck
   2.0   28-Nov-1993

   malloc() and free() front end or "wrappers".  This family of
   functions can be called explicitly (as DISKED does) or via
   macros:

      #define malloc(s)    alloc(1,s)
      #define free(p)      freep(p)
         etc.

   which will enable them to be used if needed for debugging.
   The heaptest() function can find bounds errors; just add
   this in your code to find it:

      if (heaptest() < 1)
         printf("%s%d",__FILE__,__LINE__);

   Notes:   This unit can be a separate unit.  The only globals
            referenced are the error message enums in ERROR.H.
            The ERROR.C error message calls can be removed
            without effect.

            Borland does not have a near heap in Large Memory model.
*/

#include <stdlib.h>
#include <malloc.h>

#include "alloc.h"
#include "error.h"

/* globals referenced here 

   error number definitions (enum ERROR_MSG, ERROR.H)

*/

/* globals defined here */

unsigned int blocks = 0;               /* statistics only */
unsigned int nblocks = 0;              /* used by DEBUG.C */
unsigned int hblocks = 0;
unsigned int frees = 0;
unsigned int nfrees = 0;
unsigned int hfrees = 0;


/* internal functions */

static void default_alloc_handler(void);


/***
*alloc_handler -  got the idea from C++, this is called
*                 for alloc failures
*
***/

void (*alloc_handler)(void) = default_alloc_handler;

/***
*default_alloc_handler -  handles memory allocation failure
*
*  So far only sets error message information.
*
****/

static void default_alloc_handler(void)
{
   set_err_num(ALLOC_FAIL);            /* set error number */
                                       /*  the module, function error */
                                       /*  arg are set by the caller */
   set_err_info("memavail: %lu",memavail()); /* set avail. mem. info */
}

/***
*set_alloc_handler   -  set the function to call upon malloc
*                       failure
*
****/

extern void set_alloc_handler(void (*func)(void))
{
   alloc_handler = (func) ? func : default_alloc_handler;
}

/***
*alloc   -  malloc/calloc replacement
*
****/

extern void *alloc(size_t num, size_t size)
{
void *t;

   if (size == 0)
   {
      set_err_num(ALLOC_ZERO);
      return NULL;
   }
   if ((t = calloc(num,size)) == NULL)
      alloc_handler();

   ++blocks;
   return t;
}

#ifndef __BORLANDC__

extern void _near *nalloc(size_t num, size_t size)
{
void _near *t;

   if (size == 0)
   {
      set_err_num(ALLOC_ZERO);
      return NULL;
   }
   if ((t = _ncalloc(num,size)) == NULL)
      alloc_handler();

   ++nblocks;
   return t;
}

#endif   /* !__BORLANDC__ */

extern void _huge *hugealloc(long num, long size)
{
void _huge *t;

   if (size == 0)
   {
      set_err_num(ALLOC_ZERO);
      return NULL;
   }

   if ((t = huge_alloc(num,size)) == NULL)
      alloc_handler();

   ++hblocks;
   return t;
}

/* realloc() */

extern void *newalloc(void *p, size_t size)
{
   if (p != NULL)
      freep(p);
   return alloc(1,size);
}

extern void freep(void *addr)
{
int h;

   if (addr != NULL)
   {
      free(addr);
      if ((h = _fheapset(254)) != _HEAPOK)
      {
         set_error(NULL,"alloc",HEAP_ERROR,"freep");
         set_err_arg(heapstat(h));
      }
      addr = NULL;
      ++frees;
   }
   else
      set_err_num(FREE_NULL);
}

#if !defined(__BORLANDC__)

extern void nfreep(void _near *addr)
{
int h;

   if (addr != NULL)
   {
      _nfree(addr);
      if ((h = _nheapset(254)) != _HEAPOK)
      {
         set_error(NULL,"alloc",HEAP_ERROR,"nfreep");
         set_err_arg(heapstat(h));
      }
      addr = NULL;
      ++nfrees;
   }
   else
      set_err_num(FREE_NULL);
}

#endif

extern void hugefreep(void _huge *addr)
{
   if (addr != NULL)
   {
      huge_free(addr);
      addr = NULL;
      ++hfrees;
   }
   else
      set_err_num(FREE_NULL);
}


/***
*heaptest()
*
*   putting calls to this throughout the program can help find were the
*   heap is getting corrupted:
*
*      if (heaptest() < 1)
*         printf("%s%d",__FILE__,__LINE__);
***/

extern int heaptest(void)
{
int h;

#if !defined(__BORLANDC__)
   if ((h = _fheapchk()) != _HEAPOK || (h = _nheapchk()) != _HEAPOK)
#else
   if ((h = _heapchk()) != _HEAPOK)
#endif
      return h;
   else
      return 1;
}

/* AS IN MALLOC.H:

constants for _heapchk/_heapset/_heapwalk routines

Borland:

#define _HEAPEMPTY      1     // 0
#define _HEAPOK         2     // 1
#define _HEAPEND        5     // 4
#define _HEAPCORRUPT   -1     // 5
#define _BADNODE       -2     // 2
#define _BADVALUE      -3     // 3

#define _FREEENTRY      3
#define _USEDENTRY      4

Microsoft:

#define _HEAPEMPTY      (-1)
#define _HEAPOK         (-2)           // _heapchk/_heapset only
#define _HEAPBADBEGIN   (-3)
#define _HEAPBADNODE    (-4)
#define _HEAPEND        (-5)           // _heapwalk only
#define _HEAPBADPTR     (-6)

#define _FREEENTRY      0
#define _USEDENTRY      1

Watcom:

#define _HEAPOK         0
#define _HEAPEMPTY      1  // heap isn't initialized
#define _HEAPBADBEGIN   2  // heap header is corrupted
#define _HEAPBADNODE    3  // heap entry is corrupted
#define _HEAPEND        4  // end of heap entries (_heapwalk)
#define _HEAPBADPTR     5  // invalid heap entry pointer (_heapwalk)

#define _FREEENTRY      1
#define _USEDENTRY      0


You can see the dificulty with these compiler library functions!
*/

/* heap status message - values are normalized to 0-N */

static char *heap_msg[] = {
#ifdef _MSC_VER
   "empty heap",
   "heap is fine",
   "bad start of heap",
   "bad node in heap",
   "end of heap",
   "bad pointer to heap",
   "bad heap",                /* unknown value */
#elif __WATCOMC__
   "heap is fine",
   "empty heap",
   "bad start of heap",
   "bad node in heap",
   "end of heap",
   "bad pointer to heap",
   "bad heap",                /* unknown value */
#elif __BORLANDC__
   "empty heap",
   "heap is fine",
   "bad node in heap",
   "bad pointer to heap",
   "end of heap",
   "bad heap",                /* unknown value */
#else
#error put Your compiler messages here
#endif
};

/* get heap status message */

extern char *heapstat(int status)
{
#ifdef _MSC_VER
   status = -status-1;     /* make offset into message array */
#elif defined(__BORLANDC__)
   if (status < 0)
   {
      if (status == -1)
         status = 5;
      else
         status = -status;
   }
   else
      --status;
#endif
   if (status < 0 || status > (sizeof(heap_msg)/sizeof(char *))-1)
      status = (sizeof(heap_msg)/sizeof(char *))-1;
   return heap_msg[status];
}
[ RETURN TO DIRECTORY ]