| [683] | 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 */ | 
|---|