UFO: Alien Invasion
Loading...
Searching...
No Matches
mem.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Original file from Quake 2 v3.21: quake2-2.31/game/q_shared.c
10Copyright (C) 1997-2001 Id Software, Inc.
11
12This program is free software; you can redistribute it and/or
13modify it under the terms of the GNU General Public License
14as published by the Free Software Foundation; either version 2
15of the License, or (at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with this program; if not, write to the Free Software
25Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27*/
28
29#include "common.h"
30#include <SDL_thread.h>
31
32#define MEM_MAX_POOLNAME 64
33#define MEM_HASH 11
34
36 uint32_t sentinel;
37};
38
39struct memBlock_t {
41
42 uint32_t topSentinel;
43
45 int tagNum;
46
47 char const* allocFile;
49
50 size_t memSize;
51
52 uint32_t botSentinel;
53};
54
55struct memPool_t {
57 bool inUse;
58
60
61 uint32_t blockCount;
62 uint32_t byteCount;
63
64 char const* createFile;
66};
67
68#define MEM_HEAD_SENTINEL_TOP 0xFEBDFAED
69#define MEM_HEAD_SENTINEL_BOT 0xD0BAF0FF
70#define MEM_FOOT_SENTINEL 0xF00DF00D
71
72static SDL_mutex* z_lock;
73
74#define MEM_MAX_POOLCOUNT 32
75
77static uint32_t m_numPools;
78
79/*==============================================================================
80POOL MANAGEMENT
81==============================================================================*/
82
83static memPool_t* Mem_FindPool (const char* name)
84{
85 memPool_t* pool;
86 uint32_t i;
87
88 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
89 if (!pool->inUse)
90 continue;
91 if (!Q_streq(name, pool->name))
92 continue;
93
94 return pool;
95 }
96
97 return nullptr;
98}
99
103memPool_t* _Mem_CreatePool (const char* name, const char* fileName, const int fileLine)
104{
105 /* Check name */
106 if (!name || !name[0])
107 Sys_Error("Mem_CreatePool: nullptr name %s:#%i", fileName, fileLine);
108 if (strlen(name) + 1 >= MEM_MAX_POOLNAME)
109 Com_Printf("Mem_CreatePoole: name '%s' too long, truncating!\n", name);
110
111 /* See if it already exists */
112 memPool_t* pool = Mem_FindPool(name);
113 if (pool)
114 return pool;
115
116 /* Nope, create a slot */
117 uint32_t i;
118 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
119 if (!pool->inUse)
120 break;
121 }
122 if (i == m_numPools) {
123 if (m_numPools + 1 >= MEM_MAX_POOLCOUNT)
124 Sys_Error("Mem_CreatePool: MEM_MAX_POOLCOUNT");
125 pool = &m_poolList[m_numPools++];
126 }
127
128 /* Store values */
129 for (i = 0; i < MEM_HASH; i++)
130 pool->blocks[i] = nullptr;
131 pool->blockCount = 0;
132 pool->byteCount = 0;
133 pool->createFile = fileName;
134 pool->createLine = fileLine;
135 pool->inUse = true;
136 Q_strncpyz(pool->name, name, sizeof(pool->name));
137
138 return pool;
139}
140
146void _Mem_DeletePool (memPool_t* pool, const char* fileName, const int fileLine)
147{
148 if (!pool)
149 return;
150
151 /* Release all allocated memory */
152 _Mem_FreePool(pool, fileName, fileLine);
153
154 /* Simple, yes? */
155 pool->inUse = false;
156 pool->name[0] = '\0';
157}
158
159/*==============================================================================
160POOL AND TAG MEMORY ALLOCATION
161==============================================================================*/
162
163static memBlock_t* Mem_PtrToBlock(void* const ptr)
164{
165 return static_cast<memBlock_t*>(ptr) - 1;
166}
167
168static void* Mem_BlockToPtr(memBlock_t* const mem)
169{
170 return mem + 1;
171}
172
174{
175 return reinterpret_cast<memBlockFoot_t*>(reinterpret_cast<byte*>(Mem_BlockToPtr(mem)) + mem->memSize);
176}
177
178static size_t Mem_BlockRawSize(memBlock_t const* const mem)
179{
180 return mem->memSize + sizeof(memBlock_t) + sizeof(memBlockFoot_t);
181}
182
183static void _Mem_CheckSentinels (memBlock_t* const mem, const char* fileName, const int fileLine)
184{
185 /* Check sentinels */
187 Sys_Error("Mem_CheckSentinels: bad memory header top sentinel [buffer underflow]\n"
188 "free: %s:#%i", fileName, fileLine);
189 } else if (mem->botSentinel != MEM_HEAD_SENTINEL_BOT) {
190 Sys_Error("Mem_CheckSentinels: bad memory header bottom sentinel [buffer underflow]\n"
191 "free: %s:#%i", fileName, fileLine);
192 } else if (Mem_BlockToFooter(mem)->sentinel != MEM_FOOT_SENTINEL) {
193 Sys_Error("Mem_CheckSentinels: bad memory footer sentinel [buffer overflow]\n"
194 "pool: %s\n"
195 "alloc: %s:#%i\n"
196 "free: %s:#%i",
197 mem->pool ? mem->pool->name : "UNKNOWN", mem->allocFile, mem->allocLine, fileName, fileLine);
198 }
199}
200
204void _Mem_Free (void* ptr, const char* fileName, const int fileLine)
205{
206 if (!ptr)
207 return;
208
209 memBlock_t* const mem = Mem_PtrToBlock(ptr);
210 _Mem_CheckSentinels(mem, fileName, fileLine);
211
212 if (z_lock != nullptr)
213 SDL_LockMutex(z_lock);
214
215 /* Decrement counters */
216 mem->pool->blockCount--;
217 mem->pool->byteCount -= Mem_BlockRawSize(mem);
218
219 /* De-link it */
220 memBlock_t** prev = &mem->pool->blocks[(uintptr_t)mem % MEM_HASH];
221 for (;;) {
222 memBlock_t* search = *prev;
223 if (!search)
224 break;
225
226 if (search == mem) {
227 *prev = search->next;
228 break;
229 }
230 prev = &search->next;
231 }
232
233 if (z_lock != nullptr)
234 SDL_UnlockMutex(z_lock);
235
236 /* Free it */
237 free(mem);
238}
239
243void _Mem_FreeTag (memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
244{
245 if (!pool)
246 return;
247
248 for (int j = 0; j < MEM_HASH; j++) {
249 for (memBlock_t* mem = pool->blocks[j], *next = nullptr; mem; mem = next) {
250 next = mem->next;
251 if (mem->tagNum == tagNum)
252 _Mem_Free(Mem_BlockToPtr(mem), fileName, fileLine);
253 }
254 }
255}
256
262void _Mem_FreePool (memPool_t* pool, const char* fileName, const int fileLine)
263{
264 if (!pool)
265 return;
266
267 for (int j = 0; j < MEM_HASH; j++) {
268 for (memBlock_t* mem = pool->blocks[j], *next = nullptr; mem; mem = next) {
269 next = mem->next;
270 _Mem_Free(Mem_BlockToPtr(mem), fileName, fileLine);
271 }
272 }
273
274 assert(pool->blockCount == 0);
275 assert(pool->byteCount == 0);
276}
277
281void* _Mem_Alloc (size_t size, bool zeroFill, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
282{
283 /* Check pool */
284 if (!pool)
285 Sys_Error("Mem_Alloc: Error - no pool given\n" "alloc: %s:#%i", fileName, fileLine);
286
287 /* Check size */
288 if (size <= 0)
289 Sys_Error("Mem_Alloc: Attempted allocation of '" UFO_SIZE_T "' memory ignored\n" "alloc: %s:#%i", size, fileName, fileLine);
290
291 if (size > 0x40000000)
292 Sys_Error("Mem_Alloc: Attempted allocation of '" UFO_SIZE_T "' bytes!\n" "alloc: %s:#%i", size, fileName, fileLine);
293
294 /* Add header and round to cacheline */
295 size = (size + sizeof(memBlock_t) + sizeof(memBlockFoot_t) + 31) & ~31;
296 memBlock_t* mem = static_cast<memBlock_t* >(malloc(size));
297 if (!mem)
298 Sys_Error("Mem_Alloc: failed on allocation of '" UFO_SIZE_T "' bytes\n" "alloc: %s:#%i", size, fileName, fileLine);
299
300 /* Zero fill */
301 if (zeroFill)
302 memset(mem, 0, size);
303
304 /* Fill in the header */
306 mem->tagNum = tagNum;
307 mem->memSize = size - sizeof(memBlock_t) - sizeof(memBlockFoot_t);
308 mem->pool = pool;
309 mem->allocFile = fileName;
310 mem->allocLine = fileLine;
312
313 /* Fill in the footer */
315
316 if (z_lock != nullptr)
317 SDL_LockMutex(z_lock);
318
319 /* For integrity checking and stats */
320 pool->blockCount++;
321 pool->byteCount += size;
322
323 /* Link it in to the appropriate pool */
324 mem->next = pool->blocks[(uintptr_t)mem % MEM_HASH];
325 pool->blocks[(uintptr_t)mem % MEM_HASH] = mem;
326
327 if (z_lock != nullptr)
328 SDL_UnlockMutex(z_lock);
329
330 return Mem_BlockToPtr(mem);
331}
332
333void* _Mem_ReAlloc (void* ptr, size_t size, const char* fileName, const int fileLine)
334{
335 if (!size)
336 Sys_Error("Use Mem_Free instead");
337
338 if (!ptr)
339 Sys_Error("Use Mem_Alloc instead");
340
341 memBlock_t* const mem = Mem_PtrToBlock(ptr);
342 _Mem_CheckSentinels(mem, fileName, fileLine);
343
344 /* if size matches, do nothing */
345 if (mem->memSize == size)
346 return ptr;
347
348 memPool_t* pool = mem->pool;
349
350 /* allocate memory for the new size */
351 void* newPtr = _Mem_Alloc(size, false, pool, mem->tagNum, fileName, fileLine);
352
353 /* copy old data */
354 memcpy(newPtr, ptr, std::min(mem->memSize, size));
355 if (mem->memSize < size) {
356 const size_t delta = size - mem->memSize;
357 memset((byte*)newPtr + mem->memSize, 0, delta);
358 }
359
360 /* if there was old data, free it */
361 _Mem_Free(ptr, fileName, fileLine);
362
363 _Mem_CheckSentinels(Mem_PtrToBlock(newPtr), fileName, fileLine);
364
365 return newPtr;
366}
367
368/*==============================================================================
369MISC FUNCTIONS
370==============================================================================*/
371
381char* _Mem_PoolStrDupTo (const char* in, char** out, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
382{
383 if (!out)
384 return nullptr;
385 *out = _Mem_PoolStrDup(in, pool, tagNum, fileName, fileLine);
386 return *out;
387}
388
389void* _Mem_PoolDup (const void* in, size_t size, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
390{
391 assert(in != nullptr);
392 assert(size > 0);
393
394 void* copy = _Mem_Alloc(size, false, pool, tagNum, fileName, fileLine);
395 memcpy(copy, in, size);
396 return copy;
397}
398
407char* _Mem_PoolStrDup (const char* in, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
408{
409 char* out = (char*)_Mem_Alloc((size_t)(strlen(in) + 1), true, pool, tagNum, fileName, fileLine);
410 strcpy(out, in);
411
412 return out;
413}
414
418uint32_t _Mem_PoolSize (memPool_t* pool)
419{
420 if (!pool)
421 return 0;
422
423 return pool->byteCount;
424}
425
426uint32_t _Mem_ChangeTag (memPool_t* pool, const int tagFrom, const int tagTo)
427{
428 if (!pool)
429 return 0;
430
431 uint32_t numChanged = 0;
432
433 for (int j = 0; j < MEM_HASH; j++) {
434 for (memBlock_t* mem = pool->blocks[j]; mem; mem = mem->next) {
435 if (mem->tagNum == tagFrom) {
436 mem->tagNum = tagTo;
437 numChanged++;
438 }
439 }
440 }
441
442 return numChanged;
443}
444
445static void _Mem_CheckPoolIntegrity (memPool_t* pool, const char* fileName, const int fileLine)
446{
447 uint32_t blocks;
448 uint32_t size;
449 int j = 0;
450
451 assert(pool);
452 if (!pool)
453 return;
454
455 /* Check sentinels */
456 for (j = 0, blocks = 0, size = 0; j < MEM_HASH; j++) {
457 for (memBlock_t* mem = pool->blocks[j]; mem; blocks++, mem = mem->next) {
458 size += Mem_BlockRawSize(mem);
459 _Mem_CheckSentinels(mem, fileName, fileLine);
460 }
461 }
462
463 /* Check block/byte counts */
464 if (pool->blockCount != blocks)
465 Sys_Error("Mem_CheckPoolIntegrity: bad block count\n" "check: %s:#%i", fileName, fileLine);
466 if (pool->byteCount != size)
467 Sys_Error("Mem_CheckPoolIntegrity: bad pool size\n" "check: %s:#%i", fileName, fileLine);
468}
469
470void _Mem_CheckGlobalIntegrity (const char* fileName, const int fileLine)
471{
472 memPool_t* pool;
473 uint32_t i;
474
475 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
476 if (pool->inUse)
477 _Mem_CheckPoolIntegrity(pool, fileName, fileLine);
478 }
479}
480
486bool _Mem_AllocatedInPool (memPool_t* pool, const void* pointer)
487{
488 if (!pool)
489 return false;
490
491 /* if it's in the pool, it must be in THIS block */
492 memBlock_t* mem = pool->blocks[(uintptr_t)pointer % MEM_HASH];
493 /* Cycle through the blocks */
494 for ( ; mem; mem = mem->next) {
495 if (Mem_BlockToPtr(mem) == pointer)
496 return true;
497 }
498
499 return false;
500}
501
506void Mem_Init (void)
507{
508 z_lock = SDL_CreateMutex();
509 if (z_lock == nullptr)
510 Sys_Error("Mem_Init: Cannot create mutex for memory management: %s", SDL_GetError());
511}
512
518void Mem_Shutdown (void)
519{
520 memPool_t* pool;
521 int i;
522
523 /* don't use cvars, debug print or anything else inside of this loop
524 * the mem you are trying to use here might already be wiped away */
525 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
526 if (!pool->inUse)
527 continue;
528 Mem_DeletePool(pool);
529 }
530
531 if (z_lock != nullptr)
532 SDL_DestroyMutex(z_lock);
533 z_lock = nullptr;
534}
535
536#ifdef COMPILE_UFO
537/*==============================================================================
538CONSOLE COMMANDS
539==============================================================================*/
540
541static void Mem_Check_f (void)
542{
544}
545
546static void Mem_Stats_f (void)
547{
548 uint32_t totalBytes, i;
549 memPool_t* pool;
550
551 if (Cmd_Argc() > 1) {
552 memPool_t* best;
553 memBlock_t* mem;
554
555 best = nullptr;
556 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
557 if (!pool->inUse)
558 continue;
559 if (strstr(pool->name, Cmd_Args())) {
560 if (best) {
561 Com_Printf("Too many matches for '%s'...\n", Cmd_Args());
562 return;
563 }
564 best = pool;
565 }
566 }
567 if (!best) {
568 Com_Printf("No matches for '%s'...\n", Cmd_Args());
569 return;
570 }
571
572 Com_Printf("Pool stats for '%s':\n", best->name);
573 Com_Printf("block line file size \n");
574 Com_Printf("----- ----- -------------------- ---------- \n");
575
576 uint32_t totalBytes = 0;
577 int j = 0;
578 for (j = 0; j < MEM_HASH; j++) {
579 for (i = 0, mem = best->blocks[j]; mem; mem = mem->next, i++) {
580 if (i & 1)
582
583 size_t const size = Mem_BlockRawSize(mem);
584
585 Com_Printf("%5i %5i %20s " UFO_SIZE_T "B\n", i + 1, mem->allocLine, mem->allocFile, size);
586
587 totalBytes += size;
588 }
589 }
590
591 Com_Printf("----------------------------------------\n");
592 Com_Printf("Total: %i blocks, %i bytes (%6.3fMB)\n", i, totalBytes, totalBytes/1048576.0f);
593 return;
594 }
595
596 Com_Printf("Memory stats:\n");
597 Com_Printf(" blocks size name\n");
598 Com_Printf("--- ------ ---------- ---------- --------\n");
599
600 uint32_t totalBlocks = 0;
601 totalBytes = 0;
602 uint32_t poolNum = 0;
603 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
604 if (!pool->inUse)
605 continue;
606
607 poolNum++;
608 if (poolNum & 1)
610
611 Com_Printf("#%2i %6i %9iB (%6.3fMB) %s\n", poolNum, pool->blockCount, pool->byteCount, pool->byteCount/1048576.0f, pool->name);
612
613 totalBlocks += pool->blockCount;
614 totalBytes += pool->byteCount;
615 }
616
617 Com_Printf("----------------------------------------\n");
618 Com_Printf("Total: %i pools, %i blocks, %i bytes (%6.3fMB)\n", i, totalBlocks, totalBytes, totalBytes/1048576.0f);
619}
620
624static const cmdList_t memCallbacks[] = {
625 {"mem_stats", Mem_Stats_f, "Prints out current internal memory statistics"},
626 {"mem_check", Mem_Check_f, "Checks global memory integrity"},
627 {nullptr, nullptr, nullptr}
628};
629
633void Mem_InitCallbacks (void)
634{
635 Cmd_TableAddList(memCallbacks);
636}
637
641void Mem_ShutdownCallbacks (void)
642{
643 Cmd_TableRemoveList(memCallbacks);
644}
645#endif
void Cmd_TableAddList(const cmdList_t *cmdList)
Definition cmd.cpp:853
const char * Cmd_Args(void)
Returns a single string containing argv(1) to argv(argc()-1).
Definition cmd.cpp:526
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition cmd.cpp:505
void Cmd_TableRemoveList(const cmdList_t *cmdList)
Definition cmd.cpp:859
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
definitions common between client and server, but not game lib
#define S_COLOR_GREEN
Definition common.h:219
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
voidpf void uLong size
Definition ioapi.h:42
static void _Mem_CheckSentinels(memBlock_t *const mem, const char *fileName, const int fileLine)
Definition mem.cpp:183
#define MEM_MAX_POOLNAME
Definition mem.cpp:32
void * _Mem_PoolDup(const void *in, size_t size, memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
Definition mem.cpp:389
static void * Mem_BlockToPtr(memBlock_t *const mem)
Definition mem.cpp:168
#define MEM_HASH
Definition mem.cpp:33
void Mem_Init(void)
Definition mem.cpp:506
#define MEM_FOOT_SENTINEL
Definition mem.cpp:70
#define MEM_MAX_POOLCOUNT
Definition mem.cpp:74
static memPool_t * Mem_FindPool(const char *name)
Definition mem.cpp:83
void * _Mem_ReAlloc(void *ptr, size_t size, const char *fileName, const int fileLine)
Definition mem.cpp:333
void Mem_Shutdown(void)
Definition mem.cpp:518
void _Mem_FreeTag(memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
Free memory blocks assigned to a specified tag within a pool.
Definition mem.cpp:243
#define MEM_HEAD_SENTINEL_TOP
Definition mem.cpp:68
void _Mem_DeletePool(memPool_t *pool, const char *fileName, const int fileLine)
Definition mem.cpp:146
void _Mem_FreePool(memPool_t *pool, const char *fileName, const int fileLine)
Free all items within a pool.
Definition mem.cpp:262
static void _Mem_CheckPoolIntegrity(memPool_t *pool, const char *fileName, const int fileLine)
Definition mem.cpp:445
void _Mem_CheckGlobalIntegrity(const char *fileName, const int fileLine)
Definition mem.cpp:470
static uint32_t m_numPools
Definition mem.cpp:77
uint32_t _Mem_ChangeTag(memPool_t *pool, const int tagFrom, const int tagTo)
Definition mem.cpp:426
static size_t Mem_BlockRawSize(memBlock_t const *const mem)
Definition mem.cpp:178
#define MEM_HEAD_SENTINEL_BOT
Definition mem.cpp:69
static SDL_mutex * z_lock
Definition mem.cpp:72
void * _Mem_Alloc(size_t size, bool zeroFill, memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
Optionally returns 0 filled memory allocated in a pool with a tag.
Definition mem.cpp:281
char * _Mem_PoolStrDup(const char *in, memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
No need to null terminate the extra spot because Mem_Alloc returns zero-filled memory.
Definition mem.cpp:407
memPool_t * _Mem_CreatePool(const char *name, const char *fileName, const int fileLine)
Definition mem.cpp:103
uint32_t _Mem_PoolSize(memPool_t *pool)
Definition mem.cpp:418
static memBlockFoot_t * Mem_BlockToFooter(memBlock_t *const mem)
Definition mem.cpp:173
static memBlock_t * Mem_PtrToBlock(void *const ptr)
Definition mem.cpp:163
void _Mem_Free(void *ptr, const char *fileName, const int fileLine)
Definition mem.cpp:204
static memPool_t m_poolList[MEM_MAX_POOLCOUNT]
Definition mem.cpp:76
bool _Mem_AllocatedInPool(memPool_t *pool, const void *pointer)
Definition mem.cpp:486
char * _Mem_PoolStrDupTo(const char *in, char **out, memPool_t *pool, const int tagNum, const char *fileName, const int fileLine)
Saves a string to client hunk.
Definition mem.cpp:381
#define Mem_DeletePool(pool)
Definition mem.h:33
#define Mem_CheckGlobalIntegrity()
Definition mem.h:54
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
QGL_EXTERN GLint GLenum GLboolean GLsizei const GLvoid * pointer
Definition r_gl.h:94
#define Q_streq(a, b)
Definition shared.h:136
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
uint32_t botSentinel
Definition mem.cpp:52
size_t memSize
Definition mem.cpp:50
memPool_t * pool
Definition mem.cpp:44
int allocLine
Definition mem.cpp:48
memBlock_t * next
Definition mem.cpp:40
uint32_t topSentinel
Definition mem.cpp:42
int tagNum
Definition mem.cpp:45
char const * allocFile
Definition mem.cpp:47
uint32_t sentinel
Definition mem.cpp:36
uint32_t byteCount
Definition mem.cpp:62
uint32_t blockCount
Definition mem.cpp:61
bool inUse
Definition mem.cpp:57
int createLine
Definition mem.cpp:65
char const * createFile
Definition mem.cpp:64
char name[MEM_MAX_POOLNAME]
Definition mem.cpp:56
memBlock_t * blocks[MEM_HASH]
Definition mem.cpp:59
#define UFO_SIZE_T
Definition ufotypes.h:89