diff lib/alloca.c @ 9:f8dce34b5ab0

Initial revision
author Jim Meyering <jim@meyering.net>
date Sun, 01 Nov 1992 05:44:30 +0000 (1992-11-01)
parents
children 2323794dc2ce 3024905f1d5f
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/lib/alloca.c
@@ -0,0 +1,194 @@
+/*
+	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 */