2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-01 09:35:37 +03:00
rehlds/rehlds/engine/zone.cpp
s1lent f5784be364
Reversed CIPRateLimit
Minor refactoring
2017-12-19 21:34:15 +07:00

984 lines
21 KiB
C++

/*
*
* 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 2 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, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
// ZONE MEMORY ALLOCATION
//
// There is never any space between memblocks, and there will never be two
// contiguous free memblocks.
//
// The rover can be left pointing at a non-empty block
//
// The zone calls are pretty much only used for small strings and structures,
// all big things are allocated on the hunk.
#define ZONEID 0x001d4a11
const int MINFRAGMENT = 64;
typedef struct memblock_s
{
int size;
int tag;
int id;
memblock_t *next;
memblock_t *prev;
int pad;
} memblock_t;
typedef struct memzone_s
{
int size;
memblock_t blocklist;
memblock_t *rover;
} memzone_t;
cvar_t mem_dbgfile = { "mem_dbgfile", ".\\mem.txt", 0, 0.0f, NULL };
memzone_t *mainzone;
void Z_ClearZone(memzone_t *zone, int size)
{
memblock_t *block = (memblock_t *)&zone[1];
zone->blocklist.prev = zone->blocklist.next = zone->rover = block;
zone->blocklist.size = zone->blocklist.id = 0;
zone->blocklist.tag = 1;
block->prev = block->next = &zone->blocklist;
block->tag = 0;
block->id = ZONEID;
block->size = size - sizeof(memzone_t);
}
void Z_Free(void *ptr)
{
if (!ptr)
{
Sys_Error("%s: NULL pointer", __func__);
}
memblock_t *block = (memblock_t *)((char *)ptr - sizeof(memblock_t));
if (block->id != ZONEID)
{
Sys_Error("%s: freed a pointer without ZONEID", __func__);
}
if (!block->tag)
{
Sys_Error("%s: freed a freed pointer", __func__);
}
block->tag = 0;
memblock_t *otherblock = block->prev;
if (!otherblock->tag)
{
otherblock->size += block->size;
otherblock->next = block->next;
block->next->prev = otherblock;
if (block == mainzone->rover)
{
mainzone->rover = otherblock;
}
block = otherblock;
}
otherblock = block->next;
if (!otherblock->tag)
{
block->size += otherblock->size;
block->next = otherblock->next;
otherblock->next->prev = block;
if (otherblock == mainzone->rover)
{
mainzone->rover = block;
}
}
}
void *Z_Malloc(int size)
{
Z_CheckHeap();
void *buf = Z_TagMalloc(size, 1);
if (!buf)
{
Sys_Error("%s: failed on allocation of %i bytes", __func__, size);
}
Q_memset(buf, 0, size);
return buf;
}
void *Z_TagMalloc(int size, int tag)
{
int extra;
memblock_t *start, *rover, *newz, *base;
if (tag == 0)
{
Sys_Error("%s: tried to use a 0 tag", __func__);
}
size += sizeof(memblock_t);
size += 4;
size = (size + 7) & ~7;
base = rover = mainzone->rover;
start = base->prev;
do
{
if (rover == start)
{
return(NULL);
}
if (rover->tag)
{
base = rover = rover->next;
}
else
{
rover = rover->next;
}
} while (base->tag || base->size < size);
extra = base->size - size;
if (extra > MINFRAGMENT)
{
newz = (memblock_t *)((byte *)base + size);
newz->size = extra;
newz->tag = 0;
newz->prev = base;
newz->id = ZONEID;
newz->next = base->next;
newz->next->prev = newz;
base->next = newz;
base->size = size;
}
base->tag = tag;
mainzone->rover = base->next;
base->id = ZONEID;
// marker for memory trash testing
*(int *)((byte *)base + base->size - 4) = ZONEID;
return (void *)((byte *)base + sizeof(memblock_t));
}
NOXREF void Z_Print(memzone_t *zone)
{
NOXREFCHECK;
memblock_t *block;
Con_Printf("zone size: %i location: %p\n", mainzone->size, mainzone);
for (block = zone->blocklist.next; ; block = block->next)
{
Con_Printf("block:%p size:%7i tag:%3i\n", block, block->size, block->tag);
if (block->next == &zone->blocklist)
{
break; // all blocks have been hit
}
if ((byte *)block + block->size != (byte *)block->next)
{
Con_Printf("ERROR: block size does not touch the next block\n");
}
if (block->next->prev != block)
{
Con_Printf("ERROR: next block doesn't have proper back link\n");
}
if (!block->tag && !block->next->tag)
{
Con_Printf("ERROR: two consecutive free blocks\n");
}
}
}
void Z_CheckHeap()
{
memblock_t *block;
for (block = mainzone->blocklist.next; ; block = block->next)
{
if (block->next == &mainzone->blocklist)
{
break;
}
if ((byte *)block + block->size != (byte *)block->next)
{
Sys_Error("%s: block size does not touch the next block\n", __func__);
}
if (block->next->prev != block)
{
Sys_Error("%s: next block doesn't have proper back link\n", __func__);
}
if (!block->tag && !block->next->tag)
{
Sys_Error("%s: two consecutive free blocks\n", __func__);
}
}
}
const int HUNK_NAME_LEN = 64;
#define HUNK_SENTINEL 0x1df001ed
typedef struct hunk_s
{
int sentinel;
int size;
char name[HUNK_NAME_LEN];
} hunk_t;
byte *hunk_base;
int hunk_size;
int hunk_low_used;
int hunk_high_used;
qboolean hunk_tempactive;
int hunk_tempmark;
// Run consistency and sentinel trashing checks
void Hunk_Check()
{
hunk_t *h;
for (h = (hunk_t *)hunk_base; (byte *)h != (hunk_base + hunk_low_used); h = (hunk_t *)((byte *)h + h->size))
{
if (h->sentinel != HUNK_SENTINEL)
{
Sys_Error("%s: trahsed sentinel", __func__);
}
if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
{
Sys_Error("%s: bad size", __func__);
}
}
}
// If "all" is specified, every single allocation is printed.
// Otherwise, allocations with the same name will be totaled up before printing.
NOXREF void Hunk_Print(qboolean all)
{
NOXREFCHECK;
hunk_t *h, *next, *endlow, *starthigh, *endhigh;
int count, sum;
int totalblocks;
char name[HUNK_NAME_LEN];
name[HUNK_NAME_LEN - 1] = 0;
count = 0;
sum = 0;
totalblocks = 0;
h = (hunk_t *)hunk_base;
endlow = (hunk_t *)(hunk_base + hunk_low_used);
starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
endhigh = (hunk_t *)(hunk_base + hunk_size);
Con_Printf(" :%8i total hunk size\n", hunk_size);
Con_Printf("-------------------------\n");
while (true)
{
// skip to the high hunk if done with low hunk
if (h == endlow)
{
Con_Printf("-------------------------\n");
Con_Printf(" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
Con_Printf("-------------------------\n");
h = starthigh;
}
// if totally done, break
if (h == endhigh)
break;
// run consistancy checks
if (h->sentinel != HUNK_SENTINEL)
Sys_Error("%s: trahsed sentinal", __func__);
if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
Sys_Error("%s: bad size", __func__);
next = (hunk_t *)((byte *)h + h->size);
count++;
totalblocks++;
sum += h->size;
// print the single block
Q_memcpy(name, h->name, HUNK_NAME_LEN);
if (all)
Con_Printf("%8p :%8i %8s\n", h, h->size, name);
// print the total
if (next == endlow || next == endhigh ||
Q_strncmp(h->name, next->name, HUNK_NAME_LEN))
{
if (!all)
Con_Printf(" :%8i %8s (TOTAL)\n", sum, name);
count = 0;
sum = 0;
}
h = next;
}
Con_Printf("-------------------------\n");
Con_Printf("%8i total blocks\n", totalblocks);
}
void *Hunk_AllocName(int size, const char *name)
{
if (size < 0)
{
Sys_Error("%s: bad size: %i", __func__, size);
}
int totalsize = ((size + 15) & ~15) + sizeof(hunk_t);
if (hunk_size - hunk_high_used - hunk_low_used < totalsize)
{
Sys_Error("%s: failed on %i bytes", __func__, totalsize);
}
hunk_t *h = (hunk_t *)(hunk_base + hunk_low_used);
hunk_low_used += totalsize;
Cache_FreeLow(hunk_low_used);
Q_memset(h, 0, totalsize);
h->size = totalsize;
h->sentinel = HUNK_SENTINEL;
Q_strncpy(h->name, name, HUNK_NAME_LEN - 1);
h->name[HUNK_NAME_LEN - 1] = 0;
return (void *)(h + 1);
}
void *Hunk_Alloc(int size)
{
return Hunk_AllocName(size, "unknown");
}
int Hunk_LowMark()
{
return hunk_low_used;
}
void Hunk_FreeToLowMark(int mark)
{
if (mark < 0 || mark > hunk_low_used)
{
Sys_Error("%s: bad mark %i", __func__, mark);
}
hunk_low_used = mark;
}
int Hunk_HighMark()
{
if (hunk_tempactive)
{
hunk_tempactive = FALSE;
Hunk_FreeToHighMark(hunk_tempmark);
}
return hunk_high_used;
}
void Hunk_FreeToHighMark(int mark)
{
if (hunk_tempactive)
{
hunk_tempactive = FALSE;
Hunk_FreeToHighMark(hunk_tempmark);
}
if (mark < 0 || mark > hunk_high_used)
{
Sys_Error("%s: bad mark %i", __func__, mark);
}
hunk_high_used = mark;
}
void *Hunk_HighAllocName(int size, const char *name)
{
hunk_t *h;
if (size < 0)
{
Sys_Error("%s: bad size: %i", __func__, size);
}
if (hunk_tempactive)
{
Hunk_FreeToHighMark(hunk_tempmark);
hunk_tempactive = FALSE;
}
size = ((size + 15) & ~15) + sizeof(hunk_t);
if (hunk_size - hunk_high_used - hunk_low_used < size)
{
Con_Printf("%s: failed on %i bytes\n", __func__, size);
return 0;
}
hunk_high_used += size;
Cache_FreeHigh(hunk_high_used);
h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
Q_memset(h, 0, size);
h->size = size;
h->sentinel = HUNK_SENTINEL;
Q_strncpy(h->name, name, HUNK_NAME_LEN - 1);
h->name[HUNK_NAME_LEN - 1] = 0;
return (void *)(h + 1);
}
// Return space from the top of the hunk
void *Hunk_TempAlloc(int size)
{
void *buf;
if (hunk_tempactive)
{
Hunk_FreeToHighMark(hunk_tempmark);
hunk_tempactive = 0;
}
hunk_tempmark = Hunk_HighMark();
buf = Hunk_HighAllocName((size + 15) & ~15, "temp");
hunk_tempactive = 1;
return buf;
}
// CACHE MEMORY
const int CACHE_NAME_LEN = 64;
typedef struct cache_system_s
{
int size;
cache_user_t *user;
char name[CACHE_NAME_LEN];
cache_system_t *prev;
cache_system_t *next;
cache_system_t *lru_prev;
cache_system_t *lru_next;
} cache_system_t;
cache_system_t cache_head;
void Cache_Move(cache_system_t *c)
{
cache_system_t *newmem = Cache_TryAlloc(c->size, 1);
if (!newmem)
{
Cache_Free(c->user);
}
else
{
Q_memcpy(newmem + 1, c + 1, c->size - sizeof(cache_system_t));
newmem->user = c->user;
Q_memcpy(newmem->name, c->name, sizeof(newmem->name));
Cache_Free(c->user);
newmem->user->data = (void *)(newmem + 1);
}
}
// Throw things out until the hunk can be expanded to the given point
void Cache_FreeLow(int new_low_hunk)
{
cache_system_t *c;
while (true)
{
c = cache_head.next;
if (c == &cache_head)
return; // nothing in cache at all
if ((byte *)c >= hunk_base + new_low_hunk)
return; // there is space to grow the hunk
Cache_Move(c); // reclaim the space
}
}
// Throw things out until the hunk can be expanded to the given point
void Cache_FreeHigh(int new_high_hunk)
{
cache_system_t *c, *prev;
prev = NULL;
while (true)
{
c = cache_head.prev;
if (c == &cache_head)
return; // nothing in cache at all
if ((byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
return; // there is space to grow the hunk
if (c == prev)
Cache_Free(c->user); // didn't move out of the way
else
{
Cache_Move(c); // try to move it
prev = c;
}
}
}
void Cache_UnlinkLRU(cache_system_t *cs)
{
if (!cs->lru_next || !cs->lru_prev)
{
Sys_Error("%s: NULL link", __func__);
}
cs->lru_next->lru_prev = cs->lru_prev;
cs->lru_prev->lru_next = cs->lru_next;
cs->lru_next = cs->lru_prev = 0;
}
void Cache_MakeLRU(cache_system_t *cs)
{
if (cs->lru_next || cs->lru_prev)
{
Sys_Error("%s: active link", __func__);
}
cache_head.lru_next->lru_prev = cs;
cs->lru_next = cache_head.lru_next;
cs->lru_prev = &cache_head;
cache_head.lru_next = cs;
}
// Looks for a free block of memory between the high and low hunk marks
// Size should already include the header and padding
cache_system_t *Cache_TryAlloc(int size, qboolean nobottom)
{
cache_system_t *cs;
cache_system_t *newmem;
if (!nobottom && cache_head.prev == &cache_head)
{
if (hunk_size - hunk_low_used - hunk_high_used < size)
{
Sys_Error("%s: %i is greater then free hunk", __func__, size);
}
newmem = (cache_system_t *)(hunk_base + hunk_low_used);
Q_memset(newmem, 0, sizeof(cache_system_t));
newmem->size = size;
cache_head.next = cache_head.prev = newmem;
newmem->next = newmem->prev = &cache_head;
Cache_MakeLRU(newmem);
return newmem;
}
cs = cache_head.next;
newmem = (cache_system_t *)(hunk_base + hunk_low_used);
do
{
if ((!nobottom || cs != cache_head.next) && (signed int)((char *)cs - (char *)newmem) >= size)
{
Q_memset(newmem, 0, sizeof(cache_system_t));
newmem->size = size;
newmem->next = cs;
newmem->prev = cs->prev;
cs->prev->next = newmem;
cs->prev = newmem;
Cache_MakeLRU(newmem);
return newmem;
}
newmem = (cache_system_t *)((char *)cs + cs->size);
cs = cs->next;
} while (cs != &cache_head);
if ((int)(hunk_size + hunk_base - hunk_high_used - (byte *)newmem) < size)
{
return 0;
}
Q_memset(newmem, 0, sizeof(cache_system_t));
newmem->size = size;
newmem->next = &cache_head;
newmem->prev = cache_head.prev;
cache_head.prev->next = newmem;
cache_head.prev = newmem;
Cache_MakeLRU(newmem);
return newmem;
}
// Throw everything out, so new data will be demand cached
void Cache_Force_Flush()
{
cache_system_t *i;
for (i = cache_head.next; cache_head.next != &cache_head; i = cache_head.next)
{
Cache_Free(i->user);
}
}
void Cache_Flush()
{
if (g_pcl.maxclients <= 1 || allow_cheats)
{
Cache_Force_Flush();
}
else
{
Con_Printf("Server must enable sv_cheats to activate the flush command in multiplayer games.\n");
}
}
// Compares the names of two cache_system_t structs.
// Used with qsort()
NOXREF int CacheSystemCompare(const void *ppcs1, const void *ppcs2)
{
NOXREFCHECK;
cache_system_t *pcs1 = (cache_system_t *)ppcs1;
cache_system_t *pcs2 = (cache_system_t *)ppcs2;
return Q_stricmp(pcs1->name, pcs2->name);
}
NOXREF void Cache_Print()
{
NOXREFCHECK;
cache_system_t *cd;
for (cd = cache_head.next; cd != &cache_head; cd = cd->next)
{
Con_Printf("%8i : %s\n", cd->size, cd->name);
}
}
// compares the first directory of two paths...
// (so "foo/bar" will match "foo/fred"
NOXREF int ComparePath1(char *path1, char *path2)
{
NOXREFCHECK;
while (*path1 != '/' && *path1 != '\\' && *path1)
{
if (*path1 != *path2)
return 0;
else
{
path1++;
path2++;
}
}
return 1;
}
// Takes a number, and creates a string of that with commas in the appropriate places.
NOXREF char *CommatizeNumber(int num, char *pout)
{
NOXREFCHECK;
// this is probably more complex than it needs to be.
int len = 0;
int i;
char outbuf[50];
Q_memset(outbuf, 0, 50);
while (num)
{
char tempbuf[50];
int temp = num % 1000;
num = num / 1000;
Q_strcpy(tempbuf, outbuf);
Q_snprintf(outbuf, sizeof(outbuf), ",%03i%s", temp, tempbuf);
}
len = Q_strlen(outbuf);
for (i = 0; i < len; i++) // find first significant digit
if (outbuf[i] != '0' && outbuf[i] != ',')
break;
if (i == len)
Q_strcpy(pout, "0");
else
Q_strcpy(pout, &outbuf[i]); // copy from i to get rid of the first comma and leading zeros
return pout;
}
NOXREF void Cache_Report()
{
NOXREFCHECK;
Con_DPrintf("%4.1f megabyte data cache\n", (hunk_size - hunk_low_used - hunk_high_used) / (float)(1024 * 1024));
}
NOXREF void Cache_Compact()
{
NOXREFCHECK;
}
void Cache_Init()
{
cache_head.next = cache_head.prev = &cache_head;
cache_head.lru_next = cache_head.lru_prev = &cache_head;
Cmd_AddCommand("flush", Cache_Flush);
}
// Frees the memory and removes it from the LRU list
void Cache_Free(cache_user_t *c)
{
if (!c->data)
{
Sys_Error("%s: not allocated", __func__);
}
cache_system_t *cs = ((cache_system_t *)c->data - 1);
cs->prev->next = cs->next;
cs->next->prev = cs->prev;
c->data = cs->prev = cs->next = 0;
Cache_UnlinkLRU(cs);
}
NOXREF int Cache_TotalUsed()
{
NOXREFCHECK;
cache_system_t *cd;
int Total = 0;
for (cd = cache_head.next; cd != &cache_head; cd = cd->next)
Total += cd->size;
return Total;
}
void* EXT_FUNC Cache_Check(cache_user_t *c)
{
cache_system_t *cs;
if (c->data)
{
cs = (cache_system_t *)((char *)c->data - sizeof(cache_system_t));
Cache_UnlinkLRU((cache_system_t *)c->data - 1);
Cache_MakeLRU(cs);
}
return c->data;
}
void *Cache_Alloc(cache_user_t *c, int size, char *name)
{
cache_system_t *cs;
if (c->data)
{
Sys_Error("%s: already allocated", __func__);
}
if (size <= 0)
{
Sys_Error("%s: size %i", __func__, size);
}
while (true)
{
cs = Cache_TryAlloc((size + sizeof(cache_system_t) + 15) & ~15, 0);
if (cs)
{
Q_strncpy(cs->name, name, CACHE_NAME_LEN - 1);
cs->name[CACHE_NAME_LEN - 1] = 0;
c->data = cs + 1;
cs->user = c;
break;
}
if (cache_head.lru_prev == &cache_head)
{
Sys_Error("%s: out of memory", __func__);
}
Cache_Free(cache_head.lru_prev->user);
}
return Cache_Check(c);
}
void Memory_Init(void *buf, int size)
{
int zonesize = ZONE_DYNAMIC_SIZE;
hunk_base = (byte *)buf;
hunk_size = size;
hunk_low_used = 0;
hunk_high_used = 0;
Cache_Init();
int p = COM_CheckParm("-zone");
if (p)
{
if (p < com_argc - 1)
{
zonesize = Q_atoi(com_argv[p + 1]) * 1024;
}
else
{
Sys_Error("%s: you must specify a size in KB after -zone", __func__);
}
}
mainzone = ((memzone_t *)Hunk_AllocName(zonesize, "zone"));
Z_ClearZone(mainzone, zonesize);
}
NOXREF void Cache_Print_Models_And_Totals()
{
NOXREFCHECK;
char buf[50];
cache_system_t *cd;
cache_system_t *sortarray[512];
int32 i = 0;
int32 j = 0;
int32 totalbytes = 0;
FileHandle_t file;
file = FS_Open(mem_dbgfile.string, "a");
if (!file)
return;
Q_memset(sortarray, 0, sizeof(cache_system_t *) * 512);
// pack names into the array.
for (cd = cache_head.next; cd != &cache_head; cd = cd->next)
{
if (Q_strstr(cd->name,".mdl"))
sortarray[i++] = cd;
}
qsort(sortarray, i, sizeof(cache_system_t *), CacheSystemCompare);
FS_FPrintf(file, "\nCACHED MODELS:\n");
// now process the sorted list.
for (j = 0; j < i; j++)
{
FS_FPrintf(file, "\t%16.16s : %s\n", CommatizeNumber(sortarray[j]->size, buf), sortarray[j]->name);
totalbytes += sortarray[j]->size;
}
FS_FPrintf(file,"Total bytes in cache used by models: %s\n", CommatizeNumber(totalbytes, buf));
FS_Close(file);
}
const int MAX_SFX = 1024;
NOXREF void Cache_Print_Sounds_And_Totals()
{
NOXREFCHECK;
char buf[50];
cache_system_t *cd;
cache_system_t *sortarray[1024];
int32 i = 0;
int32 j = 0;
int32 totalsndbytes = 0;
FileHandle_t file;
int subtot = 0;
file = FS_Open(mem_dbgfile.string, "a");
if (!file)
return;
Q_memset(sortarray, 0, sizeof(cache_system_t *) * MAX_SFX);
// pack names into the array.
for (cd = cache_head.next; cd != &cache_head; cd = cd->next)
{
if (Q_strstr(cd->name,".wav"))
sortarray[i++] = cd;
}
qsort(sortarray, i, sizeof(cache_system_t *), CacheSystemCompare);
FS_FPrintf(file, "\nCACHED SOUNDS:\n");
// now process the sorted list. (totals by directory)
for (j = 0; j < i; j++)
{
FS_FPrintf(file, "\t%16.16s : %s\n", CommatizeNumber(sortarray[j]->size, buf), sortarray[j]->name);
totalsndbytes += sortarray[j]->size;
if ((j + 1) == i || ComparePath1(sortarray[j]->name, sortarray[j + 1]->name) == 0)
{
char pathbuf[512];
Sys_SplitPath(sortarray[j]->name, NULL, pathbuf, NULL, NULL);
FS_FPrintf(file, "\tTotal Bytes used in \"%s\": %s\n", pathbuf, CommatizeNumber(totalsndbytes - subtot, buf));
subtot = totalsndbytes;
}
}
FS_FPrintf(file, "Total bytes in cache used by sound: %s\n", CommatizeNumber(totalsndbytes, buf));
FS_Close(file);
}