/* * gmem.c * * Memory routines with out-of-memory checking. * * Copyright 1996-2003 Glyph & Cog, LLC */ #include #include #include #include // older compilers won't define SIZE_MAX in stdint.h without this #ifndef __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS 1 #endif #include #include #include #if MULTITHREADED && defined(_WIN32) # include #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 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; }