#ifndef lint
static char *RCSid() { return RCSid("$Id: alloc.c,v 1.9 1999/11/08 19:24:27 lhecking 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 "alloc.h"
#ifndef NO_GIH
# include "help.h"
#endif
#include "util.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 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(NO_CARET, "Argh !");
}
}
/* 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(NO_CARET, "argh - pointer misuse !");
}
}
generic *
gp_alloc(size, usage)
size_t size;
const char *usage;
{
struct frame_struct *p;
size_t 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(NO_CARET, "Out of memory");
bytes_allocated += size;
mark(p, size, usage);
return (char *) (p + 1);
}
generic *
gp_realloc(old, size, usage)
generic *old;
size_t size;
const 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;
size_t total = size + RESERVED_SIZE + 1;
p = realloc(p, total);
if (!p)
int_error(NO_CARET, "Out of memory");
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 ? ((struct frame_struct *) p - 1)->use :
"(NULL)")));
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)
size_t size; /* # of bytes */
const char *message; /* description of what is being allocated */
{
char *p; /* the new allocation */
#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) {
int_error(NO_CARET, "out of memory for %s", message);
/* 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 */
size_t size; /* # of bytes */
const char *message; /* description of what is being allocated */
{
char *res; /* the new allocation */
/* 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) {
int_error(NO_CARET, "out of memory for %s", message);
/* 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