c0680334c7
Multiple consoles can now be printed/cleared via extra con commands. Fixed the tab-completion alignment, by adding support for \t characters. Changing the download mechanisms. Don't try downloading an ftp:// file. It'll probably crash you for now. Trying to fix load time issues on q3bsps with a lot of curves. Fixed sprites. Added warning prints/spam where the new backend is bypassed, thus marking things that still need to be fixed. QTV proxy fixed to not sit on qw servers unless someone is actually watching. Will ping for status requests still. QTV proxy now supports ipv6. QTV proxy now attempts to use the fte browser plugin. Reworked the browser plugin code, now uses threads instead of ugly hacks. This should make cooperation with other such plugins work. Fixes unresponsiveness of opera, and gives an API that can be used from any other bit of software you want, tbh (read: internet explorer/activex plugins). git-svn-id: https://svn.code.sf.net/p/fteqw/code/branches/wip@3516 fc73d0e0-1445-4013-8a0c-d673dee63da5
2184 lines
41 KiB
C
2184 lines
41 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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.
|
|
|
|
*/
|
|
// Z_zone.c
|
|
|
|
#include "quakedef.h"
|
|
#ifdef _WIN32
|
|
#include "winquake.h"
|
|
#endif
|
|
|
|
#undef malloc
|
|
#undef free
|
|
|
|
#define NOZONE
|
|
#define NOCACHE
|
|
#ifdef _WIN32
|
|
#define NOHIGH
|
|
#endif
|
|
|
|
void Cache_FreeLow (int new_low_hunk);
|
|
void Cache_FreeHigh (int new_high_hunk);
|
|
|
|
#ifdef _DEBUG
|
|
//#define MEMDEBUG 8192 //Debugging adds sentinels (the number is the size - I have the ram)
|
|
#endif
|
|
|
|
//must be multiple of 4.
|
|
#define TEMPDEBUG 4
|
|
#define ZONEDEBUG 4
|
|
#define HUNKDEBUG 4
|
|
#define CACHEDEBUG 4
|
|
|
|
//these need to be defined because it makes some bits of code simpler
|
|
#ifndef HUNKDEBUG
|
|
#define HUNKDEBUG 0
|
|
#endif
|
|
#ifndef ZONEDEBUG
|
|
#define ZONEDEBUG 0
|
|
#endif
|
|
#ifndef TEMPDEBUG
|
|
#define TEMPDEBUG 0
|
|
#endif
|
|
#ifndef CACHEDEBUG
|
|
#define CACHEDEBUG 0
|
|
#endif
|
|
|
|
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0
|
|
qbyte sentinalkey;
|
|
#endif
|
|
|
|
#define TAGLESS 1
|
|
|
|
typedef struct memheader_s {
|
|
int size;
|
|
int tag;
|
|
} memheader_t;
|
|
|
|
typedef struct zone_s {
|
|
struct zone_s *next;
|
|
struct zone_s *pvdn; // down if first, previous if not
|
|
memheader_t mh;
|
|
} zone_t;
|
|
zone_t *zone_head;
|
|
#ifdef MULTITHREAD
|
|
void *zonelock;
|
|
#endif
|
|
|
|
#if 0
|
|
static void Z_DumpTree(void)
|
|
{
|
|
zone_t *zone;
|
|
zone_t *nextlist;
|
|
zone_t *t;
|
|
zone_t *prev;
|
|
|
|
zone = zone_head;
|
|
while(zone)
|
|
{
|
|
nextlist = zone->pvdn;
|
|
|
|
fprintf(stderr, " +-+ %016x (tag: %08x)\n", zone, zone->mh.tag);
|
|
|
|
prev = zone;
|
|
t = zone->next;
|
|
while(t)
|
|
{
|
|
if (t->pvdn != prev)
|
|
fprintf(stderr, "Previous link failure\n");
|
|
|
|
prev = t;
|
|
t = t->next;
|
|
}
|
|
|
|
while(zone)
|
|
{
|
|
fprintf(stderr, " +-- %016x\n", zone);
|
|
|
|
zone = zone->next;
|
|
}
|
|
|
|
zone = nextlist;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void *VARGS Z_TagMalloc(int size, int tag)
|
|
{
|
|
zone_t *zone;
|
|
|
|
zone = (zone_t *)malloc(size + sizeof(zone_t));
|
|
if (!zone)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
Q_memset(zone, 0, size + sizeof(zone_t));
|
|
zone->mh.tag = tag;
|
|
zone->mh.size = size;
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
|
|
#if 0
|
|
fprintf(stderr, "Before alloc:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
if (zone_head == NULL)
|
|
zone_head = zone;
|
|
else
|
|
{
|
|
zone_t *s = zone_head;
|
|
|
|
while (s && s->mh.tag != tag)
|
|
s = s->pvdn;
|
|
|
|
if (s)
|
|
{ // tag match
|
|
zone->next = s->next;
|
|
if (s->next)
|
|
s->next->pvdn = zone;
|
|
zone->pvdn = s;
|
|
s->next = zone;
|
|
}
|
|
else
|
|
{
|
|
zone->pvdn = zone_head;
|
|
// if (s->next)
|
|
// s->next->pvdn = zone;
|
|
zone_head = zone;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
fprintf(stderr, "After alloc:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
return (void *)(zone + 1);
|
|
}
|
|
|
|
void *ZF_Malloc(int size)
|
|
{
|
|
return calloc(size, 1);
|
|
}
|
|
|
|
void *Z_Malloc(int size)
|
|
{
|
|
void *mem = ZF_Malloc(size);
|
|
if (!mem)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
|
|
void VARGS Z_TagFree(void *mem)
|
|
{
|
|
zone_t *zone = ((zone_t *)mem) - 1;
|
|
|
|
#if 0
|
|
fprintf(stderr, "Before free:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
if (zone->next)
|
|
zone->next->pvdn = zone->pvdn;
|
|
|
|
if (zone->pvdn && zone->pvdn->mh.tag == zone->mh.tag)
|
|
zone->pvdn->next = zone->next;
|
|
else
|
|
{ // zone is first entry in a tag list
|
|
zone_t *s = zone_head;
|
|
|
|
if (zone != s)
|
|
{ // traverse and update down list
|
|
while (s->pvdn != zone)
|
|
s = s->pvdn;
|
|
|
|
if (zone->next)
|
|
s->pvdn = zone->next;
|
|
else
|
|
s->pvdn = zone->pvdn;
|
|
}
|
|
}
|
|
|
|
if (zone == zone_head)
|
|
{ // freeing head node so update head pointer
|
|
if (zone->next) // move to next, pvdn should be maintained properly
|
|
zone_head = zone->next;
|
|
else // no more entries with this tag so move head down
|
|
zone_head = zone->pvdn;
|
|
}
|
|
|
|
#if 0
|
|
fprintf(stderr, "After free:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
free(zone);
|
|
}
|
|
|
|
void VARGS Z_Free(void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
void VARGS Z_FreeTags(int tag)
|
|
{
|
|
zone_t *taglist;
|
|
zone_t *t;
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
if (zone_head)
|
|
{
|
|
if (zone_head->mh.tag == tag)
|
|
{ // just pull off the head
|
|
taglist = zone_head;
|
|
zone_head = zone_head->pvdn;
|
|
}
|
|
else
|
|
{ // search for tag list and isolate it
|
|
zone_t *z;
|
|
z = zone_head;
|
|
while (z->pvdn != NULL && z->pvdn->mh.tag != tag)
|
|
z = z->pvdn;
|
|
|
|
if (z->pvdn == NULL)
|
|
taglist = NULL;
|
|
else
|
|
{
|
|
taglist = z->pvdn;
|
|
z->pvdn = z->pvdn->pvdn;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
taglist = NULL;
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
// actually free list
|
|
while (taglist != NULL)
|
|
{
|
|
t = taglist->next;
|
|
free(taglist);
|
|
taglist = t;
|
|
}
|
|
}
|
|
|
|
/*
|
|
void *Z_Realloc(void *data, int newsize)
|
|
{
|
|
memheader_t *memref;
|
|
|
|
if (!data)
|
|
return Z_Malloc(newsize);
|
|
|
|
memref = ((memheader_t *)data) - 1;
|
|
|
|
if (memref[0].tag != TAGLESS)
|
|
{ // allocate a new block and copy since we need to maintain the lists
|
|
zone_t *zone = ((zone_t *)data) - 1;
|
|
int size = zone->mh.size;
|
|
if (size != newsize)
|
|
{
|
|
void *newdata = Z_Malloc(newsize);
|
|
|
|
if (size > newsize)
|
|
size = newsize;
|
|
memcpy(newdata, data, size);
|
|
|
|
Z_Free(data);
|
|
data = newdata;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int oldsize = memref[0].size;
|
|
memref = realloc(memref, newsize + sizeof(memheader_t));
|
|
memref->size = newsize;
|
|
if (newsize > oldsize)
|
|
memset((qbyte *)memref + sizeof(memheader_t) + oldsize, 0, newsize - oldsize);
|
|
data = ((memheader_t *)memref) + 1;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
*/
|
|
|
|
void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc.
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures.
|
|
{
|
|
void *mem = BZF_Malloc(size);
|
|
if (!mem)
|
|
Sys_Error("BZ_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
|
|
void *BZF_Realloc(void *data, int newsize)
|
|
{
|
|
return realloc(data, newsize);
|
|
}
|
|
|
|
void *BZ_Realloc(void *data, int newsize)
|
|
{
|
|
void *mem = BZF_Realloc(data, newsize);
|
|
|
|
if (!mem)
|
|
Sys_Error("BZ_Realloc: Failed on reallocation of %i bytes", newsize);
|
|
|
|
return mem;
|
|
}
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
|
|
#if 0 //NOZONE //zone memory is for small dynamic things.
|
|
/*
|
|
void *Z_TagMalloc(int size, int tag)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void *Z_Malloc(int size)
|
|
{
|
|
qbyte *buf;
|
|
buf = Z_TagMalloc(size, 1);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
Q_memset(buf, 0, size);
|
|
return buf;
|
|
}
|
|
|
|
void Z_Free (void *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
void Z_FreeTags (void *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define ZONEID 0x1d4a11
|
|
|
|
#define ZONESENTINAL 0xdeadbeaf
|
|
|
|
typedef struct zone_s {
|
|
// int sentinal1;
|
|
|
|
struct zone_s *next;
|
|
struct zone_s *prev;
|
|
int size;
|
|
int tag;
|
|
|
|
// int sentinal2;
|
|
} zone_t;
|
|
zone_t *zone_head;
|
|
/*
|
|
void Z_CheckSentinals(void)
|
|
{
|
|
zone_t *zone;
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
if (zone->sentinal1 != ZONESENTINAL || zone->sentinal2 != ZONESENTINAL)
|
|
Sys_Error("Memory sentinal destroyed\n");
|
|
}
|
|
}*/
|
|
|
|
|
|
void VARGS Z_Free (void *c)
|
|
{
|
|
zone_t *nz;
|
|
nz = ((zone_t *)((char*)c-ZONEDEBUG))-1;
|
|
|
|
// Z_CheckSentinals();
|
|
|
|
#if ZONEDEBUG>0
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(nz+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += nz->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if (nz->sentinal1 != ZONESENTINAL || nz->sentinal2 != ZONESENTINAL)
|
|
// Sys_Error("zone was not z_malloced\n");
|
|
|
|
if (nz->next)
|
|
nz->next->prev = nz->prev;
|
|
if (nz->prev)
|
|
nz->prev->next = nz->next;
|
|
|
|
if (nz == zone_head)
|
|
zone_head = nz->next;
|
|
|
|
// Con_Printf("Free of %i bytes\n", nz->size);
|
|
|
|
free(nz);
|
|
}
|
|
|
|
void BZ_CheckSentinals(void *c)
|
|
{
|
|
#if ZONEDEBUG>0
|
|
zone_t *nz;
|
|
nz = ((zone_t *)((char*)c-ZONEDEBUG))-1;
|
|
|
|
// Z_CheckSentinals();
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(nz+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += nz->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
//revive this function each time you get memory corruption and need to trace it.
|
|
void BZ_CheckAllSentinals(void)
|
|
{
|
|
zone_t *zone;
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(zone+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", zone->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += zone->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", zone->size);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void VARGS Z_FreeTags(int tag)
|
|
{
|
|
zone_t *zone, *next;
|
|
for(zone = zone_head; zone; zone=next)
|
|
{
|
|
next = zone->next;
|
|
if (zone->tag == tag)
|
|
Z_Free((char*)(zone+1)+ZONEDEBUG);
|
|
}
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *Z_BaseTagMalloc (int size, int tag, qboolean clear, char *descrip, ...)
|
|
#else
|
|
void *Z_BaseTagMalloc (int size, int tag, qboolean clear)
|
|
#endif
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
va_list argptr;
|
|
char buffer[512];
|
|
#endif
|
|
void *buf;
|
|
zone_t *nt;
|
|
|
|
// Z_CheckSentinals();
|
|
//Con_Printf("Malloc of %i bytes\n", size);
|
|
//if (size>20)
|
|
//Con_Printf("Big malloc\n");
|
|
if (size <= 0)
|
|
Sys_Error ("Z_Malloc: size %i", size);
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
|
|
va_start (argptr, descrip);
|
|
vsprintf (buffer, descrip,argptr);
|
|
va_end (argptr);
|
|
|
|
nt = (zone_t*)malloc(size + sizeof(zone_t)+strlen(buffer)+1 + ZONEDEBUG*2);
|
|
#else
|
|
nt = (zone_t*)malloc(size + sizeof(zone_t)+ ZONEDEBUG*2);
|
|
#endif
|
|
if (!nt)
|
|
Sys_Error("Z_BaseTagMalloc: failed on allocation of %i bytes", size);
|
|
nt->next = zone_head;
|
|
nt->prev = NULL;
|
|
nt->size = size;
|
|
nt->tag = tag;
|
|
// nt->sentinal1 = ZONESENTINAL;
|
|
// nt->sentinal2 = ZONESENTINAL;
|
|
if (zone_head)
|
|
zone_head->prev = nt;
|
|
zone_head = nt;
|
|
buf = (void *)(nt+1);
|
|
|
|
#if ZONEDEBUG > 0
|
|
memset(buf, sentinalkey, ZONEDEBUG);
|
|
buf = (char*)buf+ZONEDEBUG;
|
|
memset((char*)buf+size, sentinalkey, ZONEDEBUG);
|
|
#endif
|
|
|
|
if (clear)
|
|
Q_memset(buf, 0, size);
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
strcpy((char *)(nt+1) + nt->size + ZONEDEBUG*2, buffer);
|
|
#endif
|
|
return buf;
|
|
}
|
|
void *VARGS Z_TagMalloc (int size, int tag)
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
return Z_BaseTagMalloc(size, tag, true, "");
|
|
#else
|
|
return Z_BaseTagMalloc(size, tag, true);
|
|
#endif
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *Z_MallocNamed (int size, char *file, int lineno)
|
|
{
|
|
qbyte *buf;
|
|
buf = Z_BaseTagMalloc(size, 1, true, "%s: %i", file, lineno);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
return buf;
|
|
}
|
|
#else
|
|
void *Z_Malloc(int size)
|
|
{
|
|
qbyte *buf;
|
|
buf = (qbyte*)Z_TagMalloc(size, 1);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
return buf;
|
|
}
|
|
|
|
|
|
|
|
void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures.
|
|
{
|
|
void *data = Z_BaseTagMalloc(size, 1, true);
|
|
if (!data)
|
|
Sys_Error("BZ_Malloc failed on %i bytes", size);
|
|
|
|
return data;
|
|
}
|
|
#endif
|
|
void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc.
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
return Z_BaseTagMalloc(size, 1, false, "");
|
|
#else
|
|
return Z_BaseTagMalloc(size, 1, false);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *BZ_NamedRealloc(void *data, int newsize, char *file, int lineno)
|
|
#else
|
|
void *BZ_Realloc(void *data, int newsize)
|
|
#endif
|
|
{
|
|
zone_t *oldzone;
|
|
void *newdata;
|
|
#ifdef NAMEDMALLOCS
|
|
if (!data)
|
|
return Z_MallocNamed(newsize, file, lineno);
|
|
oldzone = ((zone_t *)((char *)data-ZONEDEBUG))-1;
|
|
if (oldzone->size == newsize)
|
|
return data;
|
|
newdata = Z_MallocNamed(newsize, file, lineno);
|
|
#else
|
|
if (!data)
|
|
return Z_Malloc(newsize);
|
|
oldzone = ((zone_t *)((char *)data-ZONEDEBUG))-1;
|
|
if (oldzone->size == newsize)
|
|
return data;
|
|
newdata = BZ_Malloc(newsize);
|
|
#endif
|
|
if (oldzone->size < newsize)
|
|
{
|
|
memcpy(newdata, data, oldzone->size);
|
|
memset((char *)newdata + oldzone->size, 0, newsize - oldzone->size);
|
|
}
|
|
else
|
|
memcpy(newdata, data, newsize);
|
|
BZ_Free(data);
|
|
|
|
return newdata;
|
|
}
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
Z_Free(data);
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
|
|
// Zone_Groups_f: prints out zones sorting into groups
|
|
// and tracking number of allocs and total group size as
|
|
// well as a group delta against the last Zone_Group_f call
|
|
#define ZONEGROUPS 64
|
|
void Zone_Groups_f(void)
|
|
{
|
|
zone_t *zone;
|
|
char *zonename[ZONEGROUPS];
|
|
int zonesize[ZONEGROUPS];
|
|
int zoneallocs[ZONEGROUPS];
|
|
static int zonelast[ZONEGROUPS];
|
|
int groups, i;
|
|
int allocated = 0;
|
|
|
|
// initialization
|
|
for (groups = 0; groups < ZONEGROUPS; groups++)
|
|
zonename[groups] = NULL;
|
|
|
|
groups = 0;
|
|
i = 0;
|
|
|
|
for (zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
char *czg = (char *)(zone+1) + zone->size+ZONEDEBUG*2;
|
|
// check against existing tracked groups
|
|
for (i = 0; i < groups; i++)
|
|
{
|
|
if (!strcmp(czg, zonename[i]))
|
|
{
|
|
// update stats for tracked group
|
|
zonesize[i] += zone->size;
|
|
zoneallocs[i]++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (groups == i) // no existing group found
|
|
{
|
|
// track new zone group
|
|
zonename[groups] = czg;
|
|
zonesize[groups] = zone->size;
|
|
zoneallocs[groups] = 1;
|
|
groups++;
|
|
|
|
// max groups bounds check
|
|
if (groups >= ZONEGROUPS)
|
|
{
|
|
groups = ZONEGROUPS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// print group statistics
|
|
for (i = 0; i < groups; i++)
|
|
{
|
|
allocated += zonesize[i];
|
|
Con_Printf("%s, size: %i, allocs: %i, delta: %i\n", zonename[i], zonesize[i], zoneallocs[i], zonesize[i] - zonelast[i]);
|
|
zonelast[i] = zonesize[i]; // update delta tracking for next call
|
|
}
|
|
|
|
Con_Printf("Total: %i bytes\n", allocated);
|
|
}
|
|
#endif
|
|
|
|
void Zone_Print_f(void)
|
|
{
|
|
int overhead=0;
|
|
int allocated = 0;
|
|
int blocks = 0;
|
|
int futurehide = false;
|
|
int minsize = 0;
|
|
zone_t *zone;
|
|
#if ZONEDEBUG > 0
|
|
#ifdef NAMEDMALLOCS
|
|
int i;
|
|
qbyte *sent;
|
|
#endif
|
|
qboolean testsent = false;
|
|
if (*Cmd_Argv(1) == 't')
|
|
{
|
|
Con_Printf("Testing Zone sentinels\n");
|
|
testsent = true;
|
|
}
|
|
else
|
|
#endif
|
|
if (*Cmd_Argv(1) == 'h')
|
|
futurehide = true;
|
|
else if (*Cmd_Argv(1))
|
|
minsize = atoi(Cmd_Argv(1));
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
blocks++;
|
|
allocated+= zone->size;
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
if (*((char *)(zone+1)+zone->size+ZONEDEBUG*2)!='#')
|
|
{
|
|
#if ZONEDEBUG > 0
|
|
if (testsent)
|
|
{
|
|
sent = (qbyte *)(zone+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (sent[i] != sentinalkey)
|
|
{
|
|
Con_Printf(CON_ERROR "%i %i-%s\n", zone->size, i, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
break;
|
|
}
|
|
}
|
|
sent += zone->size+ZONEDEBUG;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (sent[i] != sentinalkey)
|
|
{
|
|
Con_Printf(CON_ERROR "%i %i-%s\n", zone->size, i, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (zone->size >= minsize)
|
|
#endif
|
|
Con_Printf("%i-%s\n", zone->size, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
if (futurehide)
|
|
*((char *)(zone+1)+zone->size+ZONEDEBUG*2) = '#';
|
|
|
|
// Sleep(10);
|
|
}
|
|
overhead += sizeof(zone_t)+ZONEDEBUG*2 + strlen((char *)(zone+1) + zone->size+ZONEDEBUG*2) +1;
|
|
#else
|
|
Con_Printf("%i-%i ", zone->size, zone->tag);
|
|
overhead += sizeof(zone_t)+ZONEDEBUG*2;
|
|
#endif
|
|
}
|
|
Con_Printf(CON_NOTICE "Zone:%i bytes in %i blocks\n", allocated, blocks);
|
|
Con_Printf(CON_NOTICE "Overhead %i bytes\n", overhead);
|
|
}
|
|
|
|
#elif 0//#else
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//dmw was 0x50000 19/12/02 - playing with dynamic sound system.
|
|
//was 0x80000 15/01/03 - playing with genuine pk3 files
|
|
#define DYNAMIC_SIZE 0x100000
|
|
|
|
#define ZONEID 0x1d4a11
|
|
#define MINFRAGMENT 64
|
|
|
|
typedef struct memblock_s
|
|
{
|
|
int size; // including the header and possibly tiny fragments
|
|
int tag; // a tag of 0 is a free block
|
|
int id; // should be ZONEID
|
|
struct memblock_s *next, *prev;
|
|
int pad; // pad to 64 bit boundary
|
|
} memblock_t;
|
|
|
|
typedef struct
|
|
{
|
|
int size; // total bytes malloced, including header
|
|
memblock_t blocklist; // start / end cap for linked list
|
|
memblock_t *rover;
|
|
} memzone_t;
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
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.
|
|
==============================================================================
|
|
*/
|
|
|
|
memzone_t *mainzone;
|
|
|
|
void Z_ClearZone (memzone_t *zone, int size);
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_ClearZone
|
|
========================
|
|
*/
|
|
void Z_ClearZone (memzone_t *zone, int size)
|
|
{
|
|
memblock_t *block;
|
|
|
|
// set the entire zone to one free block
|
|
|
|
zone->blocklist.next = zone->blocklist.prev = block =
|
|
(memblock_t *)( (qbyte *)zone + sizeof(memzone_t) );
|
|
zone->blocklist.tag = 1; // in use block
|
|
zone->blocklist.id = 0;
|
|
zone->blocklist.size = 0;
|
|
zone->rover = block;
|
|
|
|
block->prev = block->next = &zone->blocklist;
|
|
block->tag = 0; // free block
|
|
block->id = ZONEID;
|
|
block->size = size - sizeof(memzone_t);
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Free
|
|
========================
|
|
*/
|
|
void Z_Free (void *ptr)
|
|
{
|
|
memblock_t *block, *other;
|
|
|
|
if (!ptr)
|
|
Sys_Error ("Z_Free: NULL pointer");
|
|
|
|
block = (memblock_t *) ( (qbyte *)ptr - sizeof(memblock_t));
|
|
if (block->id != ZONEID)
|
|
Sys_Error ("Z_Free: freed a pointer without ZONEID");
|
|
if (block->tag == 0)
|
|
Sys_Error ("Z_Free: freed a freed pointer");
|
|
|
|
block->tag = 0; // mark as free
|
|
|
|
other = block->prev;
|
|
if (!other->tag)
|
|
{ // merge with previous free block
|
|
other->size += block->size;
|
|
other->next = block->next;
|
|
other->next->prev = other;
|
|
if (block == mainzone->rover)
|
|
mainzone->rover = other;
|
|
block = other;
|
|
}
|
|
|
|
other = block->next;
|
|
if (!other->tag)
|
|
{ // merge the next free block onto the end
|
|
block->size += other->size;
|
|
block->next = other->next;
|
|
block->next->prev = block;
|
|
if (other == mainzone->rover)
|
|
mainzone->rover = block;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Malloc
|
|
========================
|
|
*/
|
|
#undef Z_Malloc
|
|
void *Z_Malloc (int size)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
|
|
Q_memset (buf, 0, size);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void *Z_MallocNamed (int size, char *name)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: %s failed on allocation of %i bytes", name, size);
|
|
// Sys_DebugLog("zmalloc.log", "%s allocates %i bytes\n", name, size);
|
|
Q_memset (buf, 0, size);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void *Z_MallocNamed2 (int size, char *name, int line)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: %s %i failed on allocation of %i bytes", name, line, size);
|
|
// Sys_DebugLog("zmalloc.log", "%s %i allocates %i bytes\n", name, line, 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)
|
|
Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
|
|
|
|
//
|
|
// scan through the block list looking for the first free block
|
|
// of sufficient size
|
|
//
|
|
size += sizeof(memblock_t); // account for size of block header
|
|
size += 4; // space for memory trash tester
|
|
size = (size + 7) & ~7; // align to 8-qbyte boundary
|
|
|
|
base = rover = mainzone->rover;
|
|
start = base->prev;
|
|
|
|
do
|
|
{
|
|
if (rover == start) // scaned all the way around the list
|
|
return NULL;
|
|
if (rover->tag)
|
|
base = rover = rover->next;
|
|
else
|
|
rover = rover->next;
|
|
} while (base->tag || base->size < size);
|
|
|
|
//
|
|
// found a block big enough
|
|
//
|
|
extra = base->size - size;
|
|
if (extra > MINFRAGMENT)
|
|
{ // there will be a free fragment after the allocated block
|
|
newz = (memblock_t *) ((qbyte *)base + size );
|
|
newz->size = extra;
|
|
newz->tag = 0; // free block
|
|
newz->prev = base;
|
|
newz->id = ZONEID;
|
|
newz->next = base->next;
|
|
newz->next->prev = newz;
|
|
base->next = newz;
|
|
base->size = size;
|
|
}
|
|
|
|
base->tag = tag; // no longer a free block
|
|
|
|
mainzone->rover = base->next; // next allocation will start looking here
|
|
|
|
base->id = ZONEID;
|
|
|
|
// marker for memory trash testing
|
|
*(int *)((qbyte *)base + base->size - 4) = ZONEID;
|
|
|
|
return (void *) ((qbyte *)base + sizeof(memblock_t));
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Print
|
|
========================
|
|
*/
|
|
void Z_Print (memzone_t *zone)
|
|
{
|
|
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 ( (qbyte *)block + block->size != (qbyte *)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 *BZ_Malloc(int size)
|
|
{
|
|
void *data;
|
|
data = malloc(size);
|
|
memset(data, 0, size);
|
|
return data;
|
|
}
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
#define HUNK_SENTINAL 0x1df001ed
|
|
|
|
typedef struct
|
|
{
|
|
int sentinal;
|
|
int size; // including sizeof(hunk_t), -1 = not allocated
|
|
char name[8];
|
|
} hunk_t;
|
|
|
|
qbyte *hunk_base;
|
|
int hunk_size;
|
|
|
|
int hunk_low_used;
|
|
int hunk_high_used;
|
|
|
|
qboolean hunk_tempactive;
|
|
int hunk_tempmark;
|
|
|
|
void R_FreeTextures (void);
|
|
|
|
/*
|
|
==============
|
|
Hunk_Check
|
|
|
|
Run consistancy and sentinal trahing checks
|
|
==============
|
|
*/
|
|
void Hunk_Check (void)
|
|
{
|
|
hunk_t *h;
|
|
|
|
for (h = (hunk_t *)hunk_base ; (qbyte *)h != hunk_base + hunk_low_used ; )
|
|
{
|
|
if (h->sentinal != HUNK_SENTINAL)
|
|
Sys_Error ("Hunk_Check: trahsed sentinal");
|
|
if (h->size < 16+HUNKDEBUG*2 || h->size + (qbyte *)h - hunk_base > hunk_size)
|
|
Sys_Error ("Hunk_Check: bad size");
|
|
#if HUNKDEBUG > 0
|
|
{
|
|
qbyte *present;
|
|
qbyte *postsent;
|
|
int i;
|
|
present = (qbyte *)(h+1);
|
|
postsent = (qbyte *)h + h->size-HUNKDEBUG;
|
|
for (i = 0; i < HUNKDEBUG; i++)
|
|
{
|
|
if (present[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
if (postsent[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
}
|
|
}
|
|
#endif
|
|
h = (hunk_t *)((qbyte *)h+h->size);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Hunk_Print
|
|
|
|
If "all" is specified, every single allocation is printed.
|
|
Otherwise, allocations with the same name will be totaled up before printing.
|
|
==============
|
|
*/
|
|
void Hunk_Print (qboolean all)
|
|
{
|
|
hunk_t *h, *next, *endlow, *starthigh, *endhigh;
|
|
int count, sum;
|
|
int totalblocks;
|
|
char name[9];
|
|
|
|
name[8] = 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 (1)
|
|
{
|
|
//
|
|
// 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 (" :%8i USED\n", hunk_low_used + hunk_high_used);
|
|
Con_Printf ("-------------------------\n");
|
|
h = starthigh;
|
|
}
|
|
|
|
//
|
|
// if totally done, break
|
|
//
|
|
if ( h == endhigh )
|
|
break;
|
|
|
|
//
|
|
// run consistancy checks
|
|
//
|
|
if (h->sentinal != HUNK_SENTINAL)
|
|
Sys_Error ("Hunk_Check: trahsed sentinal");
|
|
if (h->size < 16 || h->size + (qbyte *)h - hunk_base > hunk_size)
|
|
Sys_Error ("Hunk_Check: bad size");
|
|
#if HUNKDEBUG > 0
|
|
{
|
|
qbyte *present;
|
|
qbyte *postsent;
|
|
int i;
|
|
present = (qbyte *)(h+1);
|
|
postsent = (qbyte *)h + h->size-HUNKDEBUG;
|
|
for (i = 0; i < HUNKDEBUG; i++)
|
|
{
|
|
if (present[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
if (postsent[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
}
|
|
}
|
|
#endif
|
|
next = (hunk_t *)((qbyte *)h+h->size);
|
|
count++;
|
|
totalblocks++;
|
|
sum += h->size;
|
|
|
|
//
|
|
// print the single block
|
|
//
|
|
memcpy (name, h->name, 8);
|
|
if (all)
|
|
Con_Printf ("%8p :%8i %8s\n",h, h->size, name);
|
|
|
|
//
|
|
// print the total
|
|
//
|
|
if (next == endlow || next == endhigh ||
|
|
strncmp (h->name, next->name, 8) )
|
|
{
|
|
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);
|
|
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Hunk_AllocName
|
|
===================
|
|
*/
|
|
void *Hunk_AllocName (int size, char *name)
|
|
{
|
|
#ifdef NOHIGH
|
|
int roundup;
|
|
int roundupold;
|
|
#endif
|
|
hunk_t *h;
|
|
|
|
#ifdef PARANOID
|
|
Hunk_Check ();
|
|
#endif
|
|
|
|
if (size < 0)
|
|
Sys_Error ("Hunk_Alloc: bad size: %i", size);
|
|
|
|
size = sizeof(hunk_t) + HUNKDEBUG*2 + ((size+15)&~15);
|
|
|
|
#ifndef _WIN32
|
|
if (hunk_size - hunk_low_used - hunk_high_used < size)
|
|
// Sys_Error ("Hunk_Alloc: failed on %i bytes",size);
|
|
#ifdef _WIN32
|
|
Sys_Error ("Not enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize 16000\" on the QuakeWorld command line.", name);
|
|
#else
|
|
Sys_Error ("Not enough RAM allocated. Try starting using \"-mem 16\" on the QuakeWorld command line.");
|
|
#endif
|
|
#endif
|
|
|
|
h = (hunk_t *)(hunk_base + hunk_low_used);
|
|
|
|
#ifdef NOHIGH
|
|
|
|
roundupold = hunk_low_used+sizeof(hunk_t);
|
|
roundupold += 1024*128;
|
|
roundupold &= ~(1024*128 - 1);
|
|
|
|
roundup = hunk_low_used+size+sizeof(hunk_t);
|
|
roundup += 1024*128;
|
|
roundup &= ~(1024*128 - 1);
|
|
|
|
|
|
if (!hunk_low_used || roundup != roundupold)
|
|
if (!VirtualAlloc (hunk_base, roundup, MEM_COMMIT, PAGE_READWRITE))
|
|
{
|
|
char *buf;
|
|
Hunk_Print(true);
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL);
|
|
Sys_Error ("VirtualCommit failed\nNot enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize %i\" on the QuakeWorld command line.", name, roundupold/512);
|
|
}
|
|
#endif
|
|
|
|
hunk_low_used += size;
|
|
|
|
Cache_FreeLow (hunk_low_used);
|
|
|
|
memset (h, 0, size-HUNKDEBUG);
|
|
|
|
#if HUNKDEBUG>0
|
|
memset ((h+1), sentinalkey, HUNKDEBUG);
|
|
memset ((qbyte *)h+size-HUNKDEBUG, sentinalkey, HUNKDEBUG);
|
|
#endif
|
|
|
|
h->size = size;
|
|
h->sentinal = HUNK_SENTINAL;
|
|
Q_strncpyz (h->name, COM_SkipPath(name), sizeof(h->name));
|
|
|
|
return (void *)((char *)(h+1)+HUNKDEBUG);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Hunk_Alloc
|
|
===================
|
|
*/
|
|
void *Hunk_Alloc (int size)
|
|
{
|
|
return Hunk_AllocName (size, "unknown");
|
|
}
|
|
|
|
int Hunk_LowMark (void)
|
|
{
|
|
return hunk_low_used;
|
|
}
|
|
|
|
int Hunk_LowMemAvailable(void)
|
|
{
|
|
return hunk_size - hunk_low_used - hunk_high_used;
|
|
}
|
|
|
|
void Hunk_FreeToLowMark (int mark)
|
|
{
|
|
if (mark < 0 || mark > hunk_low_used)
|
|
Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
|
|
memset (hunk_base + mark, 0, hunk_low_used - mark);
|
|
hunk_low_used = mark;
|
|
|
|
#ifdef NOHIGH
|
|
if (!VirtualAlloc (hunk_base, hunk_low_used+sizeof(hunk_t), MEM_COMMIT, PAGE_READWRITE))
|
|
{
|
|
char *buf;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL);
|
|
Sys_Error ("VirtualAlloc commit failed.\n%s", buf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int Hunk_HighMark (void)
|
|
{
|
|
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 ("Hunk_FreeToHighMark: bad mark %i", mark);
|
|
memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
|
|
hunk_high_used = mark;
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
Hunk_HighAllocName
|
|
===================
|
|
*/
|
|
void *Hunk_HighAllocName (int size, char *name)
|
|
{
|
|
#ifdef NOHIGH
|
|
Sys_Error("High hunk was disabled");
|
|
return NULL;
|
|
#else
|
|
|
|
hunk_t *h;
|
|
|
|
if (size < 0)
|
|
Sys_Error ("Hunk_HighAllocName: bad size: %i", size);
|
|
|
|
if (hunk_tempactive)
|
|
{
|
|
Hunk_FreeToHighMark (hunk_tempmark);
|
|
hunk_tempactive = false;
|
|
}
|
|
|
|
#ifdef PARANOID
|
|
Hunk_Check ();
|
|
#endif
|
|
|
|
size = sizeof(hunk_t) + ((size+15)&~15);
|
|
|
|
if (hunk_size - hunk_low_used - hunk_high_used < size)
|
|
{
|
|
Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size);
|
|
return NULL;
|
|
}
|
|
|
|
hunk_high_used += size;
|
|
Cache_FreeHigh (hunk_high_used);
|
|
|
|
h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
|
|
|
|
memset (h, 0, size);
|
|
h->size = size;
|
|
h->sentinal = HUNK_SENTINAL;
|
|
Q_strncpyz (h->name, name, sizeof(h->name));
|
|
|
|
return (void *)(h+1);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Hunk_TempAlloc
|
|
|
|
Return space from the top of the hunk
|
|
clears old temp.
|
|
=================
|
|
*/
|
|
#ifdef NOHIGH
|
|
typedef struct hnktemps_s {
|
|
struct hnktemps_s *next;
|
|
#if TEMPDEBUG>0
|
|
int len;
|
|
#endif
|
|
} hnktemps_t;
|
|
hnktemps_t *hnktemps;
|
|
|
|
void Hunk_TempFree(void)
|
|
{
|
|
hnktemps_t *nt;
|
|
|
|
while (hnktemps)
|
|
{
|
|
#if TEMPDEBUG>0
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(hnktemps+1);
|
|
for (i = 0; i < TEMPDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
*(int*)0 = -3; //force a crash... this'll get our attention.
|
|
}
|
|
buf+=TEMPDEBUG;
|
|
//app data
|
|
buf += hnktemps->len;
|
|
for (i = 0; i < TEMPDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
*(int*)0 = -3; //force a crash... this'll get our attention.
|
|
}
|
|
#endif
|
|
|
|
nt = hnktemps->next;
|
|
|
|
free(hnktemps);
|
|
hnktemps = nt;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//allocates without clearing previous temp.
|
|
//safer than my hack that fuh moaned about...
|
|
void *Hunk_TempAllocMore (int size)
|
|
{
|
|
void *buf;
|
|
#ifdef NOHIGH
|
|
#if TEMPDEBUG>0
|
|
hnktemps_t *nt;
|
|
nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t) + TEMPDEBUG*2);
|
|
nt->next = hnktemps;
|
|
nt->len = size;
|
|
hnktemps = nt;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, sentinalkey, TEMPDEBUG);
|
|
buf = (char *)buf + TEMPDEBUG;
|
|
memset(buf, 0, size);
|
|
memset((char *)buf + size, sentinalkey, TEMPDEBUG);
|
|
return buf;
|
|
#else
|
|
hnktemps_t *nt;
|
|
nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t));
|
|
nt->next = hnktemps;
|
|
hnktemps = nt;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, 0, size);
|
|
return buf;
|
|
#endif
|
|
#else
|
|
|
|
if (!hunk_tempactive)
|
|
return Hunk_TempAlloc(size);
|
|
|
|
size = (size+15)&~15;
|
|
|
|
hunk_tempactive = false; //so it doesn't wipe old temp.
|
|
buf = Hunk_HighAllocName (size, "mtmp");
|
|
hunk_tempactive = true;
|
|
|
|
return buf;
|
|
#endif
|
|
}
|
|
|
|
|
|
void *Hunk_TempAlloc (int size)
|
|
{
|
|
#ifdef NOHIGH
|
|
|
|
Hunk_TempFree();
|
|
|
|
return Hunk_TempAllocMore(size);
|
|
#else
|
|
void *buf;
|
|
|
|
size = (size+15)&~15;
|
|
|
|
if (hunk_tempactive)
|
|
{
|
|
Hunk_FreeToHighMark (hunk_tempmark);
|
|
hunk_tempactive = false;
|
|
}
|
|
|
|
hunk_tempmark = Hunk_HighMark ();
|
|
|
|
buf = Hunk_HighAllocName (size, "temp");
|
|
|
|
hunk_tempactive = true;
|
|
|
|
return buf;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
CACHE MEMORY
|
|
|
|
===============================================================================
|
|
*/
|
|
#ifdef NOCACHE
|
|
|
|
|
|
typedef struct cache_system_s {
|
|
cache_user_t *user;
|
|
struct cache_system_s *next;
|
|
struct cache_system_s *prev;
|
|
int size;
|
|
char name[16];
|
|
} cache_system_t;
|
|
cache_system_t *cache_head;
|
|
|
|
void Cache_Free (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
if (c->data == NULL)
|
|
{
|
|
cache_head = NULL; //this is evil and should never happen
|
|
Sys_Error("Cache was already free\n");
|
|
return;
|
|
}
|
|
cs = ((cache_system_t *)c->data)-1;
|
|
cs = (cache_system_t*)((char*)cs - CACHEDEBUG);
|
|
|
|
cs->user->data = NULL;
|
|
|
|
#if CACHEDEBUG>0
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(cs+1);
|
|
for (i = 0; i < CACHEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("Cache memory corrupted (%i? bytes)", cs->size);
|
|
}
|
|
buf+=CACHEDEBUG;
|
|
//app data
|
|
buf += cs->size;
|
|
for (i = 0; i < CACHEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("Cache memory corrupted (%i? bytes)", cs->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
if (cs->next)
|
|
cs->next->prev = cs->prev;
|
|
if (cs->prev)
|
|
cs->prev->next = cs->next;
|
|
|
|
if (cs == cache_head)
|
|
cache_head = cs->next;
|
|
|
|
BZ_Free(cs);
|
|
}
|
|
|
|
void *Cache_Check(cache_user_t *c)
|
|
{
|
|
if (!c->data)
|
|
return NULL;
|
|
|
|
return c->data;
|
|
}
|
|
|
|
void Cache_Flush(void)
|
|
{
|
|
while(cache_head)
|
|
{
|
|
Cache_Free(cache_head->user);
|
|
}
|
|
}
|
|
|
|
void *Cache_Alloc (cache_user_t *c, int size, char *name)
|
|
{
|
|
void *buf;
|
|
cache_system_t *nt;
|
|
|
|
if (c->data)
|
|
Sys_Error ("Cache_Alloc: already allocated");
|
|
|
|
if (size <= 0)
|
|
Sys_Error ("Cache_Alloc: size %i", size);
|
|
|
|
// size = (size + 15) & ~15;
|
|
|
|
nt = (cache_system_t*)BZ_Malloc(size + sizeof(cache_system_t) + CACHEDEBUG*2);
|
|
if (!nt)
|
|
Sys_Error("Cache_Alloc: failed on allocation of %i bytes", size);
|
|
nt->next = cache_head;
|
|
nt->prev = NULL;
|
|
nt->user = c;
|
|
nt->size = size;
|
|
Q_strncpyz(nt->name, name, sizeof(nt->name));
|
|
if (cache_head)
|
|
cache_head->prev = nt;
|
|
cache_head = nt;
|
|
nt->user->fake = false;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, sentinalkey, CACHEDEBUG);
|
|
buf = (char*)buf+CACHEDEBUG;
|
|
memset(buf, 0, size);
|
|
memset((char *)buf+size, sentinalkey, CACHEDEBUG);
|
|
c->data = buf;
|
|
return c->data;
|
|
}
|
|
|
|
void Cache_FreeLow(int newlow)
|
|
{
|
|
}
|
|
|
|
void Cache_FreeHigh(int newhigh)
|
|
{
|
|
}
|
|
|
|
void Cache_Report (void)
|
|
{
|
|
}
|
|
|
|
void Hunk_Print_f (void)
|
|
{
|
|
cache_system_t *cs;
|
|
int zoneblocks;
|
|
int cacheused;
|
|
int zoneused;
|
|
Hunk_Print(true);
|
|
|
|
cacheused = 0;
|
|
zoneused = 0;
|
|
zoneblocks = 0;
|
|
for (cs = cache_head; cs; cs = cs->next)
|
|
{
|
|
cacheused += cs->size;
|
|
}
|
|
Con_Printf("Cache: %iKB\n", cacheused/1024);
|
|
#if 0
|
|
{
|
|
zone_t *zone;
|
|
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
zoneused += zone->size + sizeof(zone_t);
|
|
zoneblocks++;
|
|
}
|
|
Con_Printf("Zone: %i containing %iKB\n", zoneblocks, zoneused/1024);
|
|
}
|
|
#endif
|
|
}
|
|
void Cache_Init(void)
|
|
{
|
|
Cmd_AddCommand ("flush", Cache_Flush);
|
|
Cmd_AddCommand ("hunkprint", Hunk_Print_f);
|
|
#if 0
|
|
Cmd_AddCommand ("zoneprint", Zone_Print_f);
|
|
#endif
|
|
#ifdef NAMEDMALLOCS
|
|
Cmd_AddCommand ("zonegroups", Zone_Groups_f);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
typedef struct cache_system_s
|
|
{
|
|
int size; // including this header
|
|
cache_user_t *user;
|
|
char name[16];
|
|
struct cache_system_s *prev, *next;
|
|
struct cache_system_s *lru_prev, *lru_next; // for LRU flushing
|
|
} cache_system_t;
|
|
|
|
cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
|
|
|
|
cache_system_t cache_head;
|
|
|
|
/*
|
|
===========
|
|
Cache_Move
|
|
===========
|
|
*/
|
|
void Cache_Move ( cache_system_t *c)
|
|
{
|
|
cache_system_t *newc;
|
|
|
|
// we are clearing up space at the bottom, so only allocate it late
|
|
newc = Cache_TryAlloc (c->size, true);
|
|
if (newc)
|
|
{
|
|
// Con_Printf ("cache_move ok\n");
|
|
|
|
Q_memcpy ( newc+1, c+1, c->size - sizeof(cache_system_t) );
|
|
newc->user = c->user;
|
|
Q_memcpy (newc->name, c->name, sizeof(newc->name));
|
|
Cache_Free (c->user);
|
|
newc->user->data = (void *)(newc+1);
|
|
}
|
|
else
|
|
{
|
|
// Con_Printf ("cache_move failed\n");
|
|
|
|
Cache_Free (c->user); // tough luck...
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_FreeLow
|
|
|
|
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 (1)
|
|
{
|
|
c = cache_head.next;
|
|
if (c == &cache_head)
|
|
return; // nothing in cache at all
|
|
if ((qbyte *)c >= hunk_base + new_low_hunk)
|
|
return; // there is space to grow the hunk
|
|
Cache_Move ( c ); // reclaim the space
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_FreeHigh
|
|
|
|
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 (1)
|
|
{
|
|
c = cache_head.prev;
|
|
if (c == &cache_head)
|
|
return; // nothing in cache at all
|
|
if ( (qbyte *)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 ("Cache_UnlinkLRU: NULL link");
|
|
|
|
cs->lru_next->lru_prev = cs->lru_prev;
|
|
cs->lru_prev->lru_next = cs->lru_next;
|
|
|
|
cs->lru_prev = cs->lru_next = NULL;
|
|
}
|
|
|
|
void Cache_MakeLRU (cache_system_t *cs)
|
|
{
|
|
if (cs->lru_next || cs->lru_prev)
|
|
Sys_Error ("Cache_MakeLRU: active link");
|
|
|
|
cache_head.lru_next->lru_prev = cs;
|
|
cs->lru_next = cache_head.lru_next;
|
|
cs->lru_prev = &cache_head;
|
|
cache_head.lru_next = cs;
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_TryAlloc
|
|
|
|
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, *newc;
|
|
|
|
// is the cache completely empty?
|
|
|
|
if (!nobottom && cache_head.prev == &cache_head)
|
|
{
|
|
if (hunk_size - hunk_high_used - hunk_low_used < size)
|
|
Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
|
|
|
|
newc = (cache_system_t *) (hunk_base + hunk_low_used);
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
cache_head.prev = cache_head.next = newc;
|
|
newc->prev = newc->next = &cache_head;
|
|
|
|
Cache_MakeLRU (newc);
|
|
return newc;
|
|
}
|
|
|
|
// search from the bottom up for space
|
|
|
|
newc = (cache_system_t *) (hunk_base + hunk_low_used);
|
|
cs = cache_head.next;
|
|
|
|
do
|
|
{
|
|
if (!nobottom || cs != cache_head.next)
|
|
{
|
|
if ( (qbyte *)cs - (qbyte *)newc >= size)
|
|
{ // found space
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
newc->next = cs;
|
|
newc->prev = cs->prev;
|
|
cs->prev->next = newc;
|
|
cs->prev = newc;
|
|
|
|
Cache_MakeLRU (newc);
|
|
|
|
return newc;
|
|
}
|
|
}
|
|
|
|
// continue looking
|
|
newc = (cache_system_t *)((qbyte *)cs + cs->size);
|
|
cs = cs->next;
|
|
|
|
} while (cs != &cache_head);
|
|
|
|
// try to allocate one at the very end
|
|
if ( hunk_base + hunk_size - hunk_high_used - (qbyte *)newc >= size)
|
|
{
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
newc->next = &cache_head;
|
|
newc->prev = cache_head.prev;
|
|
cache_head.prev->next = newc;
|
|
cache_head.prev = newc;
|
|
|
|
Cache_MakeLRU (newc);
|
|
|
|
return newc;
|
|
}
|
|
|
|
return NULL; // couldn't allocate
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Flush
|
|
|
|
Throw everything out, so new data will be demand cached
|
|
============
|
|
*/
|
|
void Cache_Flush (void)
|
|
{
|
|
while (cache_head.next != &cache_head)
|
|
Cache_Free ( cache_head.next->user ); // reclaim the space
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
Cache_Print
|
|
|
|
============
|
|
*/
|
|
void Cache_Print (void)
|
|
{
|
|
cache_system_t *cd;
|
|
|
|
for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
|
|
{
|
|
Con_Printf ("%8i : %s\n", cd->size, cd->name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Report
|
|
|
|
============
|
|
*/
|
|
void Cache_Report (void)
|
|
{
|
|
Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Compact
|
|
|
|
============
|
|
*/
|
|
void Cache_Compact (void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Init
|
|
|
|
============
|
|
*/
|
|
void Hunk_Print_f (void) {Hunk_Print(true);}
|
|
void Cache_Init (void)
|
|
{
|
|
cache_head.next = cache_head.prev = &cache_head;
|
|
cache_head.lru_next = cache_head.lru_prev = &cache_head;
|
|
|
|
Cmd_AddCommand ("flush", Cache_Flush);
|
|
|
|
Cmd_AddCommand ("hp", Hunk_Print_f);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Cache_Free
|
|
|
|
Frees the memory and removes it from the LRU list
|
|
==============
|
|
*/
|
|
void Cache_Free (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (!c->data)
|
|
Sys_Error ("Cache_Free: not allocated");
|
|
|
|
cs = ((cache_system_t *)c->data) - 1;
|
|
|
|
cs->prev->next = cs->next;
|
|
cs->next->prev = cs->prev;
|
|
cs->next = cs->prev = NULL;
|
|
|
|
c->data = NULL;
|
|
|
|
Cache_UnlinkLRU (cs);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==============
|
|
Cache_Check
|
|
==============
|
|
*/
|
|
void *Cache_Check (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (!c->data)
|
|
return NULL;
|
|
|
|
if (c->fake) //malloc or somesuch.
|
|
return c->data;
|
|
|
|
cs = ((cache_system_t *)c->data) - 1;
|
|
|
|
// move to head of LRU
|
|
Cache_UnlinkLRU (cs);
|
|
Cache_MakeLRU (cs);
|
|
|
|
return c->data;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Cache_Alloc
|
|
==============
|
|
*/
|
|
void *Cache_Alloc (cache_user_t *c, int size, char *name)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (c->data)
|
|
Sys_Error ("Cache_Alloc: already allocated");
|
|
|
|
if (size <= 0)
|
|
Sys_Error ("Cache_Alloc: size %i", size);
|
|
|
|
size = (size + sizeof(cache_system_t) + 15) & ~15;
|
|
|
|
// find memory for it
|
|
while (1)
|
|
{
|
|
cs = Cache_TryAlloc (size, false);
|
|
if (cs)
|
|
{
|
|
strncpy (cs->name, name, sizeof(cs->name)-1);
|
|
c->data = (void *)(cs+1);
|
|
cs->user = c;
|
|
break;
|
|
}
|
|
|
|
// free the least recently used cahedat
|
|
if (cache_head.lru_prev == &cache_head)
|
|
Sys_Error ("Cache_Alloc: out of memory");
|
|
// not enough memory at all
|
|
Cache_Free ( cache_head.lru_prev->user );
|
|
}
|
|
|
|
return Cache_Check (c);
|
|
}
|
|
#endif
|
|
//============================================================================
|
|
|
|
/*
|
|
========================
|
|
Memory_Init
|
|
========================
|
|
*/
|
|
void Memory_Init (void *buf, int size)
|
|
{
|
|
#if 0 //ndef NOZONE
|
|
int p;
|
|
int zonesize = DYNAMIC_SIZE;
|
|
#endif
|
|
|
|
hunk_base = (qbyte*)buf;
|
|
hunk_size = size;
|
|
hunk_low_used = 0;
|
|
hunk_high_used = 0;
|
|
|
|
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0
|
|
srand(time(0));
|
|
sentinalkey = rand();
|
|
#endif
|
|
|
|
Cache_Init ();
|
|
|
|
#ifdef MULTITHREAD
|
|
if (!zonelock)
|
|
zonelock = Sys_CreateMutex(); // this can fail!
|
|
#endif
|
|
|
|
#if 0 //ndef NOZONE
|
|
p = COM_CheckParm ("-zone");
|
|
if (p)
|
|
{
|
|
if (p < com_argc-1)
|
|
zonesize = Q_atoi (com_argv[p+1]) * 1024;
|
|
else
|
|
Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
|
|
}
|
|
mainzone = Hunk_AllocName ( zonesize, "zone" );
|
|
Z_ClearZone (mainzone, zonesize);
|
|
#endif
|
|
}
|
|
|
|
void Memory_DeInit(void)
|
|
{
|
|
#ifdef NOHIGH
|
|
Hunk_TempFree();
|
|
#endif
|
|
Cache_Flush();
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
{
|
|
Sys_DestroyMutex(zonelock);
|
|
zonelock = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|