/* * 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