| 1 | /* $Id: alloca.c,v 1.1 1999-12-10 17:11:19 ansari Exp $ */
 | 
|---|
| 2 | 
 | 
|---|
| 3 | /*
 | 
|---|
| 4 |         alloca -- (mostly) portable public-domain implementation -- D A Gwyn
 | 
|---|
| 5 | 
 | 
|---|
| 6 |         last edit:      86/05/30        rms
 | 
|---|
| 7 |            include config.h, since on VMS it renames some symbols.
 | 
|---|
| 8 |            Use xmalloc instead of malloc.
 | 
|---|
| 9 | 
 | 
|---|
| 10 |         This implementation of the PWB library alloca() function,
 | 
|---|
| 11 |         which is used to allocate space off the run-time stack so
 | 
|---|
| 12 |         that it is automatically reclaimed upon procedure exit, 
 | 
|---|
| 13 |         was inspired by discussions with J. Q. Johnson of Cornell.
 | 
|---|
| 14 | 
 | 
|---|
| 15 |         It should work under any C implementation that uses an
 | 
|---|
| 16 |         actual procedure stack (as opposed to a linked list of
 | 
|---|
| 17 |         frames).  There are some preprocessor constants that can
 | 
|---|
| 18 |         be defined when compiling for your specific system, for
 | 
|---|
| 19 |         improved efficiency; however, the defaults should be okay.
 | 
|---|
| 20 | 
 | 
|---|
| 21 |         The general concept of this implementation is to keep
 | 
|---|
| 22 |         track of all alloca()-allocated blocks, and reclaim any
 | 
|---|
| 23 |         that are found to be deeper in the stack than the current
 | 
|---|
| 24 |         invocation.  This heuristic does not reclaim storage as
 | 
|---|
| 25 |         soon as it becomes invalid, but it will do so eventually.
 | 
|---|
| 26 | 
 | 
|---|
| 27 |         As a special case, alloca(0) reclaims storage without
 | 
|---|
| 28 |         allocating any.  It is a good idea to use alloca(0) in
 | 
|---|
| 29 |         your main control loop, etc. to force garbage collection.
 | 
|---|
| 30 | */
 | 
|---|
| 31 | #ifndef lint
 | 
|---|
| 32 | static char     SCCSid[] = "@(#)alloca.c        1.1";   /* for the "what" utility */
 | 
|---|
| 33 | #endif
 | 
|---|
| 34 | 
 | 
|---|
| 35 | #ifdef __MWERKS__
 | 
|---|
| 36 | #pragma once
 | 
|---|
| 37 | #include <stdlib.h>
 | 
|---|
| 38 | #include "alloca.h"
 | 
|---|
| 39 | void *alloca (unsigned);
 | 
|---|
| 40 | static void *xmalloc (unsigned);
 | 
|---|
| 41 | #endif /* THINK_C */
 | 
|---|
| 42 | 
 | 
|---|
| 43 | #include <stdio.h>
 | 
|---|
| 44 | 
 | 
|---|
| 45 | #ifdef emacs
 | 
|---|
| 46 | #include "config.h"
 | 
|---|
| 47 | /* THINK_C chokes on following line, even though emacs is not defined
 | 
|---|
| 48 | #ifdef static
 | 
|---|
| 49 | */
 | 
|---|
| 50 | /* actually, only want this if static is defined as ""
 | 
|---|
| 51 |    -- this is for usg, in which emacs must undefine static
 | 
|---|
| 52 |    in order to make unexec workable
 | 
|---|
| 53 |    */
 | 
|---|
| 54 | #ifndef STACK_DIRECTION
 | 
|---|
| 55 | you
 | 
|---|
| 56 | lose
 | 
|---|
| 57 | -- must know STACK_DIRECTION at compile-time
 | 
|---|
| 58 | #endif /* STACK_DIRECTION undefined */
 | 
|---|
| 59 | /*
 | 
|---|
| 60 | #endif /* static */
 | 
|---|
| 61 | #endif /* emacs */
 | 
|---|
| 62 | 
 | 
|---|
| 63 | #ifndef alloca  /* If compiling with GCC, this file's not needed.  */
 | 
|---|
| 64 | 
 | 
|---|
| 65 | #ifdef __STDC__
 | 
|---|
| 66 | typedef void    *pointer;               /* generic pointer type */
 | 
|---|
| 67 | #else
 | 
|---|
| 68 | typedef char    *pointer;               /* generic pointer type */
 | 
|---|
| 69 | #endif
 | 
|---|
| 70 | 
 | 
|---|
| 71 | #ifndef NULL
 | 
|---|
| 72 | #define NULL    0                       /* null pointer constant */
 | 
