Descent3/mem/mem.cpp
2024-04-15 21:43:29 -06:00

1229 lines
27 KiB
C++

/*
* $Logfile: /DescentIII/Main/mem/mem.cpp $
* $Revision: 56 $
* $Date: 1/03/02 5:27p $
* $Author: Matt $
*
* Memory library
*
* $Log: /DescentIII/Main/mem/mem.cpp $
*
* 56 1/03/02 5:27p Matt
* Add a longlong cast to prevent math overflow.
*
* 55 4/19/00 5:33p Matt
* From Duane for 1.4
* Close renderer on malloc fail error exit
* Mac changes
*
* 54 10/21/99 2:25p Kevin
* Mac merge
*
* 53 8/10/99 5:12p Jeff
* added a debug function to dump the current state of the mem library to
* file
*
* 52 7/28/99 2:22p Kevin
* Macintosh Stuff
*
* 51 5/13/99 5:06p Ardussi
* changes for compiling on the Mac
*
* 50 5/02/99 12:24a Jason
* mem was incorrectly choosing lowmem mode on 64 meg machines
*
* 49 4/30/99 5:07p Kevin
* misc dedicated server, networking and low memory enhancements
*
* 48 4/16/99 10:42p Jeff
* moved global variables out of window scope code to all builds for linux
*
* 47 4/15/99 11:09p Jeff
* fixed mem_strdup for linux
*
* 46 4/15/99 9:22p Jeff
* Changes for Linux version
*
* 45 4/12/99 2:23p Kevin
* fixed missing symbol
*
* 44 4/12/99 1:13p Kevin
* Moved call to GetLastError and played with some debugging
*
* 43 3/17/99 11:19a 3dsmax
* Took out debugging
*
* 42 3/16/99 4:49p Kevin
* Fixed realloc
*
* 41 3/15/99 4:32p Jeff
* fixed code so mem library compiles correctly
*
* 40 3/15/99 11:24a Kevin
* Improved memory leak detection for mem_strdup and turned on debugging
*
* 39 3/14/99 2:31p Kevin
* Added mem_heapcheck() function for debugging
*
* 38 3/02/99 5:50p Kevin
* Ouch. Duplicate structures existed and were conflicting.
*
* 37 2/28/99 6:30p Kevin
* Added a call to Error() when unable to alloc memory
*
* 36 1/09/99 4:40p Jeff
* added some ifdefs and fixes to get files to compile under Linux
*
* 35 1/05/99 3:46p Matt
* For Kevin, added call to GetLastError()
*
* 34 11/05/98 2:00p Sean
*
* 33 11/05/98 11:23a Kevin
* fixed bug that caused it not to work under NT
*
* 32 10/30/98 11:23a Samir
*
* 31 10/30/98 11:10a Kevin
* doh! fixed ifdef
*
* 30 10/30/98 11:01a Kevin
* took out mem debug stuff for nt users (temp)
*
* 29 10/28/98 5:01p Kevin
* Improved debugging aids in mem lib
*
* 28 10/21/98 7:18p Matt
* Added quick-exit system to not free individual mem chunks on exit,
* since the whole heap gets freed at the end.
*
* 27 10/18/98 9:11p Matt
* Took the program name out of some error messages.
*
* 26 10/18/98 6:21p Matt
* Changed a Debug_ConsolePrintf() to mprintf() since the former doesn't
* exist in a Release build and the latter compiles out.
*
* 25 10/17/98 12:46p Kevin
* Beta 4 fixes
*
* 24 10/15/98 12:15p Kevin
* put in heap checking code
*
* 23 10/15/98 11:59a Kevin
* removed useage of timer_GetTime and FindArg
*
* 22 10/14/98 11:25p Jeff
* commented out call to HeapDestroy() to keep NT users from crashing on
* exit because it destroys the heap before Sound thread closes
*
* 21 10/14/98 7:20p Kevin
* More dsp changes...
*
* 20 10/14/98 4:39p Matt
* Changed OutrageMessageBox() calls to use either Error() or
* EditorMessageBox()
*
* 19 10/13/98 3:42p Kevin
* bug fixes
*
* 18 10/12/98 8:39p Kevin
* removed mprintf's and fixed some smallish bugs
*
* 17 10/12/98 11:32a Kevin
* doh! heap size was 160 megs
*
* 16 10/12/98 11:25a Kevin
* doh!
*
* 15 10/12/98 10:55a Kevin
* Don't use memory lib for editor currently
*
* 14 10/12/98 10:22a Samir
* made mem_strdup take a const char pointer.
*
* 13 10/09/98 7:40p Kevin
*
* 12 10/09/98 6:55p Kevin
* fized bug with zero byte mallocs and mem_size
*
* 11 10/09/98 3:32p Kevin
* New memory library
*
* 10 10/08/98 4:24p Kevin
* Changed code to comply with memory library usage. Always use mem_malloc
* , mem_free and mem_strdup
*
* 9 10/08/98 2:40p Kevin
*
* 8 10/08/98 12:00p Kevin
* Demo system work
*
* 7 9/22/98 3:55p Samir
* ifdef out stuff for non-debug version.
*
* 6 8/31/98 12:42p Samir
* added some debug code.
*
* 5 7/31/98 5:44p Samir
* improved memory debugging.
*
* $NoKeywords: $
*/
#ifndef LINUX
#include <new.h>
#endif
#if defined(MACOSX)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef WIN32
#include <search.h>
//Non-Linux Includes
#include <windows.h>
#include <crtdbg.h>
#else
#endif
#include "init.h"
#include "mem.h"
#include "pserror.h"
#include "pstypes.h"
//#include "args.h"
//#include "ddio.h"
//
//#define MEM_DEBUG
#ifdef MEM_USE_RTL
#pragma message("mem.cpp: Compiling For Run-Time Library usage")
#endif
#ifdef MEM_DEBUG
#pragma message("mem.cpp: Compiling with Debug Settings")
#endif
#ifdef MEM_LOGFILE
#pragma message("mem.cpp: Compiling with logfile support")
#endif
int Total_mem_used=0;
int Mem_high_water_mark = 0;
#define MEM_NO_MEMORY_PTR 0xdeadbeef
#define MEM_MAX_MALLOCS 199999
#define MEM_GAURDIAN_SIG 0x2bad
#define MEM_MALLOC_TO_SORT 10000
typedef struct mem_alloc_info
{
int len;
void * ptr;
unsigned short line;
char file[17];
}mem_alloc_info;
static void *Mem_failsafe_block = NULL;;
bool Mem_low_memory_mode = false;
bool Mem_superlow_memory_mode = false;
//If this is set, the mem library ignores mem_free() calls. All the memory is then freed at once oon exit.
bool Mem_quick_exit = 0;
#if defined (__LINUX__)
//Linux memory management
int LnxTotalMemUsed;
void mem_shutdown(void)
{
}
void mem_Init(void)
{
LnxTotalMemUsed = 0;
}
int mem_GetTotalMemoryUsed(void)
{
return LnxTotalMemUsed;
}
void *mem_malloc_sub(int size,const char *file,int line)
{
void *new_mem = malloc(size);
if(!new_mem){
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file));
Int3();
return NULL;
}
LnxTotalMemUsed += size;
return new_mem;
}
void mem_free_sub(void *memblock)
{
if(memblock){
#if defined(MACOSX)
LnxTotalMemUsed -= malloc_size(memblock);
#else
LnxTotalMemUsed -= malloc_usable_size(memblock);
#endif
free(memblock);
}
}
void mem_error_msg(const char *file,int line,int size)
{
mprintf((0,"Memory error (size=%d) line %d in %s\n",size,line,file));
Int3();
}
char *mem_strdup_sub(const char *string,char *file,int line)
{
char *ret = strdup(string);
if(!ret)
{
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",strlen(string)+1,line,file));
Int3();
return NULL;
}
return ret;
}
void *mem_realloc_sub(void *mem,int size)
{
return realloc(mem,size);
}
int mem_size_sub(void *memblock)
{
#if defined(MACOSX)
return malloc_size(memblock);
#else
return malloc_usable_size(memblock);
#endif
}
bool mem_dumpmallocstofile(char *filename)
{
return false;
}
#pragma mark -
#elif defined (MACINTOSH)
#include "ddio.h"
#include "ddio_mac.h"
#include "descent.h"
#include "gamesave.h"
#include "renderer.h"
//Macintosh memory management
int MacTotalMemUsed;
int MacDynamicStartMem = 0;
#define FAILSAFE_SIZE 2048
#ifdef DAJ_DEBUG
//#define MEM_LOGFILE
#endif
#define USE_MALLOC
//int Mem_next_slot = 0;
//mem_alloc_info mem_info[MEM_MAX_MALLOCS];
FILE * mem_out = NULL;
void mem_shutdown(void)
{
if (Mem_failsafe_block) {
mem_free(Mem_failsafe_block);
Mem_failsafe_block = NULL;
}
MacTotalMemUsed = 0;
// Mem_next_slot = 0;
}
void mem_Init(void)
{
if(Mem_failsafe_block)
return;
Mem_failsafe_block = malloc(FAILSAFE_SIZE);
if (!Mem_failsafe_block) {
Error("No available heap memory.");
}
MacDynamicStartMem = (int)Mem_failsafe_block;
MacTotalMemUsed = 0;
#ifdef MEM_LOGFILE
{
mem_out = fopen("memory.out","wt");
ASSERT(mem_out);
}
#endif
atexit(mem_shutdown);
return;
}
int mem_GetTotalMemoryUsed(void)
{
return MacTotalMemUsed - MacDynamicStartMem;
}
void *mem_malloc_sub(int size,const char *file,int line)
{
void *new_mem;
if(size <= 0)
{
mprintf((0, "Warning: Zero byte malloc in %s line %d!\n", file, line));
// Int3();
return (void *)MEM_NO_MEMORY_PTR;
}
#ifdef USE_MALLOC
new_mem = malloc(size);
#else
new_mem = NewPtr(size);
#endif
if(!new_mem){
if(Mem_failsafe_block == NULL) //Been here already!!
return NULL;
mem_free (Mem_failsafe_block);
Mem_failsafe_block = NULL;
#ifdef _DEBUG
mprintf((0,"Out of memory allocating %d bytes: line %d in %s\n",size,line,file));
Debugger();
#else
rend_Close();
::ShowCursor();
if (GetFunctionMode() == GAME_MODE)
{
char pathname[PSPATHNAME_LEN];
ddio_MakePath(pathname, Base_directory, "savegame", "crash", NULL);
SaveGameState(pathname, "crash save");
::Alert(129, NULL);
// ::Alert(128, NULL);
}
else
{
::Alert(129, NULL);
}
#endif
// ShutdownD3();
exit(0);
// Int3();
// ExitToShell();
// return NULL;
}
#ifdef MEM_LOGFILE
int mem_addr = (int)new_mem;
if(mem_addr > MacTotalMemUsed) {
MacTotalMemUsed = mem_addr;
fprintf(mem_out, "0x%0X %d %d %s:%d\n", mem_addr,
MacTotalMemUsed - MacDynamicStartMem, size, file, line);
}
#endif
return new_mem;
}
void mem_free_sub(void *memblock)
{
if((void *)MEM_NO_MEMORY_PTR == memblock)
{
return;
}
if(memblock){
#ifdef USE_MALLOC
free(memblock);
#else
DisposePtr((char *)memblock);
#endif
}
#if 0
for(int i=0; i<Mem_next_slot; i++) {
if(memblock == mem_info[i].ptr) {
fprintf(mem_out, "free %d %s:%d\n", mem_info[i].len, mem_info[i].file, mem_info[i].line);
}
}
#endif
}
void mem_error_msg(const char *file,int line,int size)
{
mprintf((0,"Memory error (size=%d) line %d in %s\n",size,line,file));
Int3();
}
char *mem_strdup_sub(const char *string,char *file,int line)
{
int len = strlen(string)+1;
char * ret = (char *)mem_malloc(len);
strcpy(ret,string);
return ret;
}
void *mem_realloc_sub(void *mem,int size)
{
void *new_mem;
if(size <= 0)
{
// Int3();
return (void *)MEM_NO_MEMORY_PTR;
}
#ifdef USE_MALLOC
new_mem = realloc(mem,size);
#else
SetPtrSize((char*)mem, size);
new_mem = mem;
#endif
if(!new_mem){
if(Mem_failsafe_block == NULL) //Been here already!!
return NULL;
mem_free (Mem_failsafe_block);
Mem_failsafe_block = NULL;
#ifdef _DEBUG
mprintf((0,"Out of memory allocating %d bytes\n",size));
Debugger();
#else
::ShowCursor();
if (GetFunctionMode() == GAME_MODE)
{
char pathname[PSPATHNAME_LEN];
ddio_MakePath(pathname, Base_directory, "savegame", "crash", NULL);
SaveGameState(pathname, "crash save");
::Alert(128, NULL);
}
else
{
::Alert(129, NULL);
}
#endif
// ShutdownD3();
exit(0);
// Int3();
// ExitToShell();
// return NULL;
}
return new_mem;
}
int mem_size_sub(void *memblock)
{
return 0;
}
bool mem_dumpmallocstofile(char *filename)
{
return false;
}
#pragma mark -
#else // defined (WIN32)
//Windows memory management
// Uncomment this to detect memory leaks and memory overwrites. Slows down mallocs and frees a little.
//#define MEM_DEBUG 1
//Uncomment this if you want everything written to "memory.out"
//#define MEM_LOGFILE 1
#ifdef MEM_USE_RTL
#undef MEM_DEBUG
#endif
class MemClass
{
public:
~MemClass();
};
class MemClass Mymem;
/* modify these lines to establish data type */
typedef mem_alloc_info * T; /* type of item to be stored */
typedef int hashTableIndex; /* index into hash table */
#define compEQ(a,b) ((a)->ptr == (b)->ptr)
typedef struct Node_ {
struct Node_ *next; /* next node */
T data; /* data stored in node */
} Node;
Node *findNode (T data);
void deleteNode(T data);
Node *insertNode(T data);
hashTableIndex hash(T data);
Node **hashTable;
int hashTableSize = MEM_MAX_MALLOCS;
#ifdef MEM_DEBUG
mem_alloc_info mem_info[MEM_MAX_MALLOCS];
int Mem_next_slot = 0;
int Mem_highest_used_slot = 0;
FILE * mem_out = NULL;
int Mem_mallocs_since_last_sort = 0;
/*
#undef new
void *operator new(unsigned int size, char *file, int line)
{
return mem_malloc_sub(size, file, line);
}
void *operator new [](unsigned int size, char *file, int line)
{
return mem_malloc_sub(size, file, line);
}
void *operator new(unsigned int size)
{
return mem_malloc_sub(size,"unknown",0);
}
void operator delete(void *ptr)
{
mem_free_sub(ptr);
}
*/
#endif
int handle_program_memory_depletion( size_t size );
HANDLE Heap;
void mem_Init()
{
#if defined(WIN32) || defined(__LINUX__)
// allocate failsafe block for memory used by any functions after we run out of memory.
// (printf for instance needs heap memory, as well as our error library.)
MEMORYSTATUS ms;
#ifdef MEM_DEBUG
hashTable = (Node **)malloc(hashTableSize * sizeof(Node *));
for(int a=0;a<hashTableSize;a++)
{
hashTable[a] = NULL;
}
#ifdef MEM_LOGFILE
{
mem_out = fopen("memory.out","wt");
ASSERT(mem_out);
}
#endif
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
mem_info[i].ptr = (void *)MEM_NO_MEMORY_PTR;
}
#endif
GlobalMemoryStatus(&ms);
Heap = HeapCreate(HEAP_NO_SERIALIZE,16000000,0);//GetProcessHeap();
if(!Heap)
{
mprintf((0,"Unable to create memory heap! error: %d\n",GetLastError()));
Error("Unable to create memory heap; your system may not have enough memory to run.");
}
ASSERT(Heap);
HeapCompact(Heap,0);
mprintf((0,"System Memory Status:\n"));
mprintf((0,"Percent of memory in use: %d\n",ms.dwMemoryLoad));
mprintf((0,"Bytes of physical memory : %d\n",ms.dwTotalPhys));
mprintf((0,"Free physical memory bytes : %d\n",ms.dwAvailPhys));
mprintf((0,"Available virtual memory : %d\n",ms.dwAvailPageFile));
//See if there is enough memory to run
if(((longlong) ms.dwAvailPageFile+ms.dwAvailPhys)<(50*1024*1024))
{
Error("Your system doesn't have enough available memory to continue.\r\n\r\nMemory Statistics:\r\nTotal Physical: %d\r\nAvaliable Physical: %d\r\nAvailable Virtual: %d\r\n\r\nYou may be able to continue by rebooting, or freeing up some disk space.",
ms.dwTotalPhys,ms.dwAvailPhys,ms.dwAvailPageFile);
return;
}
else if((ms.dwTotalPhys)<(62*1024*1024))
{
mprintf((0,"Using low memory mode!\n"));
Mem_low_memory_mode = true;
return;
}
else if((ms.dwTotalPhys)<(46*1024*1024))
{
mprintf((0,"Using super low memory mode!\n"));
Mem_low_memory_mode = true;
Mem_superlow_memory_mode = true;
return;
}
#else
#ifdef MEM_DEBUG
hashTable = (Node **)malloc(hashTableSize * sizeof(Node *));
for(int a=0;a<hashTableSize;a++)
{
hashTable[a] = NULL;
}
#ifdef MEM_LOGFILE
{
mem_out = fopen("memory.out","wt");
ASSERT(mem_out);
}
#endif
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
mem_info[i].ptr = (void *)MEM_NO_MEMORY_PTR;
}
#endif
Heap = NULL;
Mem_failsafe_block = mem_malloc(128);
if (!Mem_failsafe_block) {
Error("No available heap memory.");
}
return;
#endif
#if defined(WIN32) || defined(__LINUX__)
Mem_failsafe_block = mem_malloc(128);
if (!Mem_failsafe_block) {
Error("No available heap memory.");
}
_set_new_handler(handle_program_memory_depletion);
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
#ifdef _DEBUG
flags |= _CRTDBG_ALLOC_MEM_DF;
#endif
_CrtSetDbgFlag(flags);
//atexit(mem_Free);
#endif
}
// Returns the number of dynamically allocated bytes
int mem_GetTotalMemoryUsed ()
{
return Total_mem_used;
}
bool bail_now = false;
// Allocates a block of memory and returns a pointer to it
void *mem_malloc_sub (int size, const char *file, int line)
{
//float mstart = timer_GetTime();
void *retp;
#ifdef MEM_USE_RTL
retp = malloc(size);
return retp;
#else
#if defined(WIN32)
if(!Heap)
{
return 0;
}
#endif
if(size < 0)
{
mprintf((0,"Some bozo is trying to allocate a negative length of memory!\n"));
mprintf((0,"Offending file: %s line: %d\n",file, line));
Int3();
return NULL;
}
else if(size == 0)
{
mprintf((0, "Warning: Zero byte malloc in %s line %d!\n", file, line));
Int3();
return (void *)MEM_NO_MEMORY_PTR;
}
mem_alloc_info * mi = NULL;
mem_alloc_info no_track_mi;
bool track_node = true;
#ifdef MEM_DEBUG
if(!bail_now)
{
if(mem_info[Mem_next_slot].ptr==(void *)MEM_NO_MEMORY_PTR)
{
mi = &mem_info[Mem_next_slot];
Mem_next_slot++;
if(Mem_next_slot>Mem_highest_used_slot)
Mem_highest_used_slot = Mem_next_slot;
}
else
{
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
if(mem_info[i].ptr==(void *)MEM_NO_MEMORY_PTR)
{
mi = &mem_info[i];
Mem_next_slot = i+1;
if(i>Mem_highest_used_slot)
Mem_highest_used_slot = i;
break;
}
}
}
}
if(bail_now||(mi==NULL))
#else
#endif
{
mi = &no_track_mi;
if(bail_now==false)
{
track_node = false;
#ifdef MEM_DEBUG
mprintf((0,"Out of memory tracking slots!!!!\n"));
#endif
}
bail_now = true;
}
#ifndef MEM_DEBUG
mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size);
#else
mi->ptr = HeapAlloc(Heap,HEAP_NO_SERIALIZE,size+2);
mi->len = size;
unsigned short mem_sig = MEM_GAURDIAN_SIG;
memcpy(((char *)mi->ptr)+size,(void *)&mem_sig,2);
int flen = strlen(file);
int lofs = 0;
//Strip down the filename to be < 16 bytes
if(flen > 16)
lofs = flen-16;
strcpy(mi->file,file+lofs);
mi->line = line;
#ifdef MEM_LOGFILE
fprintf(mem_out,"%s,%d,%d,%d\n",mi->file,mi->line,size,HeapSize(Heap,0,mi->ptr));
#endif
#endif
int errors;
if(mi->ptr==NULL)
{
#ifndef MACINTOSH
errors = GetLastError();
#endif
mprintf((0,"Unable to alloc memory in mem_malloc_sub()!\n"));
Int3();
ASSERT(mi->ptr);
Error("Out of memory, unable to continue.");
mem_error_msg(file, line, size);
return 0;
}
retp = mi->ptr;
Total_mem_used+=size;
if(Mem_high_water_mark<Total_mem_used)
{
Mem_high_water_mark = Total_mem_used;
}
#ifdef MEM_DEBUG
if(track_node)
insertNode(mi);
#endif
return retp;
#endif
}
// Frees a previously allocated block of memory
void mem_free_sub (void *memblock)
{
#ifndef MEM_DEBUG
if (Mem_quick_exit)
return;
#endif
#ifdef MEM_USE_RTL
free(memblock);
return;
#else
#if defined(WIN32)
if(!Heap)
{
return;
}
#endif
if (memblock==0)
return;
if((void *)MEM_NO_MEMORY_PTR == memblock)
{
return;
}
#ifdef MEM_DEBUG
mem_alloc_info findmem;
mem_alloc_info *freemem;
findmem.ptr = memblock;
Node * mynode = findNode(&findmem);
if(mynode)
{
freemem = mynode->data;
freemem->ptr=(void *)MEM_NO_MEMORY_PTR;
unsigned short mem_sig = MEM_GAURDIAN_SIG;
if(memcmp((char *)memblock+freemem->len,&mem_sig,2)!=0)
{
// Corrupted memory found when we went to free it.
mprintf((0,"Memory block found to be damaged when it was freed!\n"));
Int3();
}
Total_mem_used-=freemem->len;
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
deleteNode(mynode->data);
return;
}
else
{
mprintf((0,"Warning, hash lookup of memory block failed!\n"));
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
return;
}
#endif
HeapFree(Heap,HEAP_NO_SERIALIZE,memblock);
#endif
}
int handle_program_memory_depletion( size_t size )
{
// Release character buffer memory.
mem_free (Mem_failsafe_block);
Mem_failsafe_block = NULL;
Error("Unable to allocate %d bytes of memory.", size);
// Tell new to stop allocation attempts.
return 0;
}
// prints out a memory error message
void mem_error_msg(const char *file, int line, int size)
{
Error ("Attempt to allocate %d bytes of memory in %s line %d failed.",size, file, line);
}
//int strdup_malloc_line = 0;
char * mem_strdup_sub(const char *src,char *file,int line)
{
#if defined(WIN32)
if(!Heap)
{
return 0;
}
#endif
int len = strlen(src)+1;
//strdup_malloc_line = __LINE__+1;
//Leave this line here!
char * dest = (char *)mem_malloc_sub(len,file,line);
strcpy(dest,src);
return dest;
}
void * mem_realloc_sub(void * memblock,int size)
{
#ifdef MEM_USE_RTL
void *retp = realloc(memblock,size);
return retp;
#else
#if defined(WIN32)
if(!Heap)
{
return 0;
}
#endif
if((void *)MEM_NO_MEMORY_PTR == memblock)
{
return mem_malloc(size);
}
#ifdef MEM_DEBUG
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
if(mem_info[i].ptr==(void *)memblock)
{
mem_info[i].ptr=(void *)MEM_NO_MEMORY_PTR;
Total_mem_used-=mem_info[i].len;
deleteNode(&mem_info[i]);
mem_info[i].ptr = HeapReAlloc(Heap,0,memblock,size+2);
unsigned short mem_sig = MEM_GAURDIAN_SIG;
memcpy(((char *)mem_info[i].ptr)+size,(void *)&mem_sig,2);
mem_info[i].len = size;
Total_mem_used += size;
insertNode(&mem_info[i]);
return mem_info[i].ptr;
}
}
#endif
#ifdef MACINTOSH
HeapFree(Heap, HEAP_NO_SERIALIZE, memblock);
void *retp = HeapAlloc(Heap, HEAP_NO_SERIALIZE, size);
#else
void *retp = HeapReAlloc(Heap,0,memblock,size);
#endif
return retp;
#endif
}
int mem_size_sub(void *memblock)
{
#if defined(WIN32)
if(!Heap)
{
return 0;
}
#endif
if((void *)MEM_NO_MEMORY_PTR == memblock)
{
return 0;
}
return HeapSize(Heap,0,memblock);
}
void mem_shutdown();
MemClass::~MemClass()
{
mem_shutdown();
}
// memory routines
void mem_shutdown()
{
// free failsafe memory block.
mprintf((0,"Shutting down memory system.\n"));
if (Mem_failsafe_block) {
mem_free(Mem_failsafe_block);
Mem_failsafe_block = NULL;
}
#ifdef MEM_DEBUG
free(hashTable);
if (Total_mem_used)
mprintf((0, "%d bytes leaked in mem_malloc heap!\n", Total_mem_used));
#ifdef MEM_LOGFILE
fclose(mem_out);
#endif
mprintf((0,"Looking for memory leaks.\n"));
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
if(mem_info[i].ptr!=(void *)MEM_NO_MEMORY_PTR)
{
mprintf((0,"Memory leaked from %s line %d length %d.\n",mem_info[i].file,mem_info[i].line,mem_info[i].len));
/*
//mprintf((0,"%d\n",strdup_malloc_line));
if( (strcmp(mem_info[i].file,"main\\mem\\mem.cpp")==0) && (mem_info[i].line==(strdup_malloc_line+1)) )
{
mprintf((0,"Memory leak from mem_strdup() (%s)\n",mem_info[i].ptr));
}
*/
}
}
mprintf((0,"Done looking for memory leaks.\n"));
mprintf((0,"Memory library high water mark: %d\n",Mem_high_water_mark));
Mem_next_slot = 0;
#endif
if(Heap)
{
HeapDestroy(Heap);
Heap = NULL;
}
}
#ifdef MEM_DEBUG
//accessory stuff for mem_dumpallocstofile()
typedef struct allocdumpt
{
mem_alloc_info *data;
}allocdumpt;
static int *sorted_allocs = NULL;
int mem_allocdump_cmp(allocdumpt *elem1,allocdumpt *elem2)
{
int res;
res = strcmp(elem1->data->file,elem2->data->file);
if(res!=0)
return (res<0)?-1:1;
if(elem1->data->line<elem2->data->line)
return -1;
else if(elem1->data->line>elem2->data->line)
return 1;
if(elem1->data->len<elem2->data->len)
return -1;
else if(elem1->data->len>elem2->data->len)
return 1;
return 0;
}
#endif
bool mem_dumpmallocstofile(char *filename)
{
#ifdef MEM_DEBUG
FILE *file;
file = fopen(filename,"wt");
if(!file)
return false;
int h_table_idx,num_allocs=0;
mem_alloc_info *alloc_info;
char buffer[384];
for(h_table_idx=0;h_table_idx<MEM_MAX_MALLOCS;h_table_idx++)
{
if(mem_info[h_table_idx].ptr!=(void *)MEM_NO_MEMORY_PTR)
{
num_allocs++;
}
}
allocdumpt *allocs;
int curr_idx,i;
allocs = (allocdumpt *)malloc(sizeof(allocdumpt)*num_allocs);
sorted_allocs = (int *)malloc(sizeof(int)*num_allocs);
curr_idx = 0;
for(h_table_idx=0;h_table_idx<MEM_MAX_MALLOCS;h_table_idx++)
{
if(mem_info[h_table_idx].ptr!=(void *)MEM_NO_MEMORY_PTR)
{
allocs[curr_idx++].data = &mem_info[h_table_idx];
}
}
// sort the list of allocs for easier reading
for(i=0;i<num_allocs;i++)
sorted_allocs[i] = i;
qsort(allocs,num_allocs,sizeof(allocdumpt),(int (*)(const void *,const void *))mem_allocdump_cmp);
// dump it out
int last_unique;
last_unique = -1;
for(i=0;i<num_allocs;i++)
{
curr_idx = sorted_allocs[i];
alloc_info = (mem_alloc_info *)allocs[curr_idx].data;
if(last_unique!=-1)
{
//see if we are just repeating
mem_alloc_info *last_alloc;
last_alloc = (mem_alloc_info *)allocs[sorted_allocs[last_unique]].data;
if( (!strcmp(last_alloc->file,alloc_info->file)) &&
(last_alloc->line==alloc_info->line) )
{
//its the same!
continue;
}
}
last_unique = i;
if(i<num_allocs-1)
{
//see if we have multiple of the same line
mem_alloc_info *next_alloc;
next_alloc = (mem_alloc_info *)allocs[sorted_allocs[i+1]].data;
if( (!strcmp(next_alloc->file,alloc_info->file)) &&
(next_alloc->line==alloc_info->line) )
{
// we have multiple of the same line!
// see how many we have of this line
int repeat_count = 1;
int total_size = alloc_info->len;
i++;
while(i<num_allocs)
{
next_alloc = (mem_alloc_info *)allocs[sorted_allocs[i]].data;
if( (!strcmp(next_alloc->file,alloc_info->file)) &&
(next_alloc->line==alloc_info->line) )
{
repeat_count++;
total_size += next_alloc->len;
}else
{
break;
}
i++;
}
sprintf(buffer,"\n--Repeated %d Times--\n",repeat_count);
fputs(buffer,file);
sprintf(buffer,"*[%d bytes total]\tFile %s Line %d\n",total_size,alloc_info->file,alloc_info->line);
fputs(buffer,file);
continue;
}
}
sprintf(buffer,"[%d bytes]\tFile %s Line %d\n",alloc_info->len,alloc_info->file,alloc_info->line);
fputs(buffer,file);
}
free(sorted_allocs);
free(allocs);
fclose(file);
return true;
#else
return false;
#endif
}
void mem_heapcheck(void)
{
#ifdef MEM_DEBUG
for(int i=0;i<MEM_MAX_MALLOCS;i++)
{
mem_alloc_info *freemem;
if(mem_info[i].ptr==(void *)MEM_NO_MEMORY_PTR)
continue;
freemem = &mem_info[i];
unsigned short mem_sig = MEM_GAURDIAN_SIG;
if(memcmp((char *)freemem->ptr+freemem->len,&mem_sig,2)!=0)
{
mprintf((0,"Memory block found to be damaged in mem_heapcheck()!\n"));
mprintf((0,"Originally allocated from file %s, line %d\n",freemem->file,freemem->line));
Int3();
}
}
#endif
}
hashTableIndex hash(T data) {
/***********************************
* hash function applied to data *
***********************************/
unsigned int hval = (unsigned int)data->ptr;
return (hval % MEM_MAX_MALLOCS);
}
Node *insertNode(T data)
{
Node *p, *p0;
hashTableIndex bucket;
/************************************************
* allocate node for data and insert in table *
************************************************/
/* insert node at beginning of list */
bucket = hash(data);
if ((p = (Node *)malloc(sizeof(Node))) == 0) {
fprintf (stderr, "out of memory (insertNode)\n");
exit(1);
}
p0 = hashTable[bucket];
hashTable[bucket] = p;
p->next = p0;
p->data = data;
return p;
}
void deleteNode(T data)
{
Node *p0, *p;
hashTableIndex bucket;
/********************************************
* delete node containing data from table *
********************************************/
/* find node */
p0 = 0;
bucket = hash(data);
p = hashTable[bucket];
while (p && !compEQ(p->data, data)) {
p0 = p;
p = p->next;
}
if (!p) return;
/* p designates node to delete, remove it from list */
if (p0)
/* not first node, p0 points to previous node */
p0->next = p->next;
else
/* first node on chain */
hashTable[bucket] = p->next;
free (p);
}
Node *findNode (T data)
{
Node *p;
/*******************************
* find node containing data *
*******************************/
p = hashTable[hash(data)];
while (p && !compEQ(p->data, data))
p = p->next;
return p;
}
#endif