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 */
 | 
