mirror of
git://git.psyced.org/git/psyclpc
synced 2024-08-15 03:20:16 +00:00
219 lines
6.8 KiB
C
219 lines
6.8 KiB
C
/*---------------------------------------------------------------------------
|
|
* Gamedriver alloca -- a portable alloca() implementation
|
|
* Based on the public-domain version by D.A. Gwyn.
|
|
*
|
|
*---------------------------------------------------------------------------
|
|
* 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
|
|
* and later extended by Lars Duening.
|
|
*
|
|
* 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, including storage 'cached' by the implementation
|
|
* for fast reuse. It is a good idea to use alloca(0) in
|
|
* your main control loop, etc. to force garbage collection.
|
|
*
|
|
* As opposed to the original implementaion using malloc()/free(),
|
|
* this version utilizes the 'mempools' module to allocate the memory.
|
|
*---------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "mempools.h"
|
|
|
|
typedef void *pointer; /* generic pointer type */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* 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
|
|
*/
|
|
|
|
#if defined(AMIGA)
|
|
# define STACK_DIRECTION -1
|
|
#endif
|
|
|
|
#ifndef STACK_DIRECTION
|
|
# define STACK_DIRECTION 0 /* direction unknown */
|
|
#endif
|
|
|
|
#if STACK_DIRECTION != 0
|
|
|
|
# define STACK_DIR STACK_DIRECTION /* known at compile-time */
|
|
|
|
#else /* STACK_DIRECTION == 0; need run-time code */
|
|
|
|
static int stack_dir; /* 1 or -1 once known */
|
|
# define STACK_DIR stack_dir
|
|
|
|
#endif /* STACK_DIRECTION == 0 */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
/* An "alloca header" is used to:
|
|
* (a) chain together all alloca()ed blocks;
|
|
* (b) keep track of stack depth.
|
|
*/
|
|
|
|
typedef struct hdr
|
|
{
|
|
struct hdr * next; /* for chaining headers */
|
|
char * deep; /* for stack depth measure */
|
|
mempool_t * pool; /* the memory pool */
|
|
} header;
|
|
|
|
static header *last_alloca_header = NULL; /* -> last alloca header */
|
|
static header *free_header = NULL; /* List of unused structures */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#if STACK_DIRECTION == 0
|
|
|
|
static void
|
|
find_stack_direction (void)
|
|
|
|
/* Find the direction of the stackgrowth and store the result (+1 or -1)
|
|
* into the global stack_dir.
|
|
*/
|
|
|
|
{
|
|
static char *addr = NULL; /* address of first `dummy', once known */
|
|
char dummy; /* to get stack address */
|
|
|
|
if (addr == NULL) /* initial call */
|
|
{
|
|
addr = &dummy;
|
|
find_stack_direction (); /* recurse once */
|
|
}
|
|
else /* second entry */
|
|
if (&dummy > addr)
|
|
stack_dir = 1; /* stack grew upward */
|
|
else
|
|
stack_dir = -1; /* stack grew downward */
|
|
}
|
|
|
|
#endif /* STACK_DIRECTION == 0 */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
pointer
|
|
palloca (size_t size)
|
|
|
|
/* Allocate at least <size> bytes of memory "on the stack" and return a
|
|
* pointer to the start of the memory block. Return NULL if running out
|
|
* of memory.
|
|
*
|
|
* Allocating "on the stack" means that every allocation is associated with
|
|
* the call depth of the allocator. Allocations of a higher call depth
|
|
* cause the deallocation of any memory allocated on deepter call depths.
|
|
*
|
|
* Originally this memory was meant to be taken from the stack frame of
|
|
* the caller, but some systems and C implementations do not allow this.
|
|
*
|
|
* An allocation of 0 bytes is a special case, causing the deallocation of
|
|
* every memory block allocated so far.
|
|
*/
|
|
|
|
{
|
|
char probe; /* probes stack depth: */
|
|
register char * depth = &probe;
|
|
register header * hp; /* current header structure */
|
|
|
|
#if STACK_DIRECTION == 0
|
|
if (STACK_DIR == 0) /* (yet)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->deep > depth)
|
|
|| (STACK_DIR < 0 && hp->deep < depth))
|
|
{
|
|
register header *np = hp->next;
|
|
|
|
mempool_reset(hp->pool); /* collect garbage */
|
|
hp->next = free_header;
|
|
free_header = hp;
|
|
hp = np; /* -> next header */
|
|
}
|
|
else
|
|
break; /* rest are not deeper */
|
|
}
|
|
|
|
last_alloca_header = hp; /* -> last valid storage */
|
|
|
|
/* From here on, hp is always == last_alloca_header; the
|
|
* use of the local however is a hint for the optimizer.
|
|
*/
|
|
|
|
if (size == 0)
|
|
{
|
|
while (free_header != NULL)
|
|
{
|
|
header * this = free_header;
|
|
free_header = free_header->next;
|
|
if (this->pool)
|
|
{
|
|
mempool_delete(this->pool);
|
|
}
|
|
free(this);
|
|
}
|
|
return NULL; /* no allocation required */
|
|
}
|
|
|
|
/* Allocate a new header structure if there is non for
|
|
* this particular stack depth. The new structure will
|
|
* be the new bottom of the header list.
|
|
*/
|
|
|
|
if (hp == NULL || hp->deep != depth)
|
|
{
|
|
if (free_header == NULL)
|
|
{
|
|
hp = malloc (sizeof (*hp));
|
|
if (!hp) { fprintf(stderr, "alloca: failed!\n"); return NULL; }
|
|
hp->pool = new_mempool(size_mempool(1));
|
|
if (!hp->pool)
|
|
{
|
|
free(hp);
|
|
fprintf(stderr, "alloca: failed!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hp = free_header;
|
|
free_header = hp->next;
|
|
}
|
|
|
|
hp->next = last_alloca_header;
|
|
hp->deep = depth;
|
|
last_alloca_header = hp;
|
|
}
|
|
|
|
/* The actual allocation is easy: */
|
|
return mempool_alloc(hp->pool, size);
|
|
}
|
|
|
|
/***************************************************************************/
|