slidescript/src/x3mem.c

1396 lines
53 KiB
C

/* ------------------------------------------------------------ */
/* file information */
/* ------------------------------------------------------------ */
// Filename: x3mem.c
// Purpose: Memory management
// License: MIT/X. (c) OldCoder (Robert Kiraly) 1987-2022.
/* ------------------------------------------------------------ */
/* header files */
/* ------------------------------------------------------------ */
#include <stdlib.h>
#include <string.h>
#include "inc/x3mem.h" /* main X3MEM header file */
/* ------------------------------------------------------------ */
/* miscellaneous notes */
/* ------------------------------------------------------------ */
/* 1. X3MEM is aka QMALLOC. */
/* 2. Programs should not pass QMALLOC storage pointers to */
/* MALLOC functions. */
/* 3. Programs should not pass MALLOC storage pointers to */
/* QMALLOC functions. */
/* 4. The following actions may slow programs down: */
/* */
/* (a) frequent calls to "chk_qma()" or "qflush()" */
/* (b) frequent calls to the lower-level MALLOC functions */
/* (c) frequent requests for blocks larger than "qm_block" */
/* ------------------------------------------------------------ */
/* miscellaneous definitions */
/* ------------------------------------------------------------ */
/* convert to alignment units */
/* If "x" is a size in bytes, "ALIGNSIZ(x)" returns the minimum */
/* number of "QM_ALIGN" alignment units which will hold the */
/* specified number of bytes. */
#define ALIGNSIZ(x) ((((x3u_long) (x)) + QMASIZE - ONE) / QMASIZE)
/* ------------------------------------------------------------ */
/* QMALLOC block header size */
/* size in bytes */
#define HDR_BSIZE (sizeof(QM_HDR))
/* size in alignment units */
#define HDR_ASIZE ALIGNSIZ(HDR_BSIZE)
/* ------------------------------------------------------------ */
/* null header pointer */
#define NULLQH ((QM_HDR *) ZERO)
/* ------------------------------------------------------------ */
/* memory allocation slack */
/* When "qmalloc()" allocates a "qm_free" free block which is */
/* longer than it needs, the unused portion of the block is */
/* broken off if more than "QM_SLACK" alignment units of usable */
/* storage will be freed up this way. */
#define QM_SLACK (8 / QMASIZE)
/* ------------------------------------------------------------ */
/* return alignment pointer */
/* "ALIGN_P(x)" typecasts the pointer (x) to the alignment */
/* pointer type "QM_ALIGN *". The pointer value should be */
/* suitably aligned before the conversion is done. */
#define ALIGN_P(x) ((QM_ALIGN *) (x))
/* ------------------------------------------------------------ */
/* address conversion */
/* The "addr(x)" macro is used to work around a problem with */
/* the Intel 80X86 system architecture. */
#ifdef M_I86 /* Microsoft C */
#define addr(x) ((x3u_long) ((char far *) (x)))
#else /* other systems */
#define addr(x) (x)
#endif /* endif MSC */
/* ------------------------------------------------------------ */
/* return block size */
/* If "x" is a pointer to a "QM_HDR" memory block header, */
/* "QM_ASIZE(x)" returns the size of the block in "QM_ALIGN" */
/* units as an unsigned long value. The block size includes */
/* the size of the header itself. */
#define QM_ASIZE(x) ((x3u_long) ((x)->qa_size))
/* ------------------------------------------------------------ */
/* return next memory position */
/* If "x" is a pointer to a "QM_HDR" memory block header, */
/* "NXT_POS(x)" returns a "QM_ALIGN" pointer to the first */
/* "QM_ALIGN" memory position after the specified memory */
/* block. */
#define NXT_POS(x) (ALIGN_P(x) + (x)->qa_size)
/* ------------------------------------------------------------ */
/* "qrealloc()" blocking factor */
/* To reduce fragmentation, "qrealloc()" reallocates memory in */
/* blocks of "QM_RAFF" bytes. "QM_RAFF" should be a power of */
/* two between 2^3 (8) and 2^11 (2048). */
#define QM_RAFF 256
/* ------------------------------------------------------------ */
/* consistency-check definitions */
/* ------------------------------------------------------------ */
/* consistency-check codes */
#define QM_FREE ((QM_SIZE_T) 0x12345678L)
#define QM_INUSE ((QM_SIZE_T) 0x87654321L)
/* "QM_HDR" memory block headers normally fall into the follow- */
/* ing four categories: */
/* */
/* (a) headers which are in a QLIST "qm_free" list */
/* (b) headers which are in the global "sys_free" list */
/* (c) headers allocated to higher-level users */
/* (d) headers which are in a QLIST "qm_sysmem" list */
/* If a "QM_HDR" block header falls into the first two cate- */
/* gories here, "qm_chk" contains a consistency check value */
/* equal to "qa_size" exclusive-or'd with the magic number */
/* "QM_FREE". */
/* If a "QM_HDR" block header falls into the second two cate- */
/* gories here, "qm_chk" contains a consistency check value */
/* equal to "qa_size" exclusive-or'd with the magic number */
/* "QM_INUSE". */
/* ------------------------------------------------------------ */
/* consistency-check macros */
/* These six macros each take a single "QM_HDR" pointer as a */
/* parameter. */
/* "qm_relflg(q)" and "qm_useflg(q)" return the calculated */
/* values of the "free" and "used" consistency flags, respect- */
/* ively. */
/* "qm_setrel(q)" and "qm_setuse(q)" save the correct values of */
/* the "free" and "used" consistency flags, respectively. */
/* "qm_chkrel(q)" and "qm_chkuse(q)" check the stored values of */
/* the "free" and "used" consistency flags, respectively. */
/* ------------------------------------------------------------ */
#define qm_relflg(q) ((q)->qa_size ^ QM_FREE)
#define qm_useflg(q) ((q)->qa_size ^ QM_INUSE)
#define qm_setrel(q) ((q)->qm_chk = qm_relflg(q))
#define qm_setuse(q) ((q)->qm_chk = qm_useflg(q))
#define qm_chkrel(q) if ((q)->qm_chk != qm_relflg(q)) qm_ce()
#define qm_chkuse(q) if ((q)->qm_chk != qm_useflg(q)) qm_ce()
/* ------------------------------------------------------------ */
/* allocation constraints */
/* ------------------------------------------------------------ */
/* default "qm_block" factor */
/* The constant "QM_DEFBLK" specifies the default value for */
/* "qm_block". See the description of "qm_block" in "x3mem.h" */
/* for additional information. */
/* ------------------------------------------------------------ */
#ifdef M_I86 /* Microsoft C */
#define QM_DEFBLK 512
#endif /* endif MSC */
#ifdef QMSA /* stand-alone version */
#define QM_DEFBLK 512
#endif /* endif "QMSA" */
#ifdef VMS /* VMS host */
#define QM_DEFBLK 8184
#endif /* endif VMS */
#ifndef QM_DEFBLK /* set default value? */
#define QM_DEFBLK 2036 /* yes */
#endif /* endif default value */
/* ------------------------------------------------------------ */
/* If "x" is the "qm_block" value for a given QLIST, "q_more()" */
/* will clip its "n_a" parameter to a minimum of "QM_GETMIN(x)" */
/* alignment units when extending the QLIST. */
/* "QM_GETMIN(x)" is chosen so that "q_more()" will request a */
/* minimum of at least "x" bytes when calling "malloc()". (If */
/* "x" is a multiple of QMASIZE, "q_more()" will request a min- */
/* imum of exactly "x" bytes.) */
/* ------------------------------------------------------------ */
#define QM_GETMIN(x) (ALIGNSIZ(x) - HDR_ASIZE)
/* ------------------------------------------------------------ */
/* public variables */
/* ------------------------------------------------------------ */
#ifdef QMSA /* stand-alone version */
char *mempool; /* pointer to/into storage pool */
long poolsize; /* storage pool size in bytes */
#endif /* endif "QMSA" */
/* ------------------------------------------------------------ */
/* module-private variables */
/* ------------------------------------------------------------ */
MODPRIV char *qm_ce_msg; /* used by "qm_ce()" */
/* freed system memory blocks */
MODPRIV QM_HDR *sys_free = NULLQH;
MODPRIV QLIST QM_SQL [ONE]; /* used by "sq..." functions */
/* ------------------------------------------------------------ */
/* functions */
/* ------------------------------------------------------------ */
/* Function: V_OBJ *qm_sbrk(ux) */
/* Summary: Lowest-level "stand-alone" allocation routine. */
/* ------------------------------------------------------------ */
/* x3u_int ux; - Number of bytes to allocate (0+). */
/* ------------------------------------------------------------ */
/* "qm_sbrk()" is the lowest-level allocation routine used by */
/* the "stand-alone" version of the QMALLOC package. */
/* "qm_sbrk()" allocates a block of the specified size and re- */
/* turns the associated block pointer. If the block cannot be */
/* allocated, "qm_sbrk()" returns a null pointer. */
/* ------------------------------------------------------------ */
#ifdef QMSA /* stand-alone version */
MODPRIV V_OBJ *qm_sbrk (ux)
x3u_int ux; /* # of bytes to allocate (0+) */
{
long lx; /* "signed long" byte count */
int n; /* "signed int" byte count */
V_OBJ *result; /* result pointer */
/* ------------------------------------------------------------ */
n = (int) ux; /* "signed int" byte count */
/* safety measure */
if (n < ZERO) return (NULLVP);
lx = (long) n; /* "signed long" byte count */
if (mempool == NULLCP)
{
x_panic ("Need to initialize mempool and poolsize");
}
if (lx > poolsize) /* sufficient room? */
{ /* no */
return (NULLVP); /* return a null pointer */
}
result = mempool; /* point to new block */
poolsize -= lx; /* reduce recorded pool size */
mempool += n; /* move pointer past new block */
return (result); /* return block pointer */
}
#endif /* endif "QMSA" */
/* ------------------------------------------------------------ */
/* Function: V_FUNC qm_ce(NO_PARAM) */
/* Summary: Handles inconsistent-allocation errors. */
/* ------------------------------------------------------------ */
/* Various functions call "qm_ce()" if it appears that bad */
/* pointers are being used or memory is corrupted. "qm_ce()" */
/* prints an error message and terminates the program. */
/* "qm_ce()" produces the message "inconsistent memory chain" */
/* by default. The global character pointer "qm_ce_msg" may be */
/* used to specify a secondary message string. If this pointer */
/* is non-null when "qm_ce()" is called, "qm_ce()" will add the */
/* specified message string to the base message. */
/* ------------------------------------------------------------ */
MODPRIV V_FUNC qm_ce(NO_PARAM)
{
/* base message */
char *msg = "inconsistent memory chain";
if (qm_ce_msg != NULLCP) /* add secondary message? */
{ /* yes */
x_panic ("%s: %s",msg,qm_ce_msg);
}
else
{ /* no */
x_panic (msg);
}
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC chk_qm0(cp) */
/* Summary: Checks a dynamic-memory pointer. */
/* ------------------------------------------------------------ */
/* V_OBJ *cp; - Dynamic-memory storage pointer previously */
/* returned by "qmalloc()" or a related */
/* function. */
/* ------------------------------------------------------------ */
/* "chk_qm0(cp)" checks the "V_OBJ *" pointer "cp" and term- */
/* inates the caller with an error message if "cp" is not a */
/* valid "qmalloc()" storage pointer. */
/* Note: A valid storage pointer becomes invalid if the */
/* associated storage is freed. */
/* "chk_qm0()" is the non-macro version of "chk_qmp()". See */
/* the description of "chk_qmp()" for additional information. */
/* ------------------------------------------------------------ */
MODPUB V_FUNC chk_qm0(cp)
V_OBJ *cp; /* dynamic-memory pointer */
{
REG_VAR QM_HDR *mcb; /* memory block pointer */
/* ------------------------------------------------------------ */
if (cp == NULLVP) qm_ce(); /* check for null pointer */
/* point to the header block */
mcb = ((QM_HDR *) cp) - ONE;
qm_chkuse(mcb); /* check the header block */
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC chk_qma (qp) */
/* Summary: Checks an entire QLIST. */
/* ------------------------------------------------------------ */
/* QLIST *qp; - QLIST to check. */
/* ------------------------------------------------------------ */
/* "chk_qma()" performs some consistency checks on the QLIST */
/* specified by "qp". */
/* ------------------------------------------------------------ */
MODPUB V_FUNC chk_qma (qp)
QLIST *qp; /* QLIST to check */
{
REG_VAR QM_HDR *mcb; /* memory block pointer */
/* ------------------------------------------------------------ */
/* check for null pointer */
if (qp == NULL_QLS) qm_ce();
/* ------------------------------------------------------------ */
/* The following block of code checks the free list associated */
/* with the QLIST. */
for (mcb = qp->qm_free; mcb != NULLQH; mcb = mcb->nxt_free)
{
qm_chkrel(mcb); /* consistency check */
}
/* ------------------------------------------------------------ */
/* The following block of code checks the system-memory list */
/* associated with the QLIST. */
for (mcb = qp->qm_sysmem; mcb != NULLQH; mcb = mcb->nxt_free)
{
qm_chkuse(mcb); /* consistency check */
}
}
/* ------------------------------------------------------------ */
/* Function: long sys_mem() */
/* Summary: Estimates amount of system memory available. */
/* ------------------------------------------------------------ */
/* "sys_mem()" estimates the amount of free (unallocated) sys- */
/* tem memory available and returns the result (in bytes). The */
/* value returned is conservative (low). */
/* ------------------------------------------------------------ */
/* Technical notes: */
/* 1. The result does not include unused storage space in */
/* QLISTs. */
/* 2. If the host O/S is MS-DOS, "sys_mem()" assumes that the */
/* calling program was compiled using Microsoft C's "large" */
/* memory model. */
/* 3. "WSP_SIZE" should fall into the range of 5,000 to 50,000 */
/* bytes. */
/* 4. "WSP_SLOTS" should not exceed 250 under MS-DOS. */
/* 5. "sys_mem()" is accurate up to "WSP_SIZE * WSP_SLOTS" */
/* bytes. */
/* ------------------------------------------------------------ */
#define WSP_SLOTS 250 /* # of segments to try for */
#define WSP_SIZE 5120 /* size of each segment */
/* ------------------------------------------------------------ */
#ifdef QMSA /* stand-alone version */
MODPUB long sys_mem()
{
return (poolsize);
}
#endif /* endif "QMSA" */
/* ------------------------------------------------------------ */
#ifndef QMSA /* "malloc"-based version */
MODPUB long sys_mem()
{
REG_VAR char *cp; /* single allocation pointer */
REG_VAR int i; /* scratch */
REG_VAR int n_seg; /* # of segments allocated */
char *wsp_ptr [WSP_SLOTS]; /* list of allocation pointers */
/* ------------------------------------------------------------ */
/* try to allocate memory */
for (n_seg = ZERO; n_seg < WSP_SLOTS; n_seg++)
{
cp = (char *) malloc ((x3u_int) WSP_SIZE);
if (cp == NULLCP) break;
wsp_ptr [n_seg] = cp;
}
/* free the allocated memory */
for (i = ZERO; i < n_seg; i++)
{
free (wsp_ptr [i]); /* free the current block */
}
/* return the result */
return (((long) n_seg) * (long) WSP_SIZE);
}
#endif /* endif not "QMSA" */
/* ------------------------------------------------------------ */
/* Function: V_FUNC qfree(cp) */
/* Summary: Frees memory allocated by "qmalloc()". */
/* ------------------------------------------------------------ */
/* V_OBJ *cp; - Pointer returned by "qmalloc()" or a re- */
/* lated function. */
/* ------------------------------------------------------------ */
/* "qfree(cp)" returns the dynamic memory associated with "cp" */
/* to the QLIST from which it was obtained. */
/* "qfree()" may be used to free memory allocated by the */
/* following functions: */
/* */
/* qcalloc() qmalloc() qrealloc() */
/* "qfree()" should not be used to free memory allocated by the */
/* standard MALLOC functions (calloc, malloc, etc.). */
/* ------------------------------------------------------------ */
MODPUB V_FUNC qfree (cp)
V_OBJ *cp; /* previously-allocated pointer */
{
REG_VAR QM_HDR *cur_mcb; /* memory block being freed */
REG_VAR x3u_long lx; /* scratch */
int m_flag; /* O.K.-to-merge flag */
QM_SIZE_T m_size; /* merged block size */
QM_HDR *nxt_mcb; /* next block in free list */
QM_HDR *prev; /* preceding block in free list */
QLIST *qp; /* QLIST to use */
/* ------------------------------------------------------------ */
/* initial setup */
if (cp == NULLVP) qm_ce(); /* null pointer? */
/* point to the QM_HDR block */
cur_mcb = ((QM_HDR *) cp) - ONE;
qm_chkuse(cur_mcb); /* consistency check */
qm_setrel(cur_mcb); /* mark block as released */
qp = cur_mcb->qm_owner; /* get the QLIST to use */
cur_mcb->nxt_free = NULLQH; /* clear the next-free pointer */
/* ------------------------------------------------------------ */
/* handle the simplest case */
if (qp->qm_free == NULLQH) /* is QLIST's freelist empty? */
{ /* yes */
qp->qm_free = cur_mcb; /* stick the freed block in */
return; /* done - exit */
}
/* ------------------------------------------------------------ */
/* find position in free-list */
/* When the following loop is completed, "prev" points to the */
/* header which should precede "cur_mcb" in the free-list, and */
/* "nxt_mcb" points to the header which should follow "cur_mcb" */
/* in the list. */
/* "prev" will be null (NULLQH) if "cur_mcb" belongs at the */
/* beginning of the list, and "nxt_mcb" will be null (NULLQH) */
/* if "cur_mcb" belongs at the end of the list. Since we have */
/* already verified that the list is nonempty, "prev" and */
/* "nxt_mcb" cannot both be null. */
prev = NULLQH;
nxt_mcb = qp->qm_free;
while ((nxt_mcb != NULLQH) && (addr(nxt_mcb) < addr(cur_mcb)))
{
qm_chkrel(nxt_mcb); /* consistency check */
prev = nxt_mcb;
nxt_mcb = nxt_mcb->nxt_free;
}
/* ------------------------------------------------------------ */
/* another consistency check */
/* Does the preceding free memory block overlap the beginning */
/* of the block being freed now? */
if (prev != NULLQH)
{
if ((addr(prev) >= addr(cur_mcb)) ||
(addr(NXT_POS(prev)) > addr(ALIGN_P(cur_mcb))))
{ /* yes - consistency error */
x_panic ("qfree: overlap -1");
}
}
/* ------------------------------------------------------------ */
/* another consistency check */
/* Does the end of the block being freed now overlap the begin- */
/* ning of the next free block? */
if (nxt_mcb != NULLQH)
{
if ((addr(cur_mcb) >= addr(nxt_mcb)) ||
(addr(NXT_POS(cur_mcb)) > addr(ALIGN_P(nxt_mcb))))
{ /* yes - consistency error */
x_panic ("qfree: overlap +1");
}
}
/* ------------------------------------------------------------ */
/* Can we merge the block being freed with the preceding free */
/* block? */
m_flag = FALSE;
if ((prev != NULLQH) &&
(NXT_POS(prev) == ALIGN_P(cur_mcb)))
{ /* the blocks are adjoining */
/* get the merged block size */
lx = QM_ASIZE(prev) + QM_ASIZE(cur_mcb);
/* value as it will be stored */
m_size = (QM_SIZE_T) lx;
/* will the size fit? */
if (lx == (x3u_long) m_size) m_flag = TRUE;
}
if (m_flag) /* can the blocks be merged? */
{ /* yes */
prev->qa_size = m_size; /* set the total size */
cur_mcb = prev; /* point to the resulting block */
qm_setrel(cur_mcb); /* set the consistency flag */
}
else
{ /* no - put it in the free list */
if (nxt_mcb == qp->qm_free)
{ /* head of free list */
if (prev != NULLQH) x_panic ("qfree: prev");
qp->qm_free = cur_mcb;
}
else
{ /* inside the free list */
if (prev == NULLQH) x_panic ("qfree: prev");
prev->nxt_free = cur_mcb;
}
/* point to the next block */
cur_mcb->nxt_free = nxt_mcb;
}
/* ------------------------------------------------------------ */
/* Can we merge it with the next block? */
m_flag = FALSE;
if ((nxt_mcb != NULLQH) &&
(NXT_POS(cur_mcb) == ALIGN_P(nxt_mcb)))
{ /* the blocks are adjoining */
/* get the merged block size */
lx = QM_ASIZE(cur_mcb) + QM_ASIZE(nxt_mcb);
/* value as it will be stored */
m_size = (QM_SIZE_T) lx;
/* will the size fit? */
if (lx == (x3u_long) m_size) m_flag = TRUE;
}
if (m_flag)
{ /* yes - do it */
/* set the total size */
cur_mcb->qa_size = m_size;
qm_setrel(cur_mcb); /* set the consistency flag */
/* point to the next block */
cur_mcb->nxt_free = nxt_mcb->nxt_free;
}
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC q_more(qp,n_a) */
/* Summary: Support function for "qmalloc()". */
/* ------------------------------------------------------------ */
/* QLIST *qp; - QLIST to use. */
/* x3u_long n_a; - Memory needed, in units of QMASIZE. */
/* ------------------------------------------------------------ */
/* "q_more(qp,n_a)" gets the requested amount of dynamic memory */
/* from the freed system-memory list (sys_free) or from the */
/* operating system and adds it to the specified QLIST. */
/* "n_a" includes the size of the free-list (qm_free) memory */
/* block header, but not the size of the system ("qm_sysmem" or */
/* "sys_free") memory block header. */
/* ------------------------------------------------------------ */
#ifdef QMSA /* stand-alone version */
MODPRIV int q_more (qp,n_a)
#else /* "malloc"-based version */
MODPRIV V_FUNC q_more (qp,n_a)
#endif /* endif "QMSA" */
QLIST *qp; /* QLIST to use */
x3u_long n_a; /* # of alloc. units required */
{
x3u_long lx; /* scratch */
QM_HDR *mcb; /* memory block header pointer */
QM_HDR *prev; /* pointer to preceding block */
QM_SIZE_T ux; /* scratch */
/* ------------------------------------------------------------ */
/* adjust request as necessary */
lx = (x3u_long) qp->qm_block;
if (lx == ZERO) lx = QM_DEFBLK;
lx = QM_GETMIN(lx);
if (n_a < lx) n_a = lx;
/* ------------------------------------------------------------ */
/* check the general free pool */
for (prev = NULLQH, mcb = sys_free;
mcb != NULLQH;
prev = mcb, mcb = mcb->nxt_free)
{ /* try the next system block */
qm_chkrel(mcb); /* consistency check */
if ((QM_ASIZE(mcb) - HDR_ASIZE) >= n_a)
{ /* found a block large enough */
if (prev == NULLQH) /* at the start of the list */
{ /* delete it from the list */
sys_free = mcb->nxt_free;
}
else /* it's inside the list */
{ /* delete it from the list */
prev->nxt_free = mcb->nxt_free;
}
break; /* got it */
}
}
/* ------------------------------------------------------------ */
/* use the system if necessary */
if (mcb == NULLQH) /* have we got it yet? */
{ /* no - must "malloc()" it */
lx = QMASIZE * (HDR_ASIZE + n_a);
ux = (QM_SIZE_T) lx;
if (lx != (x3u_long) ux)
{
#ifdef QMSA /* stand-alone version */
return (FALSE);
#else /* "malloc"-based version */
x_panic ("qmalloc: request too large");
#endif /* endif "QMSA" */
}
#ifdef QMSA /* stand-alone version */
mcb = (QM_HDR *) qm_sbrk (ux);
#else /* "malloc"-based version */
mcb = (QM_HDR *) malloc (ux);
#endif /* endif "QMSA" */
}
/* ------------------------------------------------------------ */
/* check the results */
if (mcb == NULLQH) /* did we get it? */
{ /* no - error */
#ifdef QMSA /* stand-alone version */
return (FALSE);
#else /* "malloc"-based version */
x_error ("insufficient memory");
#endif /* endif "QMSA" */
}
/* ------------------------------------------------------------ */
/* build a system memory header */
/* size including header itself */
mcb->qa_size = n_a + HDR_ASIZE;
qm_setuse(mcb); /* set the consistency flag */
/* ------------------------------------------------------------ */
/* add to system-memory list */
mcb->nxt_free = qp->qm_sysmem;
qp->qm_sysmem = mcb;
/* ------------------------------------------------------------ */
/* build an allocation header */
mcb++; /* point to new header */
mcb->qm_owner = qp; /* "QLIST" to put it in */
mcb->qa_size = n_a; /* size including header itself */
qm_setuse(mcb); /* set the consistency flag */
/* ------------------------------------------------------------ */
/* add to QLIST's free-list */
qfree ((V_OBJ *) (mcb + ONE));
#ifdef QMSA /* stand-alone version */
return (TRUE);
#endif /* endif "QMSA" */
}
/* ------------------------------------------------------------ */
/* Function: V_OBJ *qmalloc (qp, nb) */
/* Summary: Allocates memory from a QLIST. */
/* ------------------------------------------------------------ */
/* QLIST *qp; - QLIST to use (or QM_GEN). */
/* int nb; - Number of bytes required (1+). */
/* ------------------------------------------------------------ */
/* "qmalloc(qp,nb)" allocates the specified number of bytes */
/* from the specified QLIST and returns a pointer to the allo- */
/* cated block of storage. */
/* If the caller does not use multiple allocation lists, the */
/* predefined general-purpose list "QM_GEN" may be used. */
/* "nb" is declared as a signed value, but is interpreted as an */
/* unsigned value. */
/* If the QLIST does not contain sufficient free storage, */
/* "qmalloc()" adds dynamic memory to the QLIST as necessary. */
/* Memory allocated by "qmalloc()" may be freed using */
/* "qfree()". */
/* "qmalloc()" prints an error message and terminates the */
/* caller if sufficient memory cannot be allocated. */
/* See the header file "x3mem.h" for additional information. */
/* ------------------------------------------------------------ */
/* Internal note: */
/* The sizes referred to in the following code are in units */
/* of the alignment size (QMASIZE), with the exception of "nb" */
/* and "nu". */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *qmalloc (qp, nb)
QLIST *qp; /* QLIST to use */
int nb; /* number of bytes required */
{
REG_VAR QM_HDR *mcb; /* memory block header pointer */
REG_VAR QM_HDR *mcx; /* pointer to left-over block */
x3u_long n_a; /* # of alignment units needed */
/* "malloc()"-compatible size */
QM_SIZE_T nu = (QM_SIZE_T) nb;
REG_VAR QM_HDR *prev; /* previous header in freelist */
REG_VAR x3u_long x_a; /* size of the left-over block */
/* ------------------------------------------------------------ */
/* initial setup */
/* set the default QLIST */
if (qp == NULL_QLS) x_panic ("qmalloc: null");
if (nu == ZERO) nu = ONE; /* check the size request */
n_a = ALIGNSIZ(nu); /* # of alignment units needed */
if (qp->qm_nofree == FALSE) /* is the list freeable? */
{ /* yes */
n_a += HDR_ASIZE; /* include a block header */
}
prev = mcb = NULLQH; /* initial (null) block pointer */
/* ------------------------------------------------------------ */
/* Search the QLIST freelist for a block of memory that is */
/* large enough. */
while (mcb == NULLQH) /* got a memory block yet? */
{ /* no - try to get one */
for (prev = NULLQH, mcb = qp->qm_free;
mcb != NULLQH;
prev = mcb, mcb = mcb->nxt_free)
{ /* advance to the next entry */
qm_chkrel(mcb); /* consistency check */
if (QM_ASIZE(mcb) >= n_a) break;
}
if (mcb == NULLQH) /* did we find a block? */
{ /* no */
#ifdef QMSA /* stand-alone version */
/* get more memory from system */
if (q_more (qp, n_a) == FALSE) return (NULLVP);
#else /* "malloc"-based version */
q_more (qp, n_a); /* get more memory from system */
#endif /* endif "QMSA" */
}
}
/* ------------------------------------------------------------ */
/* break block up if necessary */
/* "mcb" now points to the "QM_HDR" header of a block in the */
/* freelist that is large enough. If the block is significant- */
/* ly larger than we need it to be, we break it into two pieces */
/* and leave the left-over portion in the freelist. */
/* The left-over portion must be split off from the end of the */
/* block, as opposed to the beginning. This requirement is */
/* imposed by "qrealloc()". */
/* We don't adjust the consistency-check field for the "used" */
/* block (*mcb) here. If this is necessary, it is done before */
/* the block is returned. */
/* ------------------------------------------------------------ */
if ((QM_ASIZE(mcb) - n_a) > (HDR_ASIZE + QM_SLACK))
{ /* break it up */
/* size of the left-over block */
x_a = mcb->qa_size - n_a;
mcb->qa_size = n_a; /* set size of "used" block */
/* point to the left-over block */
mcx = (QM_HDR *) NXT_POS(mcb);
mcx->qa_size = x_a; /* set its size */
/* set its next-pointer */
mcx->nxt_free = mcb->nxt_free;
qm_setrel(mcx); /* set its consistency flag */
mcb->nxt_free = mcx; /* put it back in the freelist */
}
/* ------------------------------------------------------------ */
/* remove block from freelist */
if (mcb == qp->qm_free) /* at head of freelist? */
{ /* yes - just advance the head */
if (prev != NULLQH) x_panic ("qmalloc: prev");
qp->qm_free = mcb->nxt_free;
}
else
{ /* no - adjust preceding entry */
if (prev == NULLQH) x_panic ("qmalloc: prev");
prev->nxt_free = mcb->nxt_free;
}
/* ------------------------------------------------------------ */
/* wrap it up */
if (qp->qm_nofree == FALSE) /* is the list freeable? */
{ /* yes - create a header */
mcb->qm_owner = qp; /* save the (QLIST) owner */
mcb->qa_size = n_a; /* block size including header */
qm_setuse(mcb); /* set the consistency flag */
mcb++; /* point to the user's portion */
}
return ((V_OBJ *) mcb); /* give it away */
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC qflush (qp) */
/* Summary: Releases an entire QLIST. */
/* ------------------------------------------------------------ */
/* QLIST *qp; - QLIST to free. */
/* ------------------------------------------------------------ */
/* Globals (in): */
/* char qm_retsys; - "Flush to system" flag. */
/* ------------------------------------------------------------ */
/* "qflush(qp)" frees up all of the dynamic memory associated */
/* with the specified QLIST. The freed memory may be re- */
/* allocated to any QLIST using "qmalloc()". */
/* "qflush(qp)" clears the QLIST header after releasing the */
/* memory. The empty QLIST may be re-used for new allocation */
/* calls. If the "qm_nofree" flag is used, this flag should be */
/* re-initialized. */
/* ------------------------------------------------------------ */
/* Technical note: */
/* If the global flag "qm_retsys" is FALSE, "qmalloc()" retains */
/* primary control of the freed memory. This is the default */
/* mode of operation. */
/* If the global flag "qm_retsys" is TRUE, "qflush()" returns */
/* primary control of the freed memory to the lower-level */
/* memory allocation function "malloc()". */
/* ------------------------------------------------------------ */
MODPUB V_FUNC qflush(qp)
QLIST *qp; /* QLIST to use */
{
REG_VAR QM_HDR *cur_mcb; /* current memory block header */
QM_HDR *nxt_mcb; /* next memory block header */
/* ------------------------------------------------------------ */
if (qp == (QLIST *) NULLVP) qm_ce();
/* ------------------------------------------------------------ */
for (cur_mcb = qp->qm_sysmem;
cur_mcb != NULLQH;
cur_mcb = nxt_mcb)
{
qm_chkuse(cur_mcb); /* consistency check */
/* point to the next header */
nxt_mcb = cur_mcb->nxt_free;
#ifndef QMSA /* "malloc"-based version */
if (qm_retsys) /* return memory to system? */
{ /* yes - use "free()" */
V_CAST free ((V_OBJ *) cur_mcb);
}
else
#endif /* endif not "QMSA" */
{ /* no - put it in a common pool */
qm_setrel(cur_mcb); /* set the consistency flag */
/* move it to "sys_free" list */
cur_mcb->nxt_free = sys_free;
sys_free = cur_mcb;
}
}
p_clr(qp); /* clear the QLIST header */
}
/* ------------------------------------------------------------ */
/* Function: V_OBJ *qcalloc (qp, num, sz) */
/* Summary: Allocates memory from a QLIST. */
/* ------------------------------------------------------------ */
/* QLIST *qp; - QLIST to use (or QM_GEN). */
/* x3u_int num; - Number of items required (1+). */
/* x3u_int sz; - Size of each item. */
/* ------------------------------------------------------------ */
/* "qcalloc(qp,num,sz)" is roughly equivalent to "qmalloc(qp, */
/* num*sz)" with the distinction that "qcalloc()" clears the */
/* block of memory allocated before returning it. */
/* Memory allocated by "qcalloc()" may be freed using */
/* "qfree()". */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *qcalloc (qp, num, sz)
QLIST *qp; /* QLIST to use */
x3u_int num; /* No. of items required (1+) */
x3u_int sz; /* Size of each item */
{
REG_VAR V_OBJ *result; /* result pointer */
/* ------------------------------------------------------------ */
int nb = num * sz; /* number of bytes required */
result = qmalloc (qp, nb); /* returns a block (or exits) */
b_clr (result, nb); /* clear the block obtained */
return (result); /* return the result pointer */
}
/* ------------------------------------------------------------ */
/* Function: V_OBJ *qrealloc (cp, nb) */
/* Summary: Re-allocates dynamic memory. */
/* ------------------------------------------------------------ */
/* V_OBJ *qp; - "qmalloc()" storage to reallocate. */
/* int nb; - New block size, in bytes (1+). */
/* ------------------------------------------------------------ */
/* If "cp" is a pointer previously returned by "qmalloc()" or a */
/* related function, "qrealloc(cp,nb)" changes the size of the */
/* allocated block to the new value "nb". */
/* The block may be moved in the process. "qrealloc()" returns */
/* a pointer to the new block location (or to the old block */
/* location, if the block is not moved). */
/* "nb" is declared as a signed integer, but interpreted as an */
/* unsigned integer. */
/* The contents of the block are unchanged up to the lesser of */
/* the old and new block sizes. */
/* Memory allocated by "qrealloc()" may be freed using */
/* "qfree()". */
/* "qrealloc()" prints an error message and terminates the */
/* caller if sufficient memory cannot be allocated. */
/* "qrealloc()" should not be used in conjunction with the */
/* standard "malloc()" and "free()" functions. */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *qrealloc (cp, nb)
V_OBJ *cp; /* storage to reallocate */
int nb; /* user request (in bytes) */
{
REG_VAR QM_HDR *cur_mcb; /* QM_HDR being reallocated */
REG_VAR QLIST *qp; /* QLIST to use */
REG_VAR V_OBJ *result; /* result pointer */
REG_VAR x3u_long u_lx; /* scratch (must be x3u_long) */
REG_VAR x3u_int ux; /* scratch (must be x3u_int ) */
/* ------------------------------------------------------------ */
REG_VAR x3u_int u_nb; /* adjusted size request */
REG_VAR x3u_int u_nc; /* number of bytes to copy */
REG_VAR x3u_int u_old; /* size of old block */
/* ------------------------------------------------------------ */
/* initial setup */
if (cp == NULLVP) qm_ce(); /* null pointer? */
/* point to the QM_HDR block */
cur_mcb = ((QM_HDR *) cp) - ONE;
qm_chkuse(cur_mcb); /* consistency check */
qp = cur_mcb->qm_owner; /* get the QLIST to use */
if (nb == ZERO) nb = ONE; /* check the size request */
u_nb = (x3u_int) nb; /* interpret "nb" as unsigned */
/* ------------------------------------------------------------ */
/* get size of old block */
/* "u_old" holds the number of bytes allocated for the old */
/* block, excluding the memory block header. */
u_old = (x3u_int) ((QM_ASIZE(cur_mcb) - HDR_ASIZE) * QMASIZE);
/* ------------------------------------------------------------ */
/* adjust the new block size */
/* To reduce fragmentation, "qrealloc()" reallocates memory in */
/* blocks of "QM_RAFF" bytes. */
/* To accomplish this, the following code rounds "u_nb" up to */
/* the nearest "QM_RAFF" boundary. The adjustment has no */
/* effect if "u_nb" is already aligned on a "QM_RAFF" */
/* boundary. */
/* The adjustment is suppressed if the adjusted value will not */
/* fit into an unsigned integer. */
/* ------------------------------------------------------------ */
u_lx = (x3u_long) u_nb;
u_lx = (((u_lx - ONE) / QM_RAFF) + ONE) * QM_RAFF;
ux = (x3u_int) u_lx;
if (u_lx == (x3u_long) ux) u_nb = ux;
/* ------------------------------------------------------------ */
/* is reallocation necessary? */
if ((u_nb <= u_old) && ((u_old - u_nb) < QM_RAFF))
{ /* no */
return (cp); /* return the existing pointer */
}
/* ------------------------------------------------------------ */
/* determine the copy count */
/* The following code sets "u_nc" to the number of data bytes */
/* to be copied. */
u_nc = (u_nb < u_old) ? u_nb : u_old;
/* ------------------------------------------------------------ */
/* reallocate the block */
/* allocate new storage */
result = qmalloc (qp,(int) u_nb);
/* copy the data */
#ifdef QMSA /* stand-alone version */
{
char *p1 = result;
char *p2 = cp;
while (u_nc-- > ZERO) *p1++ = *p2++;
}
#else /* "malloc"-based version */
V_CAST memcpy (result, cp, u_nc);
#endif /* endif "QMSA" */
qfree (cp); /* free the old block */
return (result); /* return the result pointer */
}
/* ------------------------------------------------------------ */
/* simplified QMALLOC interface layer */
/* ------------------------------------------------------------ */
/* The following interface layer can be used by application */
/* programs which do not require multiple QLISTs. */
/* ------------------------------------------------------------ */
/* Function: V_OBJ *sqCalloc(i,j) */
/* Summary: Similar to "calloc(i,j)". */
/* ------------------------------------------------------------ */
/* x3u_int i,j; - Product of these two integers gives the */
/* number of bytes to allocate. */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *sqCalloc(i,j)
x3u_int i;
x3u_int j;
{
return ((V_OBJ *) qcalloc (QM_SQL, i, j));
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC sqChkHeap() */
/* Summary: Checks the entire "sq..." heap. */
/* ------------------------------------------------------------ */
/* "sqChkHeap()" performs some consistency checks on the */
/* "sq..." heap. */
/* ------------------------------------------------------------ */
MODPUB V_FUNC sqChkHeap()
{
chk_qma (QM_SQL);
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC sqChkPtr (p, s) */
/* Summary: Checks an allocated storage pointer. */
/* ------------------------------------------------------------ */
/* V_OBJ *p; - Pointer previously returned by one of the */
/* "sq..." memory allocation functions. */
/* char *s; - Message string (see below). */
/* "sqChkPtr()" performs some consistency checks on the speci- */
/* fied pointer. If the pointer is invalid, "sqChkPtr()" pro- */
/* duces an error message and terminates the caller. The error */
/* message includes the specified message string. */
/* ------------------------------------------------------------ */
MODPUB V_FUNC sqChkPtr (p, s)
V_OBJ *p;
char *s;
{
if (p == NULLVP)
{
if ((s == NULLCP) || (*s == CH_EOS))
{
x_panic ("null pointer");
}
else
{
x_panic ("null pointer: %s",s);
}
}
if ((s != NULLCP) && (*s != CH_EOS)) qm_ce_msg = s;
chk_qm0 (p);
qm_ce_msg = NULLCP;
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC sqFlush() */
/* Summary: Frees all "sq..." storage. */
/* ------------------------------------------------------------ */
/* "sqFlush()" releases all of the storage previously allocated */
/* by "sq..." memory allocation functions. Memory is dealloca- */
/* ted at the system (or "malloc") level. */
/* ------------------------------------------------------------ */
MODPUB V_FUNC sqFlush()
{
qm_retsys++;
qflush (QM_SQL);
qm_retsys--;
}
/* ------------------------------------------------------------ */
/* Function: V_FUNC sqFree (p) */
/* Summary: Frees one storage block. */
/* ------------------------------------------------------------ */
/* V_OBJ *p; - Pointer previously returned by one of the */
/* "sq..." memory allocation functions. */
/* "sqFree(p)" deallocates the storage associated with the */
/* specified pointer. */
/* Storage is returned to the "sq..." dynamic memory pool. It */
/* is not released to the system. (Use "sqFlush()" to deallo- */
/* cate the "sq..." heap at the system level.) */
/* ------------------------------------------------------------ */
MODPUB V_FUNC sqFree (p)
V_OBJ *p;
{
qfree (p);
}
/* ------------------------------------------------------------ */
/* Function: V_OBJ *sqMalloc (n) */
/* Summary: Similar to "malloc (n)". */
/* ------------------------------------------------------------ */
/* x3u_int n; - Number of bytes to allocate. */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *sqMalloc(n)
x3u_int n;
{
return (qmalloc (QM_SQL, (int) n));
}
/* ------------------------------------------------------------ */
/* Function: V_OBJ *sqRealloc (p, n) */
/* Summary: Similar to "realloc (p, n)". */
/* ------------------------------------------------------------ */
/* V_OBJ *p; - Pointer previously returned by one of the */
/* "sq..." memory allocation functions. */
/* x3u_int n; - New block size. */
/* ------------------------------------------------------------ */
MODPUB V_OBJ *sqRealloc (p, n)
V_OBJ *p;
x3u_int n;
{
return (qrealloc (p, (int) n));
}