UFO: Alien Invasion
mem.cpp
Go to the documentation of this file.
1
6/*
7All original material Copyright (C) 2002-2022 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;
45 int tagNum;
47 char const* allocFile;
50 size_t memSize;
52 uint32_t botSentinel;
53};
54
55struct memPool_t {
57 bool inUse;
61 uint32_t blockCount;
62 uint32_t byteCount;
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 SDL_LockMutex(z_lock);
213
214 /* Decrement counters */
215 mem->pool->blockCount--;
216 mem->pool->byteCount -= Mem_BlockRawSize(mem);
217
218 /* De-link it */
219 memBlock_t** prev = &mem->pool->blocks[(uintptr_t)mem % MEM_HASH];
220 for (;;) {
221 memBlock_t* search = *prev;
222 if (!search)
223 break;
224
225 if (search == mem) {
226 *prev = search->next;
227 break;
228 }
229 prev = &search->next;
230 }
231
232 SDL_UnlockMutex(z_lock);
233
234 /* Free it */
235 free(mem);
236}
237
241void _Mem_FreeTag (memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
242{
243 if (!pool)
244 return;
245
246 for (int j = 0; j < MEM_HASH; j++) {
247 for (memBlock_t* mem = pool->blocks[j], *next = nullptr; mem; mem = next) {
248 next = mem->next;
249 if (mem->tagNum == tagNum)
250 _Mem_Free(Mem_BlockToPtr(mem), fileName, fileLine);
251 }
252 }
253}
254
260void _Mem_FreePool (memPool_t* pool, const char* fileName, const int fileLine)
261{
262 if (!pool)
263 return;
264
265 for (int j = 0; j < MEM_HASH; j++) {
266 for (memBlock_t* mem = pool->blocks[j], *next = nullptr; mem; mem = next) {
267 next = mem->next;
268 _Mem_Free(Mem_BlockToPtr(mem), fileName, fileLine);
269 }
270 }
271
272 assert(pool->blockCount == 0);
273 assert(pool->byteCount == 0);
274}
275
279void* _Mem_Alloc (size_t size, bool zeroFill, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
280{
281 /* Check pool */
282 if (!pool)
283 Sys_Error("Mem_Alloc: Error - no pool given\n" "alloc: %s:#%i", fileName, fileLine);
284
285 /* Check size */
286 if (size <= 0)
287 Sys_Error("Mem_Alloc: Attempted allocation of '" UFO_SIZE_T "' memory ignored\n" "alloc: %s:#%i", size, fileName, fileLine);
288
289 if (size > 0x40000000)
290 Sys_Error("Mem_Alloc: Attempted allocation of '" UFO_SIZE_T "' bytes!\n" "alloc: %s:#%i", size, fileName, fileLine);
291
292 /* Add header and round to cacheline */
293 size = (size + sizeof(memBlock_t) + sizeof(memBlockFoot_t) + 31) & ~31;
294 memBlock_t* mem = static_cast<memBlock_t* >(malloc(size));
295 if (!mem)
296 Sys_Error("Mem_Alloc: failed on allocation of '" UFO_SIZE_T "' bytes\n" "alloc: %s:#%i", size, fileName, fileLine);
297
298 /* Zero fill */
299 if (zeroFill)
300 memset(mem, 0, size);
301
302 /* Fill in the header */
304 mem->tagNum = tagNum;
305 mem->memSize = size - sizeof(memBlock_t) - sizeof(memBlockFoot_t);
306 mem->pool = pool;
307 mem->allocFile = fileName;
308 mem->allocLine = fileLine;
310
311 /* Fill in the footer */
313
314 SDL_LockMutex(z_lock);
315
316 /* For integrity checking and stats */
317 pool->blockCount++;
318 pool->byteCount += size;
319
320 /* Link it in to the appropriate pool */
321 mem->next = pool->blocks[(uintptr_t)mem % MEM_HASH];
322 pool->blocks[(uintptr_t)mem % MEM_HASH] = mem;
323
324 SDL_UnlockMutex(z_lock);
325
326 return Mem_BlockToPtr(mem);
327}
328
329void* _Mem_ReAlloc (void* ptr, size_t size, const char* fileName, const int fileLine)
330{
331 if (!size)
332 Sys_Error("Use Mem_Free instead");
333
334 if (!ptr)
335 Sys_Error("Use Mem_Alloc instead");
336
337 memBlock_t* const mem = Mem_PtrToBlock(ptr);
338 _Mem_CheckSentinels(mem, fileName, fileLine);
339
340 /* if size matches, do nothing */
341 if (mem->memSize == size)
342 return ptr;
343
344 memPool_t* pool = mem->pool;
345
346 /* allocate memory for the new size */
347 void* newPtr = _Mem_Alloc(size, false, pool, mem->tagNum, fileName, fileLine);
348
349 /* copy old data */
350 memcpy(newPtr, ptr, std::min(mem->memSize, size));
351 if (mem->memSize < size) {
352 const size_t delta = size - mem->memSize;
353 memset((byte*)newPtr + mem->memSize, 0, delta);
354 }
355
356 /* if there was old data, free it */
357 _Mem_Free(ptr, fileName, fileLine);
358
359 _Mem_CheckSentinels(Mem_PtrToBlock(newPtr), fileName, fileLine);
360
361 return newPtr;
362}
363
364/*==============================================================================
365MISC FUNCTIONS
366==============================================================================*/
367
377char* _Mem_PoolStrDupTo (const char* in, char** out, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
378{
379 if (!out)
380 return nullptr;
381 *out = _Mem_PoolStrDup(in, pool, tagNum, fileName, fileLine);
382 return *out;
383}
384
385void* _Mem_PoolDup (const void* in, size_t size, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
386{
387 assert(in != nullptr);
388 assert(size > 0);
389
390 void* copy = _Mem_Alloc(size, false, pool, tagNum, fileName, fileLine);
391 memcpy(copy, in, size);
392 return copy;
393}
394
403char* _Mem_PoolStrDup (const char* in, memPool_t* pool, const int tagNum, const char* fileName, const int fileLine)
404{
405 char* out = (char*)_Mem_Alloc((size_t)(strlen(in) + 1), true, pool, tagNum, fileName, fileLine);
406 strcpy(out, in);
407
408 return out;
409}
410
414uint32_t _Mem_PoolSize (memPool_t* pool)
415{
416 if (!pool)
417 return 0;
418
419 return pool->byteCount;
420}
421
422uint32_t _Mem_ChangeTag (memPool_t* pool, const int tagFrom, const int tagTo)
423{
424 if (!pool)
425 return 0;
426
427 uint32_t numChanged = 0;
428
429 for (int j = 0; j < MEM_HASH; j++) {
430 for (memBlock_t* mem = pool->blocks[j]; mem; mem = mem->next) {
431 if (mem->tagNum == tagFrom) {
432 mem->tagNum = tagTo;
433 numChanged++;
434 }
435 }
436 }
437
438 return numChanged;
439}
440
441static void _Mem_CheckPoolIntegrity (memPool_t* pool, const char* fileName, const int fileLine)
442{
443 uint32_t blocks;
444 uint32_t size;
445 int j = 0;
446
447 assert(pool);
448 if (!pool)
449 return;
450
451 /* Check sentinels */
452 for (j = 0, blocks = 0, size = 0; j < MEM_HASH; j++) {
453 for (memBlock_t* mem = pool->blocks[j]; mem; blocks++, mem = mem->next) {
454 size += Mem_BlockRawSize(mem);
455 _Mem_CheckSentinels(mem, fileName, fileLine);
456 }
457 }
458
459 /* Check block/byte counts */
460 if (pool->blockCount != blocks)
461 Sys_Error("Mem_CheckPoolIntegrity: bad block count\n" "check: %s:#%i", fileName, fileLine);
462 if (pool->byteCount != size)
463 Sys_Error("Mem_CheckPoolIntegrity: bad pool size\n" "check: %s:#%i", fileName, fileLine);
464}
465
466void _Mem_CheckGlobalIntegrity (const char* fileName, const int fileLine)
467{
468 memPool_t* pool;
469 uint32_t i;
470
471 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
472 if (pool->inUse)
473 _Mem_CheckPoolIntegrity(pool, fileName, fileLine);
474 }
475}
476
482bool _Mem_AllocatedInPool (memPool_t* pool, const void* pointer)
483{
484 if (!pool)
485 return false;
486
487 /* if it's in the pool, it must be in THIS block */
488 memBlock_t* mem = pool->blocks[(uintptr_t)pointer % MEM_HASH];
489 /* Cycle through the blocks */
490 for ( ; mem; mem = mem->next) {
491 if (Mem_BlockToPtr(mem) == pointer)
492 return true;
493 }
494
495 return false;
496}
497
498#ifdef COMPILE_UFO
499/*==============================================================================
500CONSOLE COMMANDS
501==============================================================================*/
502
503static void Mem_Check_f (void)
504{
506}
507
508static void Mem_Stats_f (void)
509{
510 uint32_t totalBytes, i;
511 memPool_t* pool;
512
513 if (Cmd_Argc() > 1) {
514 memPool_t* best;
515 memBlock_t* mem;
516
517 best = nullptr;
518 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
519 if (!pool->inUse)
520 continue;
521 if (strstr(pool->name, Cmd_Args())) {
522 if (best) {
523 Com_Printf("Too many matches for '%s'...\n", Cmd_Args());
524 return;
525 }
526 best = pool;
527 }
528 }
529 if (!best) {
530 Com_Printf("No matches for '%s'...\n", Cmd_Args());
531 return;
532 }
533
534 Com_Printf("Pool stats for '%s':\n", best->name);
535 Com_Printf("block line file size \n");
536 Com_Printf("----- ----- -------------------- ---------- \n");
537
538 uint32_t totalBytes = 0;
539 int j = 0;
540 for (j = 0; j < MEM_HASH; j++) {
541 for (i = 0, mem = best->blocks[j]; mem; mem = mem->next, i++) {
542 if (i & 1)
544
545 size_t const size = Mem_BlockRawSize(mem);
546
547 Com_Printf("%5i %5i %20s " UFO_SIZE_T "B\n", i + 1, mem->allocLine, mem->allocFile, size);
548
549 totalBytes += size;
550 }
551 }
552
553 Com_Printf("----------------------------------------\n");
554 Com_Printf("Total: %i blocks, %i bytes (%6.3fMB)\n", i, totalBytes, totalBytes/1048576.0f);
555 return;
556 }
557
558 Com_Printf("Memory stats:\n");
559 Com_Printf(" blocks size name\n");
560 Com_Printf("--- ------ ---------- ---------- --------\n");
561
562 uint32_t totalBlocks = 0;
563 totalBytes = 0;
564 uint32_t poolNum = 0;
565 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
566 if (!pool->inUse)
567 continue;
568
569 poolNum++;
570 if (poolNum & 1)
572
573 Com_Printf("#%2i %6i %9iB (%6.3fMB) %s\n", poolNum, pool->blockCount, pool->byteCount, pool->byteCount/1048576.0f, pool->name);
574
575 totalBlocks += pool->blockCount;
576 totalBytes += pool->byteCount;
577 }
578
579 Com_Printf("----------------------------------------\n");
580 Com_Printf("Total: %i pools, %i blocks, %i bytes (%6.3fMB)\n", i, totalBlocks, totalBytes, totalBytes/1048576.0f);
581}
582#endif
583
588void Mem_Init (void)
589{
590#ifdef COMPILE_UFO
591 Cmd_AddCommand("mem_stats", Mem_Stats_f, "Prints out current internal memory statistics");
592 Cmd_AddCommand("mem_check", Mem_Check_f, "Checks global memory integrity");
593#endif
594
595 z_lock = SDL_CreateMutex();
596}
597
603void Mem_Shutdown (void)
604{
605 memPool_t* pool;
606 int i;
607
608 /* don't use cvars, debug print or anything else inside of this loop
609 * the mem you are trying to use here might already be wiped away */
610 for (i = 0, pool = &m_poolList[0]; i < m_numPools; pool++, i++) {
611 if (!pool->inUse)
612 continue;
613 Mem_DeletePool(pool);
614 }
615
616 SDL_DestroyMutex(z_lock);
617 z_lock = nullptr;
618}
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_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition: cmd.cpp:744
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
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:385
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:588
#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:329
void Mem_Shutdown(void)
Definition: mem.cpp:603
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:241
#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:260
static void _Mem_CheckPoolIntegrity(memPool_t *pool, const char *fileName, const int fileLine)
Definition: mem.cpp:441
void _Mem_CheckGlobalIntegrity(const char *fileName, const int fileLine)
Definition: mem.cpp:466
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:422
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:279
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:403
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:414
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:482
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:377
#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