|
| 1 | +#ifndef lint |
| 2 | +static char *RCSid = "$Id: alloc.c,v 1.1 1999/03/26 21:47:19 lhecking Exp $"; |
| 3 | +#endif |
| 4 | + |
| 5 | +/* GNUPLOT - alloc.c */ |
| 6 | + |
| 7 | +/*[ |
| 8 | + * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley |
| 9 | + * |
| 10 | + * Permission to use, copy, and distribute this software and its |
| 11 | + * documentation for any purpose with or without fee is hereby granted, |
| 12 | + * provided that the above copyright notice appear in all copies and |
| 13 | + * that both that copyright notice and this permission notice appear |
| 14 | + * in supporting documentation. |
| 15 | + * |
| 16 | + * Permission to modify the software is granted, but not the right to |
| 17 | + * distribute the complete modified source code. Modifications are to |
| 18 | + * be distributed as patches to the released version. Permission to |
| 19 | + * distribute binaries produced by compiling modified sources is granted, |
| 20 | + * provided you |
| 21 | + * 1. distribute the corresponding source modifications from the |
| 22 | + * released version in the form of a patch file along with the binaries, |
| 23 | + * 2. add special version identification to distinguish your version |
| 24 | + * in addition to the base release version number, |
| 25 | + * 3. provide your name and address as the primary contact for the |
| 26 | + * support of your modified version, and |
| 27 | + * 4. retain our contact information in regard to use of the base |
| 28 | + * software. |
| 29 | + * Permission to distribute the released version of the source code along |
| 30 | + * with corresponding source modifications in the form of a patch file is |
| 31 | + * granted with same provisions 2 through 4 for binary distributions. |
| 32 | + * |
| 33 | + * This software is provided "as is" without express or implied warranty |
| 34 | + * to the extent permitted by applicable law. |
| 35 | +]*/ |
| 36 | + |
| 37 | +/* |
| 38 | + * AUTHORS |
| 39 | + * |
| 40 | + * Alexander Lehmann (collected functions from misc.c and binary.c) |
| 41 | + * |
| 42 | + */ |
| 43 | + |
| 44 | +#include "plot.h" /* includes "alloc.h" */ |
| 45 | + |
| 46 | +#if defined(MSDOS) && defined(__TURBOC__) && !defined(DOSX286) |
| 47 | +#include <alloc.h> /* for farmalloc, farrealloc */ |
| 48 | +#endif |
| 49 | + |
| 50 | +#if defined(_Windows) && !defined(WIN32) |
| 51 | +#include <windows.h> |
| 52 | +#include <windowsx.h> |
| 53 | +#define farmalloc(s) GlobalAllocPtr(GHND,s) |
| 54 | +#define farrealloc(p,s) GlobalReAllocPtr(p,s,GHND) |
| 55 | +#endif |
| 56 | + |
| 57 | +#ifndef NO_GIH |
| 58 | +#include "help.h" |
| 59 | +#endif |
| 60 | + |
| 61 | +#ifndef GP_FARMALLOC |
| 62 | +# ifdef FARALLOC |
| 63 | +# define GP_FARMALLOC(size) farmalloc ((size)) |
| 64 | +# define GP_FARREALLOC(p,size) farrealloc ((p), (size)) |
| 65 | +# else |
| 66 | +# define GP_FARMALLOC(size) malloc ((size_t)(size)) |
| 67 | +# define GP_FARREALLOC(p,size) realloc ((p), (size_t)(size)) |
| 68 | +# endif |
| 69 | +#endif |
| 70 | + |
| 71 | +/* uncomment if you want to trace all allocs */ |
| 72 | +#define TRACE_ALLOC(x) /*printf x*/ |
| 73 | + |
| 74 | + |
| 75 | +#ifdef CHECK_HEAP_USE |
| 76 | + |
| 77 | +/* This is in no way supported, and in particular it breaks the |
| 78 | + * online help. But it is useful to leave it in in case any |
| 79 | + * heap-corruption bugs turn up. Wont work with FARALLOC |
| 80 | + */ |
| 81 | + |
| 82 | +struct frame_struct { |
| 83 | + char *use; |
| 84 | + int requested_size; |
| 85 | + int pad; /* preserve 8-byte alignment */ |
| 86 | + int checksum; |
| 87 | +}; |
| 88 | + |
| 89 | +struct leak_struct { |
| 90 | + char *file; |
| 91 | + int line; |
| 92 | + int allocated; |
| 93 | +}; |
| 94 | + |
| 95 | +static struct leak_struct leak_stack[40]; /* up to 40 nested leak checks */ |
| 96 | +static struct leak_struct *leak_frame = leak_stack; |
| 97 | + |
| 98 | +static long bytes_allocated = 0; |
| 99 | + |
| 100 | +#define RESERVED_SIZE sizeof(struct frame_struct) |
| 101 | +#define CHECKSUM_INT 0xcaac5e1f |
| 102 | +#define CHECKSUM_FREE 0xf3eed222 |
| 103 | +#define CHECKSUM_CHAR 0xc5 |
| 104 | + |
| 105 | +static void mark(p, size, usage) |
| 106 | +struct frame_struct *p; |
| 107 | +unsigned long size; |
| 108 | +char *usage; |
| 109 | +{ |
| 110 | + p->use = usage; |
| 111 | + p->requested_size = size; |
| 112 | + p->checksum = (CHECKSUM_INT ^ (int)(p->use) ^ size); |
| 113 | + ((unsigned char *)(p+1))[size] = CHECKSUM_CHAR; |
| 114 | +} |
| 115 | + |
| 116 | +#define mark_free(p) ( ((struct frame_struct *)p)[-1].checksum = CHECKSUM_FREE) |
| 117 | + |
| 118 | +static void validate(x) |
| 119 | +void *x; |
| 120 | +{ |
| 121 | + struct frame_struct *p = (struct frame_struct *)x - 1; |
| 122 | + if (p->checksum != (CHECKSUM_INT ^ (int)(p->use) ^ p->requested_size)) |
| 123 | + { |
| 124 | + fprintf(stderr, "Heap corruption at start of block for %s\n", p->use); |
| 125 | + if (p->checksum == CHECKSUM_FREE) |
| 126 | + fprintf(stderr, "Looks like it has already been freed ?\n"); |
| 127 | + abort(); |
| 128 | + } |
| 129 | + |
| 130 | + if ( ((unsigned char *)(p+1))[p->requested_size] != CHECKSUM_CHAR) |
| 131 | + { |
| 132 | + fprintf(stderr, "Heap corruption at end of block for %-60s\n", p->use); |
| 133 | + int_error("Argh !", NO_CARET); |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +/* used to confirm that a pointer is inside an allocated region via |
| 138 | + * macro CHECK_POINTER. Nowhere near as good as using a bounds-checking |
| 139 | + * compiler (such as gcc-with-bounds-checking), but when we do |
| 140 | + * come across problems, we can add these guards to the code until |
| 141 | + * we find the problem, and then leave the guards in (as CHECK_POINTER |
| 142 | + * macros which expand to nothing, until we need to re-enable them) |
| 143 | + */ |
| 144 | + |
| 145 | +void check_pointer_in_block(void *block, void *p, int size, char *file, int line) |
| 146 | +{ |
| 147 | + struct frame_struct *f = (struct frame_struct *)block - 1; |
| 148 | + validate(block); |
| 149 | + if (p < block || p >= (block + f->requested_size)) |
| 150 | + { |
| 151 | + fprintf(stderr, "argh - pointer %p outside block %p->%p for %s at %s:%d\n", |
| 152 | + p, block, (char *)block + f->requested_size, f->use, file, line); |
| 153 | + int_error("argh - pointer misuse !", NO_CARET); |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +char *gp_alloc(size, usage) |
| 158 | +unsigned long size; |
| 159 | +char *usage; |
| 160 | +{ |
| 161 | + struct frame_struct *p; |
| 162 | + unsigned long total_size = size + RESERVED_SIZE + 1; |
| 163 | + |
| 164 | + TRACE_ALLOC(("gp_alloc %d for %s\n", (int) size, usage?usage:"<unknown>")); |
| 165 | + |
| 166 | + p=malloc(total_size); |
| 167 | + if (!p) int_error("Out of memory", NO_CARET); |
| 168 | + |
| 169 | + bytes_allocated += size; |
| 170 | + |
| 171 | + mark(p,size,usage); |
| 172 | + |
| 173 | + return (char *)(p+1); |
| 174 | +} |
| 175 | + |
| 176 | +generic *gp_realloc(old, size, usage) |
| 177 | +generic *old; |
| 178 | +unsigned long size; |
| 179 | +char *usage; |
| 180 | +{ |
| 181 | + if (!old) return gp_alloc(size, usage); |
| 182 | + validate(old); |
| 183 | + mark_free(old); /* if block gets moved, old block is marked free */ |
| 184 | + /* if not, we'll remark it later */ |
| 185 | + |
| 186 | + |
| 187 | + { |
| 188 | + struct frame_struct *p = (struct frame_struct *)old - 1; |
| 189 | + unsigned long total = size + RESERVED_SIZE + 1; |
| 190 | + |
| 191 | + p = realloc(p, total); |
| 192 | + |
| 193 | + if (!p) int_error("Out of memory", NO_CARET); |
| 194 | + |
| 195 | + TRACE_ALLOC(("gp_realloc %d for %s (was %d)\n", |
| 196 | + (int)size, usage?usage:"<unknown>", p->requested_size)); |
| 197 | + |
| 198 | + bytes_allocated += size - p->requested_size; |
| 199 | + |
| 200 | + mark(p,size,usage); |
| 201 | + |
| 202 | + return (generic *)(p+1); |
| 203 | + } |
| 204 | +} |
| 205 | + |
| 206 | +#undef free |
| 207 | + |
| 208 | +void checked_free(p) |
| 209 | +void *p; |
| 210 | +{ |
| 211 | + validate(p); |
| 212 | + mark_free(p); /* trap attempts to free twice */ |
| 213 | + TRACE_ALLOC(("free %d for %s\n", |
| 214 | + ((struct frame_struct *)p - 1)->requested_size, |
| 215 | + (((struct frame_struct *)p - 1)->use ? ((struct frame_struct *)p - 1)->use : "(NULL)" ))); |
| 216 | + bytes_allocated -= ((struct frame_struct *)p - 1) -> requested_size; |
| 217 | + free( (struct frame_struct *) p - 1); |
| 218 | +} |
| 219 | + |
| 220 | + |
| 221 | +/* this leak checking stuff will be broken by first int_error or interrupt */ |
| 222 | + |
| 223 | +void start_leak_check(char *file, int line) |
| 224 | +{ |
| 225 | + if (leak_frame >= leak_stack+40) |
| 226 | + { |
| 227 | + fprintf(stderr, "too many nested memory-leak checks - %s:%d\n", file, line); |
| 228 | + return; |
| 229 | + } |
| 230 | + |
| 231 | + leak_frame->file = file; |
| 232 | + leak_frame->line = line; |
| 233 | + leak_frame->allocated = bytes_allocated; |
| 234 | + |
| 235 | + ++leak_frame; |
| 236 | +} |
| 237 | + |
| 238 | +void end_leak_check(char *file, int line) |
| 239 | +{ |
| 240 | + if (--leak_frame < leak_stack) |
| 241 | + { |
| 242 | + fprintf(stderr, "memory-leak stack underflow at %s:%d\n", file, line); |
| 243 | + return; |
| 244 | + } |
| 245 | + |
| 246 | + if (leak_frame->allocated != bytes_allocated) |
| 247 | + { |
| 248 | + fprintf(stderr, "net change of %+d heap bytes between %s:%d and %s:%d\n", |
| 249 | + (int)(bytes_allocated - leak_frame->allocated), |
| 250 | + leak_frame->file, leak_frame->line, file, line); |
| 251 | + } |
| 252 | +} |
| 253 | + |
| 254 | +#else /* CHECK_HEAP_USE */ |
| 255 | + |
| 256 | +/* gp_alloc: |
| 257 | + * allocate memory |
| 258 | + * This is a protected version of malloc. It causes an int_error |
| 259 | + * if there is not enough memory, but first it tries FreeHelp() |
| 260 | + * to make some room, and tries again. If message is NULL, we |
| 261 | + * allow NULL return. Otherwise, we handle the error, using the |
| 262 | + * message to create the int_error string. Note cp/sp_extend uses realloc, |
| 263 | + * so it depends on this using malloc(). |
| 264 | + */ |
| 265 | + |
| 266 | +char * |
| 267 | +gp_alloc(size, message) |
| 268 | + unsigned long size; /* # of bytes */ |
| 269 | + char *message; /* description of what is being allocated */ |
| 270 | +{ |
| 271 | + char *p; /* the new allocation */ |
| 272 | + char errbuf[100]; /* error message string */ |
| 273 | + |
| 274 | +#ifndef NO_GIH |
| 275 | + p = GP_FARMALLOC(size); |
| 276 | + if (p == (char *)NULL) { |
| 277 | + FreeHelp(); /* out of memory, try to make some room */ |
| 278 | +#endif /* NO_GIH */ |
| 279 | + p = GP_FARMALLOC(size); /* try again */ |
| 280 | + if (p == (char *)NULL) { |
| 281 | + /* really out of memory */ |
| 282 | + if (message != NULL) { |
| 283 | + (void) sprintf(errbuf, "out of memory for %s", message); |
| 284 | + int_error(errbuf, NO_CARET); |
| 285 | + /* NOTREACHED */ |
| 286 | + } |
| 287 | + /* else we return NULL */ |
| 288 | + } |
| 289 | +#ifndef NO_GIH |
| 290 | + } |
| 291 | +#endif |
| 292 | + return(p); |
| 293 | +} |
| 294 | + |
| 295 | +/* |
| 296 | + * note gp_realloc assumes that failed realloc calls leave the original mem block |
| 297 | + * allocated. If this is not the case with any C compiler, a substitue |
| 298 | + * realloc function has to be used. |
| 299 | + */ |
| 300 | + |
| 301 | +generic * |
| 302 | +gp_realloc(p, size, message) |
| 303 | + generic *p; /* old mem block */ |
| 304 | + unsigned long size; /* # of bytes */ |
| 305 | + char *message; /* description of what is being allocated */ |
| 306 | +{ |
| 307 | + char *res; /* the new allocation */ |
| 308 | + char errbuf[100]; /* error message string */ |
| 309 | + |
| 310 | + /* realloc(NULL,x) is meant to do malloc(x), but doesn't always */ |
| 311 | + if (!p) |
| 312 | + return gp_alloc(size,message); |
| 313 | + |
| 314 | +#ifndef NO_GIH |
| 315 | + res = GP_FARREALLOC(p,size); |
| 316 | + if (res == (char *)NULL) { |
| 317 | + FreeHelp(); /* out of memory, try to make some room */ |
| 318 | +#endif /* NO_GIH */ |
| 319 | + res = GP_FARREALLOC(p,size); /* try again */ |
| 320 | + if (res == (char *)NULL) { |
| 321 | + /* really out of memory */ |
| 322 | + if (message != NULL) { |
| 323 | + (void) sprintf(errbuf, "out of memory for %s", message); |
| 324 | + int_error(errbuf, NO_CARET); |
| 325 | + /* NOTREACHED */ |
| 326 | + } |
| 327 | + /* else we return NULL */ |
| 328 | + } |
| 329 | +#ifndef NO_GIH |
| 330 | + } |
| 331 | +#endif |
| 332 | + return(res); |
| 333 | +} |
| 334 | + |
| 335 | +#endif /* CHECK_HEAP_USE */ |
| 336 | + |
| 337 | +#ifdef FARALLOC |
| 338 | +void gpfree(p) |
| 339 | +generic *p; |
| 340 | +{ |
| 341 | +#ifdef _Windows |
| 342 | +HGLOBAL hGlobal = GlobalHandle(SELECTOROF(p)); |
| 343 | + GlobalUnlock(hGlobal); |
| 344 | + GlobalFree(hGlobal); |
| 345 | +#else |
| 346 | + farfree(p); |
| 347 | +#endif |
| 348 | +} |
| 349 | +#endif |
0 commit comments