#ifndef lint static char *RCSid = "$Id: alloc.c,v 1.1.1.2.2.1 2001/06/25 16:06:34 broeker Exp $"; #endif /* GNUPLOT - alloc.c */ /*[ * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley * * Permission to use, copy, and distribute this software and its * documentation for any purpose with or without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. * * Permission to modify the software is granted, but not the right to * distribute the complete modified source code. Modifications are to * be distributed as patches to the released version. Permission to * distribute binaries produced by compiling modified sources is granted, * provided you * 1. distribute the corresponding source modifications from the * released version in the form of a patch file along with the binaries, * 2. add special version identification to distinguish your version * in addition to the base release version number, * 3. provide your name and address as the primary contact for the * support of your modified version, and * 4. retain our contact information in regard to use of the base * software. * Permission to distribute the released version of the source code along * with corresponding source modifications in the form of a patch file is * granted with same provisions 2 through 4 for binary distributions. * * This software is provided "as is" without express or implied warranty * to the extent permitted by applicable law. ]*/ /* * AUTHORS * * Alexander Lehmann (collected functions from misc.c and binary.c) * */ #include "plot.h" /* includes "alloc.h" */ #if defined(MSDOS) && defined(__TURBOC__) && !defined(DOSX286) #include /* for farmalloc, farrealloc */ #endif #if defined(_Windows) && !defined(WIN32) #include #include #define farmalloc(s) GlobalAllocPtr(GHND,s) #define farrealloc(p,s) GlobalReAllocPtr(p,s,GHND) #endif #ifndef NO_GIH #include "help.h" #endif #ifndef GP_FARMALLOC # ifdef FARALLOC # define GP_FARMALLOC(size) farmalloc ((size)) # define GP_FARREALLOC(p,size) farrealloc ((p), (size)) # else # define GP_FARMALLOC(size) malloc ((size_t)(size)) # define GP_FARREALLOC(p,size) realloc ((p), (size_t)(size)) # endif #endif /* uncomment if you want to trace all allocs */ #define TRACE_ALLOC(x) /*printf x*/ #ifdef CHECK_HEAP_USE /* This is in no way supported, and in particular it breaks the * online help. But it is useful to leave it in in case any * heap-corruption bugs turn up. Wont work with FARALLOC */ struct frame_struct { char *use; int requested_size; int pad; /* preserve 8-byte alignment */ int checksum; }; struct leak_struct { char *file; int line; int allocated; }; static struct leak_struct leak_stack[40]; /* up to 40 nested leak checks */ static struct leak_struct *leak_frame = leak_stack; static long bytes_allocated = 0; #define RESERVED_SIZE sizeof(struct frame_struct) #define CHECKSUM_INT 0xcaac5e1f #define CHECKSUM_FREE 0xf3eed222 #define CHECKSUM_CHAR 0xc5 static void mark(p, size, usage) struct frame_struct *p; unsigned long size; char *usage; { p->use = usage; p->requested_size = size; p->checksum = (CHECKSUM_INT ^ (int)(p->use) ^ size); ((unsigned char *)(p+1))[size] = CHECKSUM_CHAR; } #define mark_free(p) ( ((struct frame_struct *)p)[-1].checksum = CHECKSUM_FREE) static void validate(x) void *x; { struct frame_struct *p = (struct frame_struct *)x - 1; if (p->checksum != (CHECKSUM_INT ^ (int)(p->use) ^ p->requested_size)) { fprintf(stderr, "Heap corruption at start of block for %s\n", p->use); if (p->checksum == CHECKSUM_FREE) fprintf(stderr, "Looks like it has already been freed ?\n"); abort(); } if ( ((unsigned char *)(p+1))[p->requested_size] != CHECKSUM_CHAR) { fprintf(stderr, "Heap corruption at end of block for %-60s\n", p->use); int_error("Argh !", NO_CARET); } } /* used to confirm that a pointer is inside an allocated region via * macro CHECK_POINTER. Nowhere near as good as using a bounds-checking * compiler (such as gcc-with-bounds-checking), but when we do * come across problems, we can add these guards to the code until * we find the problem, and then leave the guards in (as CHECK_POINTER * macros which expand to nothing, until we need to re-enable them) */ void check_pointer_in_block(void *block, void *p, int size, char *file, int line) { struct frame_struct *f = (struct frame_struct *)block - 1; validate(block); if (p < block || p >= (block + f->requested_size)) { fprintf(stderr, "argh - pointer %p outside block %p->%p for %s at %s:%d\n", p, block, (char *)block + f->requested_size, f->use, file, line); int_error("argh - pointer misuse !", NO_CARET); } } generic * gp_alloc(size, usage) unsigned long size; char *usage; { struct frame_struct *p; unsigned long total_size = size + RESERVED_SIZE + 1; TRACE_ALLOC(("gp_alloc %d for %s\n", (int) size, usage?usage:"")); p=malloc(total_size); if (!p) int_error("Out of memory", NO_CARET); bytes_allocated += size; mark(p,size,usage); return (char *)(p+1); } generic *gp_realloc(old, size, usage) generic *old; unsigned long size; char *usage; { if (!old) return gp_alloc(size, usage); validate(old); mark_free(old); /* if block gets moved, old block is marked free */ /* if not, we'll remark it later */ { struct frame_struct *p = (struct frame_struct *)old - 1; unsigned long total = size + RESERVED_SIZE + 1; p = realloc(p, total); if (!p) int_error("Out of memory", NO_CARET); TRACE_ALLOC(("gp_realloc %d for %s (was %d)\n", (int)size, usage?usage:"", p->requested_size)); bytes_allocated += size - p->requested_size; mark(p,size,usage); return (generic *)(p+1); } } #undef free void checked_free(p) void *p; { validate(p); mark_free(p); /* trap attempts to free twice */ TRACE_ALLOC(("free %d for %s\n", ((struct frame_struct *)p - 1)->requested_size, ((struct frame_struct *)p - 1)->use)); bytes_allocated -= ((struct frame_struct *)p - 1) -> requested_size; free( (struct frame_struct *) p - 1); } /* this leak checking stuff will be broken by first int_error or interrupt */ void start_leak_check(char *file, int line) { if (leak_frame >= leak_stack+40) { fprintf(stderr, "too many nested memory-leak checks - %s:%d\n", file, line); return; } leak_frame->file = file; leak_frame->line = line; leak_frame->allocated = bytes_allocated; ++leak_frame; } void end_leak_check(char *file, int line) { if (--leak_frame < leak_stack) { fprintf(stderr, "memory-leak stack underflow at %s:%d\n", file, line); return; } if (leak_frame->allocated != bytes_allocated) { fprintf(stderr, "net change of %+d heap bytes between %s:%d and %s:%d\n", (int)(bytes_allocated - leak_frame->allocated), leak_frame->file, leak_frame->line, file, line); } } #else /* CHECK_HEAP_USE */ /* gp_alloc: * allocate memory * This is a protected version of malloc. It causes an int_error * if there is not enough memory, but first it tries FreeHelp() * to make some room, and tries again. If message is NULL, we * allow NULL return. Otherwise, we handle the error, using the * message to create the int_error string. Note cp/sp_extend uses realloc, * so it depends on this using malloc(). */ generic * gp_alloc(size, message) unsigned long size; /* # of bytes */ char *message; /* description of what is being allocated */ { char *p; /* the new allocation */ char errbuf[100]; /* error message string */ #ifndef NO_GIH p = GP_FARMALLOC(size); if (p == (char *)NULL) { FreeHelp(); /* out of memory, try to make some room */ #endif /* NO_GIH */ p = GP_FARMALLOC(size); /* try again */ if (p == (char *)NULL) { /* really out of memory */ if (message != NULL) { (void) sprintf(errbuf, "out of memory for %s", message); int_error(errbuf, NO_CARET); /* NOTREACHED */ } /* else we return NULL */ } #ifndef NO_GIH } #endif return(p); } /* * note gp_realloc assumes that failed realloc calls leave the original mem block * allocated. If this is not the case with any C compiler, a substitue * realloc function has to be used. */ generic * gp_realloc(p, size, message) generic *p; /* old mem block */ unsigned long size; /* # of bytes */ char *message; /* description of what is being allocated */ { char *res; /* the new allocation */ char errbuf[100]; /* error message string */ /* realloc(NULL,x) is meant to do malloc(x), but doesn't always */ if (!p) return gp_alloc(size,message); #ifndef NO_GIH res = GP_FARREALLOC(p,size); if (res == (char *)NULL) { FreeHelp(); /* out of memory, try to make some room */ #endif /* NO_GIH */ res = GP_FARREALLOC(p,size); /* try again */ if (res == (char *)NULL) { /* really out of memory */ if (message != NULL) { (void) sprintf(errbuf, "out of memory for %s", message); int_error(errbuf, NO_CARET); /* NOTREACHED */ } /* else we return NULL */ } #ifndef NO_GIH } #endif return(res); } #endif /* CHECK_HEAP_USE */ #ifdef FARALLOC void gpfree(p) generic *p; { #ifdef _Windows HGLOBAL hGlobal = GlobalHandle(SELECTOROF(p)); GlobalUnlock(hGlobal); GlobalFree(hGlobal); #else farfree(p); #endif } #endif