397 lines
8.1 KiB
C++
397 lines
8.1 KiB
C++
/*
|
|
* gmem.c
|
|
*
|
|
* Memory routines with out-of-memory checking.
|
|
*
|
|
* Copyright 1996-2003 Glyph & Cog, LLC
|
|
*/
|
|
|
|
#include <aconf.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
// older compilers won't define SIZE_MAX in stdint.h without this
|
|
#ifndef __STDC_LIMIT_MACROS
|
|
# define __STDC_LIMIT_MACROS 1
|
|
#endif
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#if MULTITHREADED && defined(_WIN32)
|
|
# include <windows.h>
|
|
#endif
|
|
#include "gmem.h"
|
|
|
|
#ifdef DEBUG_MEM
|
|
|
|
typedef struct _GMemHdr {
|
|
unsigned int magic;
|
|
int index;
|
|
size_t size;
|
|
struct _GMemHdr *next, *prev;
|
|
} GMemHdr;
|
|
|
|
#define gMemHdrSize ((sizeof(GMemHdr) + 7) & ~7)
|
|
#define gMemTrlSize (sizeof(long))
|
|
|
|
#define gMemMagic 0xabcd9999
|
|
|
|
#if ULONG_MAX > 0xffffffffL
|
|
#define gMemDeadVal 0xdeadbeefdeadbeefUL
|
|
#else
|
|
#define gMemDeadVal 0xdeadbeefUL
|
|
#endif
|
|
|
|
/* round data size so trailer will be aligned */
|
|
#define gMemDataSize(size) \
|
|
(int)(((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize))
|
|
|
|
#define gMemDataSize64(size) \
|
|
(size_t)(((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize))
|
|
|
|
static GMemHdr *gMemHead = NULL;
|
|
static GMemHdr *gMemTail = NULL;
|
|
|
|
static int gMemIndex = 0;
|
|
static int gMemAlloc = 0;
|
|
static size_t gMemInUse = 0;
|
|
static size_t gMaxMemInUse = 0;
|
|
|
|
#if MULTITHREADED
|
|
# ifdef _WIN32
|
|
static CRITICAL_SECTION gMemMutex;
|
|
static INIT_ONCE gMemMutexInitStruct = INIT_ONCE_STATIC_INIT;
|
|
static BOOL CALLBACK gMemMutexInitFunc(PINIT_ONCE initOnce, PVOID param,
|
|
PVOID *context) {
|
|
InitializeCriticalSection(&gMemMutex);
|
|
return TRUE;
|
|
}
|
|
# define gMemInitMutex InitOnceExecuteOnce(&gMemMutexInitStruct, \
|
|
&gMemMutexInitFunc, NULL, NULL)
|
|
# define gMemLock EnterCriticalSection(&gMemMutex);
|
|
# define gMemUnlock LeaveCriticalSection(&gMemMutex);
|
|
# else
|
|
# include <pthread.h>
|
|
static pthread_mutex_t gMemMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
# define gMemInitMutex
|
|
# define gMemLock pthread_mutex_lock(&gMemMutex)
|
|
# define gMemUnlock pthread_mutex_unlock(&gMemMutex)
|
|
# endif
|
|
#else
|
|
# define gMemInitMutex
|
|
# define gMemLock
|
|
# define gMemUnlock
|
|
#endif
|
|
|
|
#endif /* DEBUG_MEM */
|
|
|
|
#ifdef DEBUG_MEM
|
|
void *gmalloc(int size, int ignore) GMEM_EXCEP {
|
|
int size1;
|
|
char *mem;
|
|
GMemHdr *hdr;
|
|
void *data;
|
|
unsigned long *trl, *p;
|
|
|
|
gMemInitMutex;
|
|
if (size < 0) {
|
|
gMemError("Invalid memory allocation size");
|
|
}
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
size1 = gMemDataSize(size);
|
|
if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {
|
|
gMemError("Out of memory");
|
|
}
|
|
hdr = (GMemHdr *)mem;
|
|
data = (void *)(mem + gMemHdrSize);
|
|
trl = (unsigned long *)(mem + gMemHdrSize + size1);
|
|
hdr->magic = gMemMagic;
|
|
hdr->size = size;
|
|
gMemLock;
|
|
if (ignore) {
|
|
hdr->index = -1;
|
|
} else {
|
|
hdr->index = gMemIndex++;
|
|
}
|
|
if (gMemTail) {
|
|
gMemTail->next = hdr;
|
|
hdr->prev = gMemTail;
|
|
gMemTail = hdr;
|
|
} else {
|
|
hdr->prev = NULL;
|
|
gMemHead = gMemTail = hdr;
|
|
}
|
|
hdr->next = NULL;
|
|
++gMemAlloc;
|
|
gMemInUse += size;
|
|
if (gMemInUse > gMaxMemInUse) {
|
|
gMaxMemInUse = gMemInUse;
|
|
}
|
|
gMemUnlock;
|
|
for (p = (unsigned long *)data; p <= trl; ++p) {
|
|
*p = gMemDeadVal;
|
|
}
|
|
return data;
|
|
}
|
|
#else
|
|
void *gmalloc(int size) GMEM_EXCEP {
|
|
void *p;
|
|
|
|
if (size < 0) {
|
|
gMemError("Invalid memory allocation size");
|
|
}
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
if (!(p = malloc(size))) {
|
|
gMemError("Out of memory");
|
|
}
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
void *grealloc(void *p, int size) GMEM_EXCEP {
|
|
#ifdef DEBUG_MEM
|
|
GMemHdr *hdr;
|
|
void *q;
|
|
int oldSize;
|
|
|
|
if (size < 0) {
|
|
gMemError("Invalid memory allocation size");
|
|
}
|
|
if (size == 0) {
|
|
if (p) {
|
|
gfree(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
if (p) {
|
|
hdr = (GMemHdr *)((char *)p - gMemHdrSize);
|
|
oldSize = (int)hdr->size;
|
|
q = gmalloc(size);
|
|
memcpy(q, p, size < oldSize ? size : oldSize);
|
|
gfree(p);
|
|
} else {
|
|
q = gmalloc(size);
|
|
}
|
|
return q;
|
|
#else
|
|
void *q;
|
|
|
|
if (size < 0) {
|
|
gMemError("Invalid memory allocation size");
|
|
}
|
|
if (size == 0) {
|
|
if (p) {
|
|
free(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
if (p) {
|
|
q = realloc(p, size);
|
|
} else {
|
|
q = malloc(size);
|
|
}
|
|
if (!q) {
|
|
gMemError("Out of memory");
|
|
}
|
|
return q;
|
|
#endif
|
|
}
|
|
|
|
void *gmallocn(int nObjs, int objSize) GMEM_EXCEP {
|
|
int n;
|
|
|
|
if (nObjs == 0) {
|
|
return NULL;
|
|
}
|
|
n = nObjs * objSize;
|
|
if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
|
|
gMemError("Bogus memory allocation size");
|
|
}
|
|
return gmalloc(n);
|
|
}
|
|
|
|
#ifdef DEBUG_MEM
|
|
void *gmalloc64(size_t size, int ignore) GMEM_EXCEP {
|
|
size_t size1;
|
|
char *mem;
|
|
GMemHdr *hdr;
|
|
void *data;
|
|
unsigned long *trl, *p;
|
|
|
|
gMemInitMutex;
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
size1 = gMemDataSize64(size);
|
|
if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {
|
|
gMemError("Out of memory");
|
|
}
|
|
hdr = (GMemHdr *)mem;
|
|
data = (void *)(mem + gMemHdrSize);
|
|
trl = (unsigned long *)(mem + gMemHdrSize + size1);
|
|
hdr->magic = gMemMagic;
|
|
hdr->size = size;
|
|
gMemLock;
|
|
if (ignore) {
|
|
hdr->index = -1;
|
|
} else {
|
|
hdr->index = gMemIndex++;
|
|
}
|
|
if (gMemTail) {
|
|
gMemTail->next = hdr;
|
|
hdr->prev = gMemTail;
|
|
gMemTail = hdr;
|
|
} else {
|
|
hdr->prev = NULL;
|
|
gMemHead = gMemTail = hdr;
|
|
}
|
|
hdr->next = NULL;
|
|
++gMemAlloc;
|
|
gMemInUse += size;
|
|
if (gMemInUse > gMaxMemInUse) {
|
|
gMaxMemInUse = gMemInUse;
|
|
}
|
|
gMemUnlock;
|
|
for (p = (unsigned long *)data; p <= trl; ++p) {
|
|
*p = gMemDeadVal;
|
|
}
|
|
return data;
|
|
}
|
|
#else
|
|
void *gmalloc64(size_t size) GMEM_EXCEP {
|
|
void *p;
|
|
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
if (!(p = malloc(size))) {
|
|
gMemError("Out of memory");
|
|
}
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
void *gmallocn64(int nObjs, size_t objSize) GMEM_EXCEP {
|
|
size_t n;
|
|
|
|
if (nObjs == 0) {
|
|
return NULL;
|
|
}
|
|
n = nObjs * objSize;
|
|
if (nObjs < 0 || (size_t)nObjs >= SIZE_MAX / objSize) {
|
|
gMemError("Bogus memory allocation size");
|
|
}
|
|
return gmalloc64(n);
|
|
}
|
|
|
|
void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP {
|
|
int n;
|
|
|
|
if (nObjs == 0) {
|
|
if (p) {
|
|
gfree(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
n = nObjs * objSize;
|
|
if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
|
|
gMemError("Bogus memory allocation size");
|
|
}
|
|
return grealloc(p, n);
|
|
}
|
|
|
|
void gfree(void *p) {
|
|
#ifdef DEBUG_MEM
|
|
size_t size;
|
|
GMemHdr *hdr;
|
|
unsigned long *trl, *clr;
|
|
|
|
if (p) {
|
|
hdr = (GMemHdr *)((char *)p - gMemHdrSize);
|
|
gMemLock;
|
|
if (hdr->magic == gMemMagic &&
|
|
((hdr->prev == NULL) == (hdr == gMemHead)) &&
|
|
((hdr->next == NULL) == (hdr == gMemTail))) {
|
|
if (hdr->prev) {
|
|
hdr->prev->next = hdr->next;
|
|
} else {
|
|
gMemHead = hdr->next;
|
|
}
|
|
if (hdr->next) {
|
|
hdr->next->prev = hdr->prev;
|
|
} else {
|
|
gMemTail = hdr->prev;
|
|
}
|
|
--gMemAlloc;
|
|
gMemInUse -= hdr->size;
|
|
gMemUnlock;
|
|
size = gMemDataSize64(hdr->size);
|
|
trl = (unsigned long *)((char *)hdr + gMemHdrSize + size);
|
|
if (*trl != gMemDeadVal) {
|
|
fprintf(stderr, "Overwrite past end of block %d at address %p\n",
|
|
hdr->index, p);
|
|
}
|
|
for (clr = (unsigned long *)hdr; clr <= trl; ++clr) {
|
|
*clr = gMemDeadVal;
|
|
}
|
|
free(hdr);
|
|
} else {
|
|
gMemUnlock;
|
|
fprintf(stderr, "Attempted to free bad address %p\n", p);
|
|
}
|
|
}
|
|
#else
|
|
if (p) {
|
|
free(p);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void gMemError(const char *msg) GMEM_EXCEP {
|
|
#if USE_EXCEPTIONS
|
|
throw GMemException();
|
|
#else
|
|
fprintf(stderr, "%s\n", msg);
|
|
exit(1);
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUG_MEM
|
|
void gMemReport(FILE *f) {
|
|
GMemHdr *p;
|
|
int left;
|
|
|
|
fprintf(f, "%d memory allocations in all\n", gMemIndex);
|
|
fprintf(f, "maximum memory in use: %zd bytes\n", gMaxMemInUse);
|
|
left = 0;
|
|
if (gMemAlloc > 0) {
|
|
for (p = gMemHead; p; p = p->next) {
|
|
if (p->index >= 0) {
|
|
if (!left) {
|
|
fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc);
|
|
fprintf(f, " index size\n");
|
|
fprintf(f, "-------- --------\n");
|
|
left = 1;
|
|
}
|
|
fprintf(f, "%8d %8zd\n", p->index, p->size);
|
|
}
|
|
}
|
|
}
|
|
if (!left) {
|
|
fprintf(f, "No memory blocks left allocated\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
char *copyString(const char *s) {
|
|
char *s1;
|
|
|
|
s1 = (char *)gmalloc((int)strlen(s) + 1);
|
|
strcpy(s1, s);
|
|
return s1;
|
|
}
|