/******************************************************************************
*
*  NSSDC/CDF                                            Memory Functions.
*
*  Version 1.2b, 29-Oct-97, Hughes STX.
*
*  Modification history:
*
*   V1.0  17-May-95, J Love     Original version.
*                    J Williams
*   V1.1  25-Jul-95, J Love     Simple virtual memory function.
*   V1.1a  6-Sep-95, J Love     Added `nBytes' to MEMLOG_.
*   V1.1b 22-Sep-95, J Love     Fixed call to `FreeVMemory'.
*   V1.2  16-Aug-96, J Love	CDF V2.6.
*   V1.2a 28-Oct-96, J Love	Added `return' statement to cdf_FreeMemory for
*				when building for IDL.
*   V1.2b 29-Oct-97, J Love	More Windows NT.
*   V2.0  19-May-05, M Liu      Added "cdf_" to FreeMemory, AllocateMemory, 
*                               ReallocateMemory functions.
*
******************************************************************************/

#define CDFMEM
#include "cdflib.h"
#include "cdflib64.h"

#define MEMLOG		0
#define TO_STDOUT	0

/******************************************************************************
* MEM structures/typedef's/global variables.
******************************************************************************/

#if !defined(BUILDINGforIDL)
typedef struct memSTRUCT {	/* Structure. */
  void *ptr;
  struct memSTRUCT *next;
  size_t nBytes;
} MEM;
typedef MEM *MEMp;		/* Pointer (to structure). */
static MEMp memHeadP = NULL;	/* Head of memory linked list. */
#endif

/******************************************************************************
* Local function prototypes.
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
static Logical InitVMemory PROTOARGs((void));
static void TermVMemory PROTOARGs((void));
#endif

#if MEMLOG
static uLong TotalBytes PROTOARGs((MEMp memHeadP));
#endif

/******************************************************************************
* Debugging macros.
******************************************************************************/

#if MEMLOG
#if TO_STDOUT
#define ALLOCFAILED(type,which,nBytes) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "%sllocation FAILED [%d]: %lu bytes\n", \
	   type, which, (uLong) nBytes); \
  fprintf (stdout, "%sllocation FAILED [%d]: %lu bytes\n", \
	   type, which, (uLong) nBytes); \
  fclose (fp);}
#else
#define ALLOCFAILED(type,which,nBytes) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "%sllocation FAILED [%d]: %lu bytes\n", \
	   type, which, (uLong) nBytes); \
  fclose (fp);}
#endif
#else
#define ALLOCFAILED(type,which,nBytes) \

#endif

#if MEMLOG
#if TO_STDOUT
#define ALLOCSUCCESS(type,nBytes,ptr,totalBytes) \
  {FILE *fp = FOPEN("mem.log","a"); uLong totalBytesSave = totalBytes; \
  fprintf (fp, "%sllocated: %lu bytes at %p (%lu bytes total)\n", \
	   type, (uLong) nBytes, ptr, totalBytesSave); \
  fprintf (stdout, "%sllocated: %lu bytes at %p (%lu bytes total)\n", \
	   type, (uLong) nBytes, ptr, totalBytesSave); \
  fclose (fp);}
#else
#define ALLOCSUCCESS(type,nBytes,ptr,totalBytes) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "%sllocated: %lu bytes at %p (%lu bytes total)\n", \
	   type, (uLong) nBytes, ptr, totalBytes); \
  fclose (fp);}
#endif
#else
#define ALLOCSUCCESS(type,nBytes,ptr,totalBytes) \

#endif

#if MEMLOG
#if TO_STDOUT
#define FREESUCCESS(type,ptr,nBytes) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "Freed (%s): %p, %lu bytes\n", type, ptr, (uLong) nBytes); \
  fprintf (stdout, "Freed (%s): %p, %lu bytes\n", type, ptr, (uLong) nBytes); \
  fclose (fp);}
#else
#define FREESUCCESS(type,ptr,nBytes) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "Freed (%s): %p, %lu bytes\n", type, ptr, (uLong) nBytes); \
  fclose (fp);}
#endif
#else
#define FREESUCCESS(type,ptr,nBytes) \

#endif

#if MEMLOG
#if TO_STDOUT
#define FREEFAILED(ptr) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "Free failed [pointer not found]: %p\n", ptr); \
  fprintf (stdout, "Free failed [pointer not found]: %p\n", ptr); \
  fclose (fp);}