|---|
| 73 | #endif
 | 
|---|
| 74 | 
 | 
|---|
| 75 | extern void     free();
 | 
|---|
| 76 | extern pointer  xmalloc();
 | 
|---|
| 77 | 
 | 
|---|
| 78 | /*
 | 
|---|
| 79 |         Define STACK_DIRECTION if you know the direction of stack
 | 
|---|
| 80 |         growth for your system; otherwise it will be automatically
 | 
|---|
| 81 |         deduced at run-time.
 | 
|---|
| 82 | 
 | 
|---|
| 83 |         STACK_DIRECTION > 0 => grows toward higher addresses
 | 
|---|
| 84 |         STACK_DIRECTION < 0 => grows toward lower addresses
 | 
|---|
| 85 |         STACK_DIRECTION = 0 => direction of growth unknown
 | 
|---|
| 86 | */
 | 
|---|
| 87 | 
 | 
|---|
| 88 | #ifndef STACK_DIRECTION
 | 
|---|
| 89 | #define STACK_DIRECTION 0               /* direction unknown */
 | 
|---|
| 90 | #endif
 | 
|---|
| 91 | 
 | 
|---|
| 92 | #if STACK_DIRECTION != 0
 | 
|---|
| 93 | 
 | 
|---|
| 94 | #define STACK_DIR       STACK_DIRECTION /* known at compile-time */
 | 
|---|
| 95 | 
 | 
|---|
| 96 | #else   /* STACK_DIRECTION == 0; need run-time code */
 | 
|---|
| 97 | 
 | 
|---|
| 98 | static int      stack_dir;              /* 1 or -1 once known */
 | 
|---|
| 99 | #define STACK_DIR       stack_dir
 | 
|---|
| 100 | 
 | 
|---|
| 101 | static void
 | 
|---|
| 102 | find_stack_direction (/* void */)
 | 
|---|
| 103 | {
 | 
|---|
| 104 |   static char   *addr = NULL;   /* address of first
 | 
|---|
| 105 |                                    `dummy', once known */
 | 
|---|
| 106 |   auto char     dummy;          /* to get stack address */
 | 
|---|
| 107 | 
 | 
|---|
| 108 |   if (addr == NULL)
 | 
|---|
| 109 |     {                           /* initial entry */
 | 
|---|
| 110 |       addr = &dummy;
 | 
|---|
| 111 | 
 | 
|---|
| 112 |       find_stack_direction ();  /* recurse once */
 | 
|---|
| 113 |     }
 | 
|---|
| 114 |   else                          /* second entry */
 | 
|---|
| 115 |     if (&dummy > addr)
 | 
|---|
| 116 |       stack_dir = 1;            /* stack grew upward */
 | 
|---|
| 117 |     else
 | 
|---|
| 118 |       stack_dir = -1;           /* stack grew downward */
 | 
|---|
| 119 | }
 | 
|---|
| 120 | 
 | 
|---|
| 121 | #endif  /* STACK_DIRECTION == 0 */
 | 
|---|
| 122 | 
 | 
|---|
| 123 | /*
 | 
|---|
| 124 |         An "alloca header" is used to:
 | 
|---|
| 125 |         (a) chain together all alloca()ed blocks;
 | 
|---|
| 126 |         (b) keep track of stack depth.
 | 
|---|
| 127 | 
 | 
|---|
| 128 |         It is very important that sizeof(header) agree with malloc()
 | 
|---|
| 129 |         alignment chunk size.  The following default should work okay.
 | 
|---|
| 130 | */
 | 
|---|
| 131 | 
 | 
|---|
| 132 | #ifndef ALIGN_SIZE
 | 
|---|
| 133 | #define ALIGN_SIZE      sizeof(double)
 | 
|---|
| 134 | #endif
 | 
|---|
| 135 | 
 | 
|---|
| 136 | typedef union hdr
 | 
|---|
| 137 | {
 | 
|---|
| 138 |   char  align[ALIGN_SIZE];      /* to force sizeof(header) */
 | 
|---|
| 139 |   struct
 | 
|---|
| 140 |     {
 | 
|---|
| 141 |       union hdr *next;          /* for chaining headers */
 | 
|---|
| 142 |       char *deep;               /* for stack depth measure */
 | 
|---|
| 143 |     } h;
 | 
|---|
| 144 | } header;
 | 
|---|
| 145 | 
 | 
