home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 4
/
DATAFILE_PDCD4.iso
/
unix
/
unixtools
/
util
/
c
/
alloca
next >
Wrap
Text File
|
1992-07-21
|
5KB
|
167 lines
/*
alloca -- (mostly) portable public-domain implementation -- D A Gwyn
Arc port: 90/01/28 pfm
Convert to work on Archimedes. Note that the assumption
of a simple procedure stack is NOT guaranteed under the
Arm procedure call standard. C Version 3.00 may well
violate it (but the only result should be a delay in
reclaiming some storage... I hope).
last edit: 86/05/30 rms
include config.h, since on VMS it renames some symbols.
Use xmalloc instead of malloc.
This implementation of the PWB library alloca() function,
which is used to allocate space off the run-time stack so
that it is automatically reclaimed upon procedure exit,
was inspired by discussions with J. Q. Johnson of Cornell.
It should work under any C implementation that uses an
actual procedure stack (as opposed to a linked list of
frames). There are some preprocessor constants that can
be defined when compiling for your specific system, for
improved efficiency; however, the defaults should be okay.
The general concept of this implementation is to keep
track of all alloca()-allocated blocks, and reclaim any
that are found to be deeper in the stack than the current
invocation. This heuristic does not reclaim storage as
soon as it becomes invalid, but it will do so eventually.
As a special case, alloca(0) reclaims storage without
allocating any. It is a good idea to use alloca(0) in
your main control loop, etc. to force garbage collection.
*/
#include <stdlib.h>
#include <stddef.h>
#include "utils.h"
/*
Define STACK_DIRECTION if you know the direction of stack
growth for your system; otherwise it will be automatically
deduced at run-time.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown
*/
#ifndef STACK_DIRECTION
#define STACK_DIRECTION 0 /* direction unknown */
#endif
#if STACK_DIRECTION != 0
/* known at compile-time */
#define STACK_DIR STACK_DIRECTION
#else
/* STACK_DIRECTION == 0; need run-time code */
static int stack_dir; /* 1 or -1 once known */
#define STACK_DIR stack_dir
static void find_stack_direction (void)
{
static char *addr = NULL; /* address of first 'dummy', once known */
auto char dummy; /* to get stack address */
if (addr == NULL)
{
/* initial entry */
addr = &dummy;
/* recurse once */
find_stack_direction ();
}
else if (&dummy > addr) /* second entry */
stack_dir = 1; /* stack grew upward */
else
stack_dir = -1; /* stack grew downward */
}
#endif /* STACK_DIRECTION == 0 */
/*
An "alloca header" is used to:
(a) chain together all alloca()ed blocks;
(b) keep track of stack depth.
It is very important that sizeof(header) agree with malloc()
alignment chunk size. The following default should work okay.
*/
#ifndef ALIGN_SIZE
#define ALIGN_SIZE sizeof(double)
#endif
typedef union hdr
{
char align[ALIGN_SIZE]; /* to force sizeof(header) */
struct
{
union hdr *next; /* for chaining headers */
char *deep; /* for stack depth measure */
} h;
}
header;
/*
alloca(size) returns a pointer to at least `size' bytes of
storage which will be automatically reclaimed upon exit from
the procedure that called alloca(). Originally, this space
was supposed to be taken from the current stack frame of the
caller, but that method cannot be made to work for some
implementations of C, for example under Gould's UTX/32.
*/
static header *last_alloca_header = NULL; /* -> last alloca header */
void *alloca (size_t size)
{
/* variable 'probe' probes stack depth: */
auto char probe;
register char *depth = &probe;
register header *hp;
register header *new;
#if STACK_DIRECTION == 0
if (STACK_DIR == 0) /* unknown growth direction */
find_stack_direction ();
#endif
/* Reclaim garbage, defined as all alloca()ed storage that
was allocated from deeper in the stack than currently. */
for (hp = last_alloca_header; hp != NULL;)
{
if (STACK_DIR > 0 && hp->h.deep > depth
|| STACK_DIR < 0 && hp->h.deep < depth)
{
/* collect garbage */
register header *np = hp->h.next;
free (hp);
hp = np;
}
else /* rest are not deeper */
break;
}
last_alloca_header = hp; /* -> last valid storage */
if (size == 0)
return NULL; /* no allocation required */
/* Allocate combined header + user data storage. */
new = emalloc (sizeof (header) + size);
new->h.next = last_alloca_header;
new->h.deep = depth;
last_alloca_header = new;
/* User storage begins just after header. */
return (void *)((char *)new + sizeof(header));
}