#else
#define FREEFAILED(ptr) \
  {FILE *fp = FOPEN("mem.log","a"); \
  fprintf (fp, "Free failed [pointer not found]: %p\n", ptr); \
  fclose (fp);}
#endif
#else
#define FREEFAILED(ptr) \

#endif

/******************************************************************************
* cdf_AllocateMemory.
******************************************************************************/

VISIBLE_PREFIX void *cdf_AllocateMemory (nBytes, fatalFnc)
size_t nBytes;
void (*fatalFnc) PROTOARGs((char *msg));
{
#if defined(BUILDINGforIDL)
  return malloc(nBytes);
#else
  MEMp mem;
  if (nBytes < 1) {
    ALLOCFAILED("A",1,nBytes)
    return NULL;
  }
  mem = (MEMp) malloc (sizeof(MEM));
  if (mem == NULL) {
    ALLOCFAILED("A",2,nBytes)
    if (fatalFnc != NULL) (*fatalFnc)("Unable to allocate memory buffer [1].");
    return NULL;
  }
  mem->ptr = (void *) malloc (nBytes);
  if (mem->ptr == NULL) {
    ALLOCFAILED("A",3,nBytes)
    free (mem);
    if (fatalFnc != NULL) (*fatalFnc)("Unable to allocate memory buffer [2].");
    return NULL;
  }
  mem->nBytes = nBytes;
  mem->next = memHeadP;
  memHeadP = mem;
  ALLOCSUCCESS("A",mem->nBytes,mem->ptr,TotalBytes(memHeadP))
  return mem->ptr;
#endif
}

/******************************************************************************
* cdf_ReallocateMemory.
******************************************************************************/

VISIBLE_PREFIX void *cdf_ReallocateMemory (ptr, nBytes, fatalFnc)
void *ptr;
size_t nBytes;
void (*fatalFnc) PROTOARGs((char *msg));
{
#if defined(BUILDINGforIDL)
  return realloc(ptr,nBytes);
#else
  MEMp mem = memHeadP;
  while (mem != NULL) {
    if (mem->ptr == ptr) {
      void *newPtr = (void *) realloc (ptr, nBytes);
      if (newPtr == NULL) {
	ALLOCFAILED("Rea",1,nBytes)
	if (fatalFnc != NULL) {
	  (*fatalFnc)("Unable to reallocate memory buffer [1].");
	}
	return NULL;
      }
      mem->ptr = newPtr;
      mem->nBytes = nBytes;
      ALLOCSUCCESS("Rea",mem->nBytes,mem->ptr,TotalBytes(memHeadP))
      return newPtr;
    }
    mem = mem->next;
  }
  ALLOCFAILED("Rea",2,nBytes)
  if (fatalFnc != NULL) (*fatalFnc)("Unable to reallocate memory buffer [2].");
  return NULL;
#endif
}

/******************************************************************************
* cdf_FreeMemory.
* If NULL is passed as the pointer to free, then free the entire list of
* allocated memory blocks.
******************************************************************************/

VISIBLE_PREFIX int cdf_FreeMemory (ptr, fatalFnc)
void *ptr;
void (*fatalFnc) PROTOARGs((char *msg));
{
#if defined(BUILDINGforIDL)
  free (ptr);
  return 1;
#else
  if (ptr == NULL) {
    int count = 0;
    MEMp mem = memHeadP;
    while (mem != NULL) {
      MEMp memX = mem;
      mem = mem->next;
      FREESUCCESS("NULL",memX->ptr,memX->nBytes)
      free (memX->ptr);
      free (memX);
      count++;
    }
    memHeadP = NULL;
    return count;
  }
  else {
    MEMp mem = memHeadP, memPrev = NULL;
    while (mem != NULL) {
      if (mem->ptr == ptr) {
	MEMp memX = mem;
	if (memPrev == NULL)
	  memHeadP = mem->next;
	else
	  memPrev->next = mem->next;
        FREESUCCESS("one",memX->ptr,memX->nBytes)
	free (memX->ptr);
	free (memX);
	return 1;
      }
      memPrev = mem;
      mem = mem->next;
    }
    FREEFAILED(ptr)
    if (fatalFnc != NULL) (*fatalFnc)("Unable to free memory buffer.");
    return 0;
  }
#endif
}

/******************************************************************************
* cdf_FreeMemoryX.
* If NULL is passed as the pointer to free, then free the entire list of
* allocated memory blocks.
******************************************************************************/

