/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
--- HISTORICAL COMMENTS FOLLOW ---
* $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: $
*/
#if !defined(POSIX)
#include
#endif
#if defined(MACOSX)
#include
#else
#include
#endif
#include
#include
#include
#ifdef WIN32
// Non-Linux Includes
#include
#include
#else
#endif
#include
#include "log.h"
#include "mem.h"
#include "pserror.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
struct mem_alloc_info {
int len;
void *ptr;
uint16_t line;
char file[17];
};
static void *Mem_failsafe_block = nullptr;
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 on exit.
bool Mem_quick_exit = false;
#if defined(POSIX)
// Linux memory management
int LnxTotalMemUsed;
void mem_shutdown() {}
void mem_Init() { LnxTotalMemUsed = 0; }
int mem_GetTotalMemoryUsed() { return LnxTotalMemUsed; }
void *mem_malloc_sub(int size, const char *file, int line) {
void *new_mem = malloc(size);
if (!new_mem) {
LOG_ERROR.printf("Out of memory allocating %d bytes: line %d in %s", size, line, file);
Int3();
return nullptr;
}
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) {
LOG_ERROR.printf("Memory error (size=%d) line %d in %s", size, line, file);
Int3();
}
char *mem_strdup_sub(const char *string, const char *file, int line) {
char *ret = strdup(string);
if (!ret) {
LOG_ERROR.printf("Out of memory allocating %d bytes: line %d in %s", strlen(string) + 1, line, file);
Int3();
return nullptr;
}
return ret;
}
void *mem_realloc_sub(void *mem, int size) { return realloc(mem, size); }
int mem_size_sub(void *memblock) {
#ifdef MACOSX
return malloc_size(memblock);
#else
return malloc_usable_size(memblock);
#endif
}
bool mem_dumpmallocstofile(char *filename) { return false; }
#pragma mark -
#else // defined(POSIX)
// 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)
struct Node {
struct Node *next; /* next node */
T data; /* data stored in 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(uint32_t size, char *file, int line)
{
return mem_malloc_sub(size, file, line);
}
void *operator new [](uint32_t size, char *file, int line)
{
return mem_malloc_sub(size, file, line);
}
void *operator new(uint32_t 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(POSIX)
// 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) {
LOG_ERROR.printf("Unable to create memory heap! error: %d", GetLastError());
Error("Unable to create memory heap; your system may not have enough memory to run.");
}
ASSERT(Heap);
HeapCompact(Heap, 0);
LOG_DEBUG.printf("System Memory Status:");
LOG_DEBUG.printf("Percent of memory in use: %d", ms.dwMemoryLoad);
LOG_DEBUG.printf("Bytes of physical memory : %d", ms.dwTotalPhys);
LOG_DEBUG.printf("Free physical memory bytes : %d", ms.dwAvailPhys);
LOG_DEBUG.printf("Available virtual memory : %d", ms.dwAvailPageFile);
// See if there is enough memory to run
if (((int64_t)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)) {
LOG_DEBUG << "Using low memory mode!";
Mem_low_memory_mode = true;
return;
} else if ((ms.dwTotalPhys) < (46 * 1024 * 1024)) {
LOG_DEBUG << "Using super low memory mode!";
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(POSIX)
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) {
LOG_ERROR << "Some bozo is trying to allocate a negative length of memory!";
LOG_ERROR.printf("Offending file: %s line: %d", file, line);
Int3();
return NULL;
} else if (size == 0) {
LOG_ERROR.printf("Warning: Zero byte malloc in %s line %d!", 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
LOG_DEBUG << "Out of memory tracking slots!!!!";
#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;
uint16_t 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) {
errors = GetLastError();
LOG_DEBUG << "Unable to alloc memory in mem_malloc_sub()!");
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;
uint16_t mem_sig = MEM_GAURDIAN_SIG;
if (memcmp((char *)memblock + freemem->len, &mem_sig, 2) != 0) {
// Corrupted memory found when we went to free it.
LOG_ERROR << "Memory block found to be damaged when it was freed!";
Int3();
}
Total_mem_used -= freemem->len;
HeapFree(Heap, HEAP_NO_SERIALIZE, memblock);
deleteNode(mynode->data);
return;
} else {
LOG_WARNING << "Warning, hash lookup of memory block failed!";
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, const 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);
uint16_t 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
void *retp = HeapReAlloc(Heap, 0, memblock, size);
return retp;
#endif
}
int mem_size_sub(void *memblock) {
#ifdef MEM_USE_RTL
return _msize(memblock);
#else // MEM_USE_RTL
#if defined(WIN32)
if (!Heap) {
return 0;
}
#endif
if ((void *)MEM_NO_MEMORY_PTR == memblock) {
return 0;
}
return HeapSize(Heap, 0, memblock);
#endif
}
void mem_shutdown();
MemClass::~MemClass() { mem_shutdown(); }
// memory routines
void mem_shutdown() {
// free failsafe memory block.
LOG_DEBUG << "Shutting down memory system.";
if (Mem_failsafe_block) {
mem_free(Mem_failsafe_block);
Mem_failsafe_block = NULL;
}
#ifdef MEM_DEBUG
free(hashTable);
if (Total_mem_used)
LOG_WARNING.printf("%d bytes leaked in mem_malloc heap!", Total_mem_used);
#ifdef MEM_LOGFILE
fclose(mem_out);
#endif
LOG_DEBUG << "Looking for memory leaks.";
for (int i = 0; i < MEM_MAX_MALLOCS; i++) {
if (mem_info[i].ptr != (void *)MEM_NO_MEMORY_PTR) {
LOG_WARNING.printf("Memory leaked from %s line %d length %d.", 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);
}
*/
}
}
LOG_DEBUG << "Done looking for memory leaks.";
LOG_DEBUG.printf("Memory library high water mark: %d", Mem_high_water_mark);
Mem_next_slot = 0;
#endif
if (Heap) {
HeapDestroy(Heap);
Heap = NULL;
}
}
#ifdef MEM_DEBUG
// accessory stuff for mem_dumpallocstofile()
struct allocdumpt {
mem_alloc_info *data;
};
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)) {
// it is 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];
uint16_t mem_sig = MEM_GAURDIAN_SIG;
if (memcmp((char *)freemem->ptr + freemem->len, &mem_sig, 2) != 0) {
LOG_ERROR << "Memory block found to be damaged in mem_heapcheck()!";
LOG_ERROR.printf("Originally allocated from file %s, line %d", freemem->file, freemem->line);
Int3();
}
}
#endif
}
hashTableIndex hash(T data) {
/***********************************
* hash function applied to data *
***********************************/
uint32_t hval = (uint32_t)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