196 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
	alloca -- (mostly) portable public-domain implementation -- D A Gwyn
 | 
						|
 | 
						|
	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.
 | 
						|
*/
 | 
						|
#ifndef lint
 | 
						|
static char	SCCSid[] = "@(#)alloca.c	1.1";	/* for the "what" utility */
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef emacs
 | 
						|
#include "config.h"
 | 
						|
#ifdef static
 | 
						|
/* actually, only want this if static is defined as ""
 | 
						|
   -- this is for usg, in which emacs must undefine static
 | 
						|
   in order to make unexec workable
 | 
						|
   */
 | 
						|
#ifndef STACK_DIRECTION
 | 
						|
you
 | 
						|
lose
 | 
						|
-- must know STACK_DIRECTION at compile-time
 | 
						|
#endif /* STACK_DIRECTION undefined */
 | 
						|
#endif /* static */
 | 
						|
#endif /* emacs */
 | 
						|
 | 
						|
#ifndef alloca  /* If compiling with GCC, this file's not needed.  */
 | 
						|
 | 
						|
#ifdef __STDC__
 | 
						|
typedef void	*pointer;		/* generic pointer type */
 | 
						|
#else
 | 
						|
typedef char	*pointer;		/* generic pointer type */
 | 
						|
#endif
 | 
						|
 | 
						|
#define	NULL	0			/* null pointer constant */
 | 
						|
 | 
						|
extern void	free();
 | 
						|
extern pointer	xmalloc();
 | 
						|
 | 
						|
/*
 | 
						|
	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
 | 
						|
 | 
						|
#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
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
      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 */
 | 
						|
 | 
						|
/*
 | 
						|
	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 */
 | 
						|
 | 
						|
pointer
 | 
						|
alloca (size)			/* returns pointer to storage */
 | 
						|
     unsigned	size;		/* # bytes to allocate */
 | 
						|
{
 | 
						|
  auto char	probe;		/* probes stack depth: */
 | 
						|
  register char	*depth = &probe;
 | 
						|
 | 
						|
#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. */
 | 
						|
 | 
						|
  {
 | 
						|
    register header	*hp;	/* traverses linked list */
 | 
						|
 | 
						|
    for (hp = last_alloca_header; hp != NULL;)
 | 
						|
      if ((STACK_DIR > 0 && hp->h.deep > depth)
 | 
						|
	  || (STACK_DIR < 0 && hp->h.deep < depth))
 | 
						|
	{
 | 
						|
	  register header	*np = hp->h.next;
 | 
						|
 | 
						|
	  free ((pointer) hp);	/* collect garbage */
 | 
						|
 | 
						|
	  hp = np;		/* -> next header */
 | 
						|
	}
 | 
						|
      else
 | 
						|
	break;			/* rest are not deeper */
 | 
						|
 | 
						|
    last_alloca_header = hp;	/* -> last valid storage */
 | 
						|
  }
 | 
						|
 | 
						|
  if (size == 0)
 | 
						|
    return NULL;		/* no allocation required */
 | 
						|
 | 
						|
  /* Allocate combined header + user data storage. */
 | 
						|
 | 
						|
  {
 | 
						|
    register pointer	new = xmalloc (sizeof (header) + size);
 | 
						|
 | 
						|
    /* address of header */
 | 
						|
 | 
						|
    ((header *)new)->h.next = last_alloca_header;
 | 
						|
    ((header *)new)->h.deep = depth;
 | 
						|
 | 
						|
    last_alloca_header = (header *)new;
 | 
						|
 | 
						|
    /* User storage begins just after header. */
 | 
						|
 | 
						|
    return (pointer)((char *)new + sizeof(header));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#endif /* no alloca */
 |