1396 lines
53 KiB
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));
|
|
}
|