VISIBLE_PREFIX int cdf_FreeMemoryX (ptr, fatalFnc, loc)
void *ptr;
void (*fatalFnc) PROTOARGs((char *msg));
int loc;
{
#if defined(BUILDINGforIDL)
  free (ptr);
  return 1;
#else
  if (ptr == NULL) {
    int count = 0;
    MEMp mem = memHeadP;
    while (mem != NULL) {
      MEMp memX = mem;
      mem = mem->next;
      FREESUCCESS("NULL",memX->ptr,memX->nBytes)
      free (memX->ptr);
      free (memX);
      count++;
    }
    memHeadP = NULL;
    return count;
  }
  else {
    MEMp mem = memHeadP, memPrev = NULL;
    while (mem != NULL) {
      if (mem->ptr == ptr) {
	MEMp memX = mem;
	if (memPrev == NULL)
	  memHeadP = mem->next;
	else
	  memPrev->next = mem->next;
        FREESUCCESS("one",memX->ptr,memX->nBytes)
	free (memX->ptr);
	free (memX);
	return 1;
      }
      memPrev = mem;
      mem = mem->next;
    }
    FREEFAILED(ptr)
    if (fatalFnc != NULL) {
      char tmp[40];
      strcpy (tmp, "Unable to free memory buffer at ");
      sprintf (EofS(tmp), "%d", loc);
      (*fatalFnc)(tmp);
    }
    return 0;
  }
#endif
}

/******************************************************************************
* CallocateMemory.
* Using `calloc' might be more efficient on some platforms if the memory can
* be cleared faster than the loop used here.
******************************************************************************/

VISIBLE_PREFIX void *CallocateMemory (nObjects, objSize, fatalFnc)
size_t nObjects;
size_t objSize;
void (*fatalFnc) PROTOARGs((char *msg));
{
#if defined(BUILDINGforIDL)
  return calloc(nObjects,objSize);
#else
  size_t nBytes = nObjects * objSize, i;
  void *ptr = cdf_AllocateMemory (nBytes, fatalFnc);
  if (ptr != NULL) for (i = 0; i < nBytes; i++) ((Byte *) ptr)[i] = 0;
  return ptr;
#endif
}

/******************************************************************************
* InitVMemory.
*   NOTE: It is a mystery as to why values of `maxParagraphs' other than 128
* (or near 128) cause allocation/loading failures (as well as other nasty
* things).  Be a hero, solve the mystery...
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
static Logical InitVMemory () {
  uInt maxParagraphs = (uInt) (vMemSize / 16);
  if (!_vheapinit(0U,maxParagraphs,vMemMask)) return FALSE;
  return TRUE;
}
#endif

/******************************************************************************
* TermVMemory.
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
static void TermVMemory () {
  _vheapterm();
}
#endif

/******************************************************************************
* AllocateVMemory.
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
STATICforIDL MemHandle AllocateVMemory (nBytes)
size_t nBytes;
{
  MemHandle handle;
  static int first = TRUE;
  if (first) {
    first = FALSE;
    if (!InitVMemory()) return NULL;
    _fatexit (TermVMemory);
  }
  if ((handle = _vmalloc(nBytes)) == _VM_NULL) return NULL;
  return handle;
}
#endif

/******************************************************************************
* LoadVMemory.
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
STATICforIDL void *LoadVMemory (handle, writeFlag)
MemHandle handle;
Logical writeFlag;      /* TRUE if virtual memory will be written to. */
{
  void *buffer = _vload(handle,BOO(writeFlag,_VM_DIRTY,_VM_CLEAN));
  if (buffer == NULL) {
    FreeVMemory (handle);
    return NULL;
  }
  return buffer;
}
#endif

/******************************************************************************
* FreeVMemory.
******************************************************************************/

#if defined(MICROSOFTC_700) && INCLUDEvMEMORY
STATICforIDL int FreeVMemory (handle)
MemHandle handle;
{
  _vfree (handle);
  return 1;
}
#endif

/******************************************************************************
* TotalBytes.
******************************************************************************/

#if MEMLOG
static uLong TotalBytes (memHeadP)
MEMp memHeadP;
{
  MEMp mem = memHeadP; uLong totalBytes = 0;
  while (mem != NULL) {
    totalBytes += (uLong) mem->nBytes;
    mem = mem->next;
  }
  return totalBytes;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1