1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-08 15:52:53 +02:00
2015-04-07 22:10:17 -05:00

763 lines
19 KiB
C

/* xldmem - xlisp dynamic memory management routines */
/* Copyright (c) 1985, by David Michael Betz
All Rights Reserved
Permission is granted for unrestricted non-commercial use
* HISTORY
* 28-Apr-03 Mazzoni
* eliminate some compiler warnings
* 14-Apr-88 Dannenberg
* Call free method when an EXTERN node is garbage collected
*/
/* #define DEBUG_MEM 1 */
#include "stdlib.h"
#include "string.h"
#include "xlisp.h"
#ifdef WIN32
#include "malloc.h" // defines alloca()
#endif
/* node flags */
#define MARK 1
#define LEFT 2
/* macro to compute the size of a segment */
#define segsize(n) (sizeof(SEGMENT)+((n)-1)*sizeof(struct node))
#ifdef DEBUG_INPUT
extern FILE *debug_input_fp;
#endif
/* variables local to xldmem.c and xlimage.c */
SEGMENT *segs,*lastseg,*fixseg,*charseg;
int anodes,nsegs,gccalls;
long nnodes,nfree,total;
LVAL fnodes;
#ifdef DEBUG_MEM
long xldmem_trace = 0; /* debugging */
#endif
/* forward declarations */
FORWARD LOCAL void findmem(void);
FORWARD LVAL newnode(int type);
FORWARD LOCAL unsigned char *stralloc(int size);
FORWARD LOCAL int addseg(void);
FORWARD void mark(LVAL ptr);
FORWARD LOCAL void sweep(void);
#ifdef DEBUG_GC
static long dbg_gc_n = 0; /* counts save operations */
long dbg_gc_count = 0; /* says when to stop */
LVAL *dbg_gc_addr = NULL; /* says what we're looking for */
void dbg_gc_xlsave(LVAL *n)
{
dbg_gc_n++;
if (n == dbg_gc_addr) {
printf("dbg_gc_xlsave: %x at count %d\n",
dbg_gc_addr, dbg_gc_n);
}
if (dbg_gc_count == dbg_gc_n) {
printf("dbg_gc_xlsave: reached %d\n",
dbg_gc_count);
}
}
#endif
/* cons - construct a new cons node */
LVAL cons(LVAL x, LVAL y)
{
LVAL nnode;
/* get a free node */
if ((nnode = fnodes) == NIL) {
xlstkcheck(2);
xlprotect(x);
xlprotect(y);
findmem();
if ((nnode = fnodes) == NIL)
xlabort("insufficient node space");
xlpop();
xlpop();
}
/* unlink the node from the free list */
fnodes = cdr(nnode);
--nfree;
/* initialize the new node */
nnode->n_type = CONS;
rplaca(nnode,x);
rplacd(nnode,y);
/* return the new node */
return (nnode);
}
/* cvstring - convert a string to a string node */
LVAL cvstring(const char *str)
{
LVAL val;
xlsave1(val);
val = newnode(STRING);
val->n_strlen = strlen(str) + 1;
val->n_string = stralloc(getslength(val));
strcpy((char *) getstring(val),str);
xlpop();
return (val);
}
/* new_string - allocate and initialize a new string */
LVAL new_string(int size)
{
LVAL val;
xlsave1(val);
val = newnode(STRING);
val->n_strlen = size;
val->n_string = stralloc(getslength(val));
strcpy((char *) getstring(val),"");
xlpop();
return (val);
}
/* cvsymbol - convert a string to a symbol */
LVAL cvsymbol(const char *pname)
{
/* pname points to a global buffer space. This is ok unless you have
* a gc hook that writes things and therefore uses the buffer. Then
* if newvector causes a GC, pname is overwritten before cvstring is
* called and the symbol will have the wrong name!
* The bug is fixed by copying pname to the stack.
*/
LVAL val;
int len = strlen(pname) + 1; /* don't forget the terminating zero */
char *local_pname_copy = (char *) alloca(len);
memcpy(local_pname_copy, pname, len);
xlsave1(val);
val = newvector(SYMSIZE);
val->n_type = SYMBOL;
setvalue(val,s_unbound);
setfunction(val,s_unbound);
setpname(val,cvstring(local_pname_copy));
xlpop();
return (val);
}
/* cvsubr - convert a function to a subr or fsubr */
LVAL cvsubr(LVAL (*fcn)(void), int type, int offset)
{
LVAL val;
val = newnode(type);
val->n_subr = fcn;
val->n_offset = offset;
return (val);
}
/* cvfile - convert a file pointer to a stream */
LVAL cvfile(FILE *fp)
{
LVAL val;
val = newnode(STREAM);
setfile(val,fp);
setsavech(val,'\0');
return (val);
}
/* cvfixnum - convert an integer to a fixnum node */
LVAL cvfixnum(FIXTYPE n)
{
LVAL val;
if (n >= SFIXMIN && n <= SFIXMAX)
return (&fixseg->sg_nodes[(int)n-SFIXMIN]);
val = newnode(FIXNUM);
val->n_fixnum = n;
return (val);
}
/* cvflonum - convert a floating point number to a flonum node */
LVAL cvflonum(FLOTYPE n)
{
LVAL val;
val = newnode(FLONUM);
val->n_flonum = n;
return (val);
}
/* cvchar - convert an integer to a character node */
LVAL cvchar(int n)
{
if (n >= CHARMIN && n <= CHARMAX)
return (&charseg->sg_nodes[n-CHARMIN]);
xlerror("character code out of range",cvfixnum((FIXTYPE)n));
return NIL; /* won't reach this line */
}
/* newustream - create a new unnamed stream */
LVAL newustream(void)
{
LVAL val;
val = newnode(USTREAM);
sethead(val,NIL);
settail(val,NIL);
return (val);
}
/* newobject - allocate and initialize a new object */
LVAL newobject(LVAL cls, int size)
{
LVAL val;
val = newvector(size+1);
val->n_type = OBJECT;
setelement(val,0,cls);
return (val);
}
/* newclosure - allocate and initialize a new closure */
LVAL newclosure(LVAL name, LVAL type, LVAL env, LVAL fenv)
{
LVAL val;
val = newvector(CLOSIZE);
val->n_type = CLOSURE;
setname(val,name);
settype(val,type);
setenv(val,env);
setfenv(val,fenv);
return (val);
}
/* newvector - allocate and initialize a new vector node */
LVAL newvector(int size)
{
LVAL vect;
int bsize;
xlsave1(vect);
vect = newnode(VECTOR);
vect->n_vsize = 0;
if ((bsize = size * sizeof(LVAL))) {
if ((vect->n_vdata = (LVAL *)calloc(1,bsize)) == NULL) {
findmem();
if ((vect->n_vdata = (LVAL *)calloc(1,bsize)) == NULL)
xlfail("insufficient vector space");
}
vect->n_vsize = size;
total += (long) bsize;
}
xlpop();
return (vect);
}
/* newnode - allocate a new node */
LVAL newnode(int type)
{
LVAL nnode;
/* get a free node */
if ((nnode = fnodes) == NIL) {
findmem();
if ((nnode = fnodes) == NIL)
xlabort("insufficient node space");
}
/* unlink the node from the free list */
fnodes = cdr(nnode);
nfree -= 1L;
/* initialize the new node */
nnode->n_type = type;
rplacd(nnode,NIL);
/* return the new node */
return (nnode);
}
/* stralloc - allocate memory for a string adding a byte for the terminator */
LOCAL unsigned char *stralloc(int size)
{
unsigned char *sptr;
/* allocate memory for the string copy */
if ((sptr = (unsigned char *)malloc(size)) == NULL) {
gc();
if ((sptr = (unsigned char *)malloc(size)) == NULL)
xlfail("insufficient string space");
}
total += (long)size;
/* return the new string memory */
return (sptr);
}
/* findmem - find more memory by collecting then expanding */
LOCAL void findmem(void)
{
gc();
if (nfree < (long)anodes)
addseg();
}
/* gc - garbage collect (only called here and in xlimage.c) */
void gc(void)
{
register LVAL **p,*ap,tmp;
char buf[STRMAX+1];
LVAL *newfp,fun;
extern LVAL profile_fixnum;
/* print the start of the gc message */
if (s_gcflag && getvalue(s_gcflag)) {
sprintf(buf,"[ gc: total %ld, ",nnodes);
stdputstr(buf);
}
/* mark the fixnum used by profiler */
if (!null(profile_fixnum)) mark(profile_fixnum);
/* mark the obarray, the argument list and the current environment */
if (obarray)
mark(obarray);
if (xlenv)
mark(xlenv);
if (xlfenv)
mark(xlfenv);
if (xldenv)
mark(xldenv);
/* mark the evaluation stack */
for (p = xlstack; p < xlstktop; ++p)
if ((tmp = **p))
mark(tmp);
/* mark the argument stack */
for (ap = xlargstkbase; ap < xlsp; ++ap)
if ((tmp = *ap))
mark(tmp);
/* sweep memory collecting all unmarked nodes */
sweep();
/* count the gc call */
++gccalls;
/* call the *gc-hook* if necessary */
if (s_gchook && (fun = getvalue(s_gchook))) {
newfp = xlsp;
pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
pusharg(fun);
pusharg(cvfixnum((FIXTYPE)2));
pusharg(cvfixnum((FIXTYPE)nnodes));
pusharg(cvfixnum((FIXTYPE)nfree));
xlfp = newfp;
xlapply(2);
}
/* print the end of the gc message */
if (s_gcflag && getvalue(s_gcflag)) {
sprintf(buf,"%ld free", nfree);
stdputstr(buf);
/* print additional info (e.g. sound blocks in Nyquist) */
print_local_gc_info();
stdputstr(" ]\n");
stdflush(); /* output in a timely fashion so user sees progress */
}
#ifdef DEBUG_INPUT
if (debug_input_fp) {
int c = getc(debug_input_fp);
ungetc(c, debug_input_fp);
}
#endif
}
/* mark - mark all accessible nodes */
void mark(LVAL ptr)
{
register LVAL this,prev,tmp;
int type,i,n;
/* initialize */
prev = NIL;
this = ptr;
/* mark this list */
for (;;) {
/* descend as far as we can */
while (!(this->n_flags & MARK))
/* check cons and symbol nodes */
if (((type = ntype(this))) == CONS || type == USTREAM) {
if ((tmp = car(this))) {
this->n_flags |= MARK|LEFT;
rplaca(this,prev);
}
else if ((tmp = cdr(this))) {
this->n_flags |= MARK;
rplacd(this,prev);
}
else { /* both sides nil */
this->n_flags |= MARK;
break;
}
prev = this; /* step down the branch */
this = tmp;
}
/* mark other node types */
else {
this->n_flags |= MARK;
switch (type) {
case SYMBOL:
case OBJECT:
case VECTOR:
case CLOSURE:
for (i = 0, n = getsize(this); --n >= 0; ++i)
if ((tmp = getelement(this,i)))
mark(tmp);
break;
case EXTERN:
if (getdesc(this)->mark_meth) { (*(getdesc(this)->mark_meth))(getinst(this));
}
}
break;
}
/* backup to a point where we can continue descending */
for (;;)
/* make sure there is a previous node */
if (prev) {
if (prev->n_flags & LEFT) { /* came from left side */
prev->n_flags &= ~LEFT;
tmp = car(prev);
rplaca(prev,this);
if ((this = cdr(prev))) {
rplacd(prev,tmp);
break;
}
}
else { /* came from right side */
tmp = cdr(prev);
rplacd(prev,this);
}
this = prev; /* step back up the branch */
prev = tmp;
}
/* no previous node, must be done */
else
return;
}
}
/* sweep - sweep all unmarked nodes and add them to the free list */
LOCAL void sweep(void)
{
SEGMENT *seg;
LVAL p;
int n;
/* empty the free list */
fnodes = NIL;
nfree = 0L;
/* add all unmarked nodes */
for (seg = segs; seg; seg = seg->sg_next) {
if (seg == fixseg) /* don't sweep the fixnum segment */
continue;
else if (seg == charseg) /* don't sweep the character segment */
continue;
p = &seg->sg_nodes[0];
for (n = seg->sg_size; --n >= 0; ++p) {
#ifdef DEBUG_MEM
if (xldmem_trace &&
ntype(p) == EXTERN &&
xldmem_trace == getinst(p)) {
printf("sweep: EXTERN node %lx is %smarked, points to %lx\n",
p, (p->n_flags & MARK ? "" : "un"), getinst(p));
}
#endif
if (!(p->n_flags & MARK)) {
switch (ntype(p)) {
case STRING:
if (getstring(p) != NULL) {
total -= (long)getslength(p);
free(getstring(p));
}
break;
case STREAM:
if (getfile(p))
osclose(getfile(p));
break;
case SYMBOL:
case OBJECT:
case VECTOR:
case CLOSURE:
if (p->n_vsize) {
total -= (long) (p->n_vsize * sizeof(LVAL));
free((void *) p->n_vdata);
}
break;
case EXTERN:
/* printf("GC about to free %x\n", p);
* fflush(stdout);
*/
if (getdesc(p)) { (*(getdesc(p)->free_meth))(getinst(p));
}
break;
}
p->n_type = FREE_NODE;
rplaca(p,NIL);
rplacd(p,fnodes);
fnodes = p;
nfree += 1L;
}
else
p->n_flags &= ~MARK;
}
}
}
/* addseg - add a segment to the available memory */
LOCAL int addseg(void)
{
SEGMENT *newseg;
LVAL p;
int n;
/* allocate the new segment */
if (anodes == 0 || (newseg = newsegment(anodes)) == NULL)
return (FALSE);
/* add each new node to the free list */
p = &newseg->sg_nodes[0];
for (n = anodes; --n >= 0; ++p) {
rplacd(p,fnodes);
fnodes = p;
}
/* return successfully */
return (TRUE);
}
/* newsegment - create a new segment (only called here and in xlimage.c) */
SEGMENT *newsegment(int n)
{
SEGMENT *newseg;
/* allocate the new segment */
if ((newseg = (SEGMENT *)calloc(1,segsize(n))) == NULL)
return (NULL);
/* initialize the new segment */
newseg->sg_size = n;
newseg->sg_next = NULL;
if (segs)
lastseg->sg_next = newseg;
else
segs = newseg;
lastseg = newseg;
/* update the statistics */
total += (long)segsize(n);
nnodes += (long)n;
nfree += (long)n;
++nsegs;
/* return the new segment */
return (newseg);
}
/* stats - print memory statistics */
LOCAL void stats(void)
{
sprintf(buf,"Nodes: %ld\n",nnodes); stdputstr(buf);
sprintf(buf,"Free nodes: %ld\n",nfree); stdputstr(buf);
sprintf(buf,"Segments: %d\n",nsegs); stdputstr(buf);
sprintf(buf,"Allocate: %d\n",anodes); stdputstr(buf);
sprintf(buf,"Total: %ld\n",total); stdputstr(buf);
sprintf(buf,"Collections: %d\n",gccalls); stdputstr(buf);
}
/* xgc - xlisp function to force garbage collection */
LVAL xgc(void)
{
/* make sure there aren't any arguments */
xllastarg();
/* garbage collect */
gc();
/* return nil */
return (NIL);
}
/* xexpand - xlisp function to force memory expansion */
LVAL xexpand(void)
{
LVAL num;
int n,i;
/* get the new number to allocate */
if (moreargs()) {
num = xlgafixnum();
n = getfixnum(num);
}
else
n = 1;
xllastarg();
/* allocate more segments */
for (i = 0; i < n; i++)
if (!addseg())
break;
/* return the number of segments added */
return (cvfixnum((FIXTYPE)i));
}
/* xalloc - xlisp function to set the number of nodes to allocate */
LVAL xalloc(void)
{
int n,oldn;
LVAL num;
/* get the new number to allocate */
num = xlgafixnum();
n = getfixnum(num);
/* make sure there aren't any more arguments */
xllastarg();
/* set the new number of nodes to allocate */
oldn = anodes;
anodes = n;
/* return the old number */
return (cvfixnum((FIXTYPE)oldn));
}
/* xmem - xlisp function to print memory statistics */
LVAL xmem(void)
{
/* allow one argument for compatiblity with common lisp */
if (moreargs()) xlgetarg();
xllastarg();
/* print the statistics */
stats();
/* return nil */
return (NIL);
}
/* xinfo - show information on control-t */
LVAL xinfo()
{
char buf[80];
sprintf(buf,"\n[ Free: %d, GC calls: %d, Total: %d",
(int)nfree, (int)gccalls, (int)total);
stdputstr(buf);
print_local_gc_info();
stdputstr("]\n");
return NULL;
}
#ifdef SAVERESTORE
/* xsave - save the memory image */
LVAL xsave(void)
{
unsigned char *name;
/* get the file name, verbose flag and print flag */
name = getstring(xlgetfname());
xllastarg();
/* save the memory image */
return (xlisave((char *) name) ? s_true : NIL);
}
/* xrestore - restore a saved memory image */
LVAL xrestore(void)
{
extern jmp_buf top_level;
unsigned char *name;
/* get the file name, verbose flag and print flag */
name = getstring(xlgetfname());
xllastarg();
/* restore the saved memory image */
if (!xlirestore((char *) name))
return (NIL);
/* return directly to the top level */
stdputstr("[ returning to the top level ]\n");
_longjmp(top_level,1);
}
#endif
/* xlminit - initialize the dynamic memory module */
void xlminit(void)
{
LVAL p;
int i;
/* initialize our internal variables */
segs = lastseg = NULL;
nnodes = nfree = total = 0L;
nsegs = gccalls = 0;
anodes = NNODES;
fnodes = NIL;
/* allocate the fixnum segment */
if ((fixseg = newsegment(SFIXSIZE)) == NULL)
xlfatal("insufficient memory");
/* initialize the fixnum segment */
p = &fixseg->sg_nodes[0];
for (i = SFIXMIN; i <= SFIXMAX; ++i) {
p->n_type = FIXNUM;
p->n_fixnum = i;
++p;
}
/* allocate the character segment */
if ((charseg = newsegment(CHARSIZE)) == NULL)
xlfatal("insufficient memory");
/* initialize the character segment */
p = &charseg->sg_nodes[0];
for (i = CHARMIN; i <= CHARMAX; ++i) {
p->n_type = CHAR;
p->n_chcode = i;
++p;
}
/* initialize structures that are marked by the collector */
obarray = xlenv = xlfenv = xldenv = NIL;
s_gcflag = s_gchook = NIL;
/* allocate the evaluation stack */
if ((xlstkbase = (LVAL **)malloc(EDEPTH * sizeof(LVAL *))) == NULL)
xlfatal("insufficient memory");
xlstack = xlstktop = xlstkbase + EDEPTH;
/* allocate the argument stack */
if ((xlargstkbase = (LVAL *)malloc(ADEPTH * sizeof(LVAL))) == NULL)
xlfatal("insufficient memory");
// printf("ADEPTH is %d\n", ADEPTH);
xlargstktop = xlargstkbase + ADEPTH;
xlfp = xlsp = xlargstkbase;
*xlsp++ = NIL;
}