/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
/* SCCS Info: @(#)storage.h	1.13 3/13/90 */

#ifndef storage_included

#include <setjmp.h>

/* note: using the _setjmp and _longjmp forms might be more efficient, but */
/* they seem to be less portable.  in particular, they make gprof choke. */

#define SetJmp setjmp
#define LongJmp(jumpbuf) longjmp((jumpbuf), -1)
#define VLongJmp(jumpbuf, val) longjmp((jumpbuf), val)
#define JumpBufPtr int *
#define JumpBuf jmp_buf

#define CopyJumpBuf(dstbuf, srcbuf) bcopy(srcbuf, dstbuf, sizeof(JumpBuf))

/* a "generic" piece of storage */
typedef struct {
    int foo;
} generic;

/* Following definition sets the minimum satisfactory "hit ratio" for */
/* quick cell memory allocation.  If the achieved ratio falls below */
/* this minimum for a complete run of the interpreter, a warning */
/* message is generated when the interpreter exits.  Set the limit to */
/* zero to avoid the overhead of this monitoring... the associated */
/* code will not be generated in this case. */
#ifndef QCMINHIT
#  define QCMINHIT 85
#endif

/* Following definition causes a histogram to be collected showing */
/* storage allocation request sizes up to the given maximum size.  The */
/* histogram is printed when the interpreter exits.  Setting the */
/* value to zero suppresses histogram collection and printing. */
#ifndef QCHISTSIZE
#  define QCHISTSIZE 0
#endif

/* maximum allocation size for which we might have a quick cell list */
#define MAXQCSIZE 60

/* number of cells allocated in a single shot for quickcell lists */
#define QCBLOCKSIZE 1024

/* macros to adjust whatever counts are being kept (controlled by CPP */
/* macros) and to test quickcell chains for correctness */
#ifdef QCDEBUG
#  define QCFREE_ADJ_DBG(size) usedcnt[size]--;
#  define QCGET_ADJ_DBG(size) usedcnt[size]++,
#  if QCDEBUG == 0
#    define QCFREE_VERIFY(size) qc_verify(size);
#    define QCGET_VERIFY(size) qc_verify(size),
#  else
#    define QCFREE_VERIFY(size) qc_verify(0);
#    define QCGET_VERIFY(size) qc_verify(0),
#  endif
#else
#  define QCFREE_ADJ_DBG(size)
#  define QCGET_ADJ_DBG(size)
#  define QCFREE_VERIFY(size)
#  define QCGET_VERIFY(size)
#endif

#ifdef QCTRACK
#  define QCTRACK_SETID(cell) *(((char *) cell) - 1) = stg_allocator,
#  define QCTRACK_RMID(cell) *(((char *) cell) - 1) = nil;
#else
#  define QCTRACK_SETID(cell)
#  define QCTRACK_RMID(cell)
#endif

#if (QCMINHIT != 0)
#  define QCGET_ADJ_HIT qcallocs++,
#else
#  define QCGET_ADJ_HIT
#endif

#if (QCHISTSIZE != 0)
#  define QCGET_ADJ_HIST(size) \
     ((size) <= QCHISTSIZE ? qcHist[(size)-1]++ : qcxHist++),
#else
#  define QCGET_ADJ_HIST(size)
#endif

#define QCFREE_ADJUST(size) QCFREE_ADJ_DBG(size)
#define QCGET_ADJUST(size) \
  QCGET_ADJ_DBG(size) QCGET_ADJ_HIT QCGET_ADJ_HIST(size)

/* basic memory allocation/deallocation primitives... defined as */
/* macros only if inlining is not suppressed or if this is the module */
/* storage.c, in which case the macros will be incorporated into */
/* procedures that will be called from all other modules */
#ifdef NOINLINE
#  ifdef ISSTORAGE
#    define DEF_STG_MACROS
#  else
#    undef DEF_STG_MACROS
#  endif
#else
#  define DEF_STG_MACROS
#endif

#ifdef DEF_STG_MACROS
#define GETMAIN(size) \
  (_stgsize = (size), \
   _stg = (_stgsize <= MAXQCSIZE ? qcHeads[_stgsize] : nil), \
   _stg ? (qcHeads[_stgsize] = *((generic **)_stg), \
	   QCGET_ADJUST(_stgsize) QCGET_VERIFY(_stgsize) \
	   QCTRACK_SETID(_stg) _stg) \
        : d_getmain(_stgsize))

#define FREEMAIN(stg,size) {\
    _stg = (generic *) (stg); _stgsize = (size); \
    QCTRACK_RMID(_stg) \
    if (_stgsize <= MAXQCSIZE and qcBSize[_stgsize] isnt 0) { \
	*((generic **) _stg) = qcHeads[_stgsize]; \
	qcHeads[_stgsize] = (generic *) _stg; \
	QCFREE_ADJUST(_stgsize) QCFREE_VERIFY(_stgsize) \
    } \
    else \
      free((char *) _stg); \
  }
#endif

#ifndef ISSTORAGE
/* global temporaries used in above macros... */
extern counter _stgsize;
extern generic *_stg; 
/* here's the array of quickcell lists */
extern generic *qcHeads[];
/* here's the array that determines which sizes have quickcells */
extern int qcBSize[];
#ifdef QCDEBUG
extern unsigned long usedcnt[];
#endif
#if (QCMINHIT != 0)
extern unsigned long qcallocs;
#endif
#if (QCHISTSIZE != 0)
extern unsigned long qcHist[];
extern unsigned long qcxHist;
#endif
#ifdef QCTRACK
extern char *stg_allocator;
#endif
#endif

/* if inlining is suppressed, declare functions getmain() and */
/* freemain() whose bodies are the above macros.  Otherwise define */
/* getmain and freemain as GETMAIN and FREEMAIN, repsectively */
#ifdef NOINLINE
#  ifndef ISSTORAGE
      generic *getmain();
      void freemain();
#  endif
#else
#  define getmain GETMAIN
#  define freemain FREEMAIN
#endif

/* some convenient shorthands */
#define dotsize(objnum) (sizeof(dot_info)+sizeof(object)*(objnum))
#define new(thing) ((thing *) getmain (sizeof (thing)))
#define dispose(stg,type) freemain(stg, sizeof(type))
#define freedotmain(stg, objnum) freemain(stg, dotsize(objnum))

/* routine that does allocation when inlining fails */
generic *d_getmain();
/* routine to allocate a dotted object and clear its data vector */
dotcontainer *getdotmain();

#ifdef QCDEBUG
/* routine to test correctness of quickcell lists */
void qc_verify();
#endif

#endif
#define storage_included
