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