|---|
| 146 | /*
 | 
|---|
| 147 |         alloca( size ) returns a pointer to at least `size' bytes of
 | 
|---|
| 148 |         storage which will be automatically reclaimed upon exit from
 | 
|---|
| 149 |         the procedure that called alloca().  Originally, this space
 | 
|---|
| 150 |         was supposed to be taken from the current stack frame of the
 | 
|---|
| 151 |         caller, but that method cannot be made to work for some
 | 
|---|
| 152 |         implementations of C, for example under Gould's UTX/32.
 | 
|---|
| 153 | */
 | 
|---|
| 154 | 
 | 
|---|
| 155 | static header *last_alloca_header = NULL; /* -> last alloca header */
 | 
|---|
| 156 | 
 | 
|---|
| 157 | pointer
 | 
|---|
| 158 | alloca (size)                   /* returns pointer to storage */
 | 
|---|
| 159 |      unsigned   size;           /* # bytes to allocate */
 | 
|---|
| 160 | {
 | 
|---|
| 161 |   auto char     probe;          /* probes stack depth: */
 | 
|---|
| 162 |   register char *depth = &probe;
 | 
|---|
| 163 | 
 | 
|---|
| 164 | #if STACK_DIRECTION == 0
 | 
|---|
| 165 |   if (STACK_DIR == 0)           /* unknown growth direction */
 | 
|---|
| 166 |     find_stack_direction ();
 | 
|---|
| 167 | #endif
 | 
|---|
| 168 | 
 | 
|---|
| 169 |                                 /* Reclaim garbage, defined as all alloca()ed storage that
 | 
|---|
| 170 |                                    was allocated from deeper in the stack than currently. */
 | 
|---|
| 171 | 
 | 
|---|
| 172 |   {
 | 
|---|
| 173 |     register header     *hp;    /* traverses linked list */
 | 
|---|
| 174 | 
 | 
|---|
| 175 |     for (hp = last_alloca_header; hp != NULL;)
 | 
|---|
| 176 |       if ((STACK_DIR > 0 && hp->h.deep > depth)
 | 
|---|
| 177 |           || (STACK_DIR < 0 && hp->h.deep < depth))
 | 
|---|
| 178 |         {
 | 
|---|
| 179 |           register header       *np = hp->h.next;
 | 
|---|
| 180 | 
 | 
|---|
| 181 |           free ((pointer) hp);  /* collect garbage */
 | 
|---|
| 182 | 
 | 
|---|
| 183 |           hp = np;              /* -> next header */
 | 
|---|
| 184 |         }
 | 
|---|
| 185 |       else
 | 
|---|
| 186 |         break;                  /* rest are not deeper */
 | 
|---|
| 187 | 
 | 
|---|
| 188 |     last_alloca_header = hp;    /* -> last valid storage */
 | 
|---|
| 189 |   }
 | 
|---|
| 190 | 
 | 
|---|
| 191 |   if (size == 0)
 | 
|---|
| 192 |     return NULL;                /* no allocation required */
 | 
|---|
| 193 | 
 | 
|---|
| 194 |   /* Allocate combined header + user data storage. */
 | 
|---|
| 195 | 
 | 
|---|
| 196 |   {
 | 
|---|
| 197 |     register pointer    new = xmalloc (sizeof (header) + size);
 | 
|---|
| 198 |     /* address of header */
 | 
|---|
| 199 | 
 | 
|---|
| 200 |     ((header *)new)->h.next = last_alloca_header;
 | 
|---|
| 201 |     ((header *)new)->h.deep = depth;
 | 
|---|
| 202 | 
 | 
|---|
| 203 |     last_alloca_header = (header *)new;
 | 
|---|
| 204 | 
 | 
|---|
| 205 |     /* User storage begins just after header. */
 | 
|---|
| 206 | 
 | 
|---|
| 207 |     return (pointer)((char *)new + sizeof(header));
 | 
|---|
| 208 |   }
 | 
|---|
| 209 | }
 | 
|---|
| 210 | 
 | 
|---|
| 211 | #ifdef __MWERKS__
 | 
|---|
| 212 | static void*
 | 
|---|
| 213 | xmalloc (unsigned size)
 | 
|---|
| 214 | {
 | 
|---|
| 215 |   void *new_mem = (void*) malloc (size);
 | 
|---|
| 216 | 
 | 
|---|
| 217 |   if (new_mem == NULL)
 | 
|---|
| 218 |     {
 | 
|---|
| 219 |       fprintf (stderr, "xmalloc: request for %u bytes failed.\n", size);
 | 
|---|
| 220 |       abort ();
 | 
|---|
| 221 |     }
 | 
|---|
| 222 | 
 | 
|---|
| 223 |   return new_mem;
 | 
|---|
| 224 | }
 | 
|---|
| 225 | #endif /* THINK_C */
 | 
|---|
| 226 | 
 | 
|---|
| 227 | #endif /* no alloca */
 | 
|---|