X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=zone.c;h=2aa575e786b442cd96a59f683c856f4f89bac3da;hp=a5bb4f0f750d8b0869cc0a67273d6fbdf515fc97;hb=85a33377d64ae8438e6582a7b8472f5a4bd41942;hpb=15f74b0989186c26df4bbf0f2ea6c5b095e74269 diff --git a/zone.c b/zone.c index a5bb4f0f..2aa575e7 100644 --- a/zone.c +++ b/zone.c @@ -8,7 +8,7 @@ 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. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -20,879 +20,969 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Z_zone.c #include "quakedef.h" +#include "thread.h" -// LordHavoc: everyone used a -zone 512, but 128k is sufficient usually, I think... -#define DYNAMIC_SIZE 0x20000 -//#define DYNAMIC_SIZE 0xc000 - -#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; +#ifdef WIN32 +#include +#include +#else +#include +#endif -void Cache_FreeLow (int new_low_hunk); -void Cache_FreeHigh (int new_high_hunk); +#ifdef _MSC_VER +#include +#else +#include +#endif +#define MEMHEADER_SENTINEL_FOR_ADDRESS(p) ((sentinel_seed ^ (unsigned int) (uintptr_t) (p)) + sentinel_seed) +unsigned int sentinel_seed; +qboolean mem_bigendian = false; +void *mem_mutex = NULL; -/* -============================================================================== +// divVerent: enables file backed malloc using mmap to conserve swap space (instead of malloc) +#ifndef FILE_BACKED_MALLOC +# define FILE_BACKED_MALLOC 0 +#endif - ZONE MEMORY ALLOCATION +// LordHavoc: enables our own low-level allocator (instead of malloc) +#ifndef MEMCLUMPING +# define MEMCLUMPING 0 +#endif +#ifndef MEMCLUMPING_FREECLUMPS +# define MEMCLUMPING_FREECLUMPS 0 +#endif -There is never any space between memblocks, and there will never be two -contiguous free memblocks. +#if MEMCLUMPING +// smallest unit we care about is this many bytes +#define MEMUNIT 128 +// try to do 32MB clumps, but overhead eats into this +#ifndef MEMWANTCLUMPSIZE +# define MEMWANTCLUMPSIZE (1<<27) +#endif +// give malloc padding so we can't waste most of a page at the end +#define MEMCLUMPSIZE (MEMWANTCLUMPSIZE - MEMWANTCLUMPSIZE/MEMUNIT/32 - 128) +#define MEMBITS (MEMCLUMPSIZE / MEMUNIT) +#define MEMBITINTS (MEMBITS / 32) + +typedef struct memclump_s +{ + // contents of the clump + unsigned char block[MEMCLUMPSIZE]; + // should always be MEMCLUMP_SENTINEL + unsigned int sentinel1; + // if a bit is on, it means that the MEMUNIT bytes it represents are + // allocated, otherwise free + unsigned int bits[MEMBITINTS]; + // should always be MEMCLUMP_SENTINEL + unsigned int sentinel2; + // if this drops to 0, the clump is freed + size_t blocksinuse; + // largest block of memory available (this is reset to an optimistic + // number when anything is freed, and updated when alloc fails the clump) + size_t largestavailable; + // next clump in the chain + struct memclump_s *chain; +} +memclump_t; -The rover can be left pointing at a non-empty block +#if MEMCLUMPING == 2 +static memclump_t masterclump; +#endif +static memclump_t *clumpchain = NULL; +#endif -The zone calls are pretty much only used for small strings and structures, -all big things are allocated on the hunk. -============================================================================== -*/ -memzone_t *mainzone; +cvar_t developer_memory = {0, "developer_memory", "0", "prints debugging information about memory allocations"}; +cvar_t developer_memorydebug = {0, "developer_memorydebug", "0", "enables memory corruption checks (very slow)"}; +cvar_t developer_memoryreportlargerthanmb = {0, "developer_memorylargerthanmb", "16", "prints debugging information about memory allocations over this size"}; +cvar_t sys_memsize_physical = {CVAR_READONLY, "sys_memsize_physical", "", "physical memory size in MB (or empty if unknown)"}; +cvar_t sys_memsize_virtual = {CVAR_READONLY, "sys_memsize_virtual", "", "virtual memory size in MB (or empty if unknown)"}; -void Z_ClearZone (memzone_t *zone, int size); +static mempool_t *poolchain = NULL; +void Mem_PrintStats(void); +void Mem_PrintList(size_t minallocationsize); -/* -======================== -Z_ClearZone -======================== -*/ -void Z_ClearZone (memzone_t *zone, int size) +#if FILE_BACKED_MALLOC +#include +#include +typedef struct mmap_data_s { - memblock_t *block; - -// set the entire zone to one free block - - zone->blocklist.next = zone->blocklist.prev = block = - (memblock_t *)( (byte *)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); + size_t len; } - - -/* -======================== -Z_Free -======================== -*/ -void Z_Free (void *ptr) -{ - memblock_t *block, *other; - - if (!ptr) - Sys_Error ("Z_Free: NULL pointer"); - - block = (memblock_t *) ( (byte *)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; - } +mmap_data_t; +static void *mmap_malloc(size_t size) +{ + char vabuf[MAX_OSPATH + 1]; + char *tmpdir = getenv("TEMP"); + mmap_data_t *data; + int fd; + size += sizeof(mmap_data_t); // waste block + dpsnprintf(vabuf, sizeof(vabuf), "%s/darkplaces.XXXXXX", tmpdir ? tmpdir : "/tmp"); + fd = mkstemp(vabuf); + if(fd < 0) + return NULL; + ftruncate(fd, size); + data = (unsigned char *) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, fd, 0); + close(fd); + unlink(vabuf); + if(!data) + return NULL; + data->len = size; + return (void *) (data + 1); } +static void mmap_free(void *mem) +{ + mmap_data_t *data; + if(!mem) + return; + data = ((mmap_data_t *) mem) - 1; + munmap(data, data->len); +} +#define malloc mmap_malloc +#define free mmap_free +#endif - -/* -======================== -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); - memset (buf, 0, size); - - return buf; -} - -void *Z_TagMalloc (int size, int tag) -{ - int extra; - memblock_t *start, *rover, *new, *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-byte boundary - - base = rover = mainzone->rover; - start = base->prev; - - do +#if MEMCLUMPING != 2 +// some platforms have a malloc that returns NULL but succeeds later +// (Windows growing its swapfile for example) +static void *attempt_malloc(size_t size) +{ + void *base; + // try for half a second or so + unsigned int attempts = 500; + while (attempts--) { - 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 - new = (memblock_t *) ((byte *)base + size ); - new->size = extra; - new->tag = 0; // free block - new->prev = base; - new->id = ZONEID; - new->next = base->next; - new->next->prev = new; - base->next = new; - base->size = size; + base = (void *)malloc(size); + if (base) + return base; + Sys_Sleep(1000); } - - base->tag = tag; // no longer a free block - - mainzone->rover = base->next; // next allocation will start looking here - - base->id = ZONEID; + return NULL; +} +#endif -// marker for memory trash testing - *(int *)((byte *)base + base->size - 4) = ZONEID; +#if MEMCLUMPING +static memclump_t *Clump_NewClump(void) +{ + memclump_t **clumpchainpointer; + memclump_t *clump; +#if MEMCLUMPING == 2 + if (clumpchain) + return NULL; + clump = &masterclump; +#else + clump = (memclump_t*)attempt_malloc(sizeof(memclump_t)); + if (!clump) + return NULL; +#endif - return (void *) ((byte *)base + sizeof(memblock_t)); + // initialize clump + if (developer_memorydebug.integer) + memset(clump, 0xEF, sizeof(*clump)); + clump->sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1); + memset(clump->bits, 0, sizeof(clump->bits)); + clump->sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2); + clump->blocksinuse = 0; + clump->largestavailable = 0; + clump->chain = NULL; + + // link clump into chain + for (clumpchainpointer = &clumpchain;*clumpchainpointer;clumpchainpointer = &(*clumpchainpointer)->chain) + ; + *clumpchainpointer = clump; + + return clump; } +#endif - -/* -======================== -Z_Print -======================== -*/ -void Z_Print (memzone_t *zone) +// low level clumping functions, all other memory functions use these +static void *Clump_AllocBlock(size_t size) { - memblock_t *block; - - Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); - - for (block = zone->blocklist.next ; ; block = block->next) + unsigned char *base; +#if MEMCLUMPING + if (size <= MEMCLUMPSIZE) { - 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"); + int index; + unsigned int bit; + unsigned int needbits; + unsigned int startbit; + unsigned int endbit; + unsigned int needints; + int startindex; + int endindex; + unsigned int value; + unsigned int mask; + unsigned int *array; + memclump_t **clumpchainpointer; + memclump_t *clump; + needbits = (size + MEMUNIT - 1) / MEMUNIT; + needints = (needbits+31)>>5; + for (clumpchainpointer = &clumpchain;;clumpchainpointer = &(*clumpchainpointer)->chain) + { + clump = *clumpchainpointer; + if (!clump) + { + clump = Clump_NewClump(); + if (!clump) + return NULL; + } + if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1)) + Sys_Error("Clump_AllocBlock: trashed sentinel1\n"); + if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2)) + Sys_Error("Clump_AllocBlock: trashed sentinel2\n"); + startbit = 0; + endbit = startbit + needbits; + array = clump->bits; + // do as fast a search as possible, even if it means crude alignment + if (needbits >= 32) + { + // large allocations are aligned to large boundaries + // furthermore, they are allocated downward from the top... + endindex = MEMBITINTS; + startindex = endindex - needints; + index = endindex; + while (--index >= startindex) + { + if (array[index]) + { + endindex = index; + startindex = endindex - needints; + if (startindex < 0) + goto nofreeblock; + } + } + startbit = startindex*32; + goto foundblock; + } + else + { + // search for a multi-bit gap in a single int + // (not dealing with the cases that cross two ints) + mask = (1<bits[bit>>5] & (1<<(bit & 31))) + Sys_Error("Clump_AllocBlock: internal error (%i needbits)\n", needbits); + for (bit = startbit;bit < endbit;bit++) + clump->bits[bit>>5] |= (1<<(bit & 31)); + clump->blocksinuse += needbits; + base = clump->block + startbit * MEMUNIT; + if (developer_memorydebug.integer) + memset(base, 0xBF, needbits * MEMUNIT); + return base; +nofreeblock: + ; + } + // never reached + return NULL; } + // too big, allocate it directly +#endif +#if MEMCLUMPING == 2 + return NULL; +#else + base = (unsigned char *)attempt_malloc(size); + if (base && developer_memorydebug.integer) + memset(base, 0xAF, size); + return base; +#endif +} +static void Clump_FreeBlock(void *base, size_t size) +{ +#if MEMCLUMPING + unsigned int needbits; + unsigned int startbit; + unsigned int endbit; + unsigned int bit; + memclump_t **clumpchainpointer; + memclump_t *clump; + unsigned char *start = (unsigned char *)base; + for (clumpchainpointer = &clumpchain;(clump = *clumpchainpointer);clumpchainpointer = &(*clumpchainpointer)->chain) + { + if (start >= clump->block && start < clump->block + MEMCLUMPSIZE) + { + if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1)) + Sys_Error("Clump_FreeBlock: trashed sentinel1\n"); + if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2)) + Sys_Error("Clump_FreeBlock: trashed sentinel2\n"); + if (start + size > clump->block + MEMCLUMPSIZE) + Sys_Error("Clump_FreeBlock: block overrun\n"); + // the block belongs to this clump, clear the range + needbits = (size + MEMUNIT - 1) / MEMUNIT; + startbit = (start - clump->block) / MEMUNIT; + endbit = startbit + needbits; + // first verify all bits are set, otherwise this may be misaligned or a double free + for (bit = startbit;bit < endbit;bit++) + if ((clump->bits[bit>>5] & (1<<(bit & 31))) == 0) + Sys_Error("Clump_FreeBlock: double free\n"); + for (bit = startbit;bit < endbit;bit++) + clump->bits[bit>>5] &= ~(1<<(bit & 31)); + clump->blocksinuse -= needbits; + memset(base, 0xFF, needbits * MEMUNIT); + // if all has been freed, free the clump itself + if (clump->blocksinuse == 0) + { + *clumpchainpointer = clump->chain; + if (developer_memorydebug.integer) + memset(clump, 0xFF, sizeof(*clump)); +#if MEMCLUMPING != 2 + free(clump); +#endif + } + return; + } + } + // does not belong to any known chunk... assume it was a direct allocation +#endif +#if MEMCLUMPING != 2 + memset(base, 0xFF, size); + free(base); +#endif } - -/* -======================== -Z_CheckHeap -======================== -*/ -void Z_CheckHeap (void) +void *_Mem_Alloc(mempool_t *pool, void *olddata, size_t size, size_t alignment, const char *filename, int fileline) { - memblock_t *block; - - for (block = mainzone->blocklist.next ; ; block = block->next) + unsigned int sentinel1; + unsigned int sentinel2; + size_t realsize; + size_t sharedsize; + size_t remainsize; + memheader_t *mem; + memheader_t *oldmem; + unsigned char *base; + + if (size <= 0) { - if (block->next == &mainzone->blocklist) - break; // all blocks have been hit - if ( (byte *)block + block->size != (byte *)block->next) - Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); - if ( block->next->prev != block) - Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); - if (!block->tag && !block->next->tag) - Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); + if (olddata) + _Mem_Free(olddata, filename, fileline); + return NULL; } + if (pool == NULL) + { + if(olddata) + pool = ((memheader_t *)((unsigned char *) olddata - sizeof(memheader_t)))->pool; + else + Sys_Error("Mem_Alloc: pool == NULL (alloc at %s:%i)", filename, fileline); + } + if (mem_mutex) + Thread_LockMutex(mem_mutex); + if (developer_memory.integer || size >= developer_memoryreportlargerthanmb.value * 1048576) + Con_DPrintf("Mem_Alloc: pool %s, file %s:%i, size %f bytes (%f MB)\n", pool->name, filename, fileline, (double)size, (double)size / 1048576.0f); + //if (developer.integer > 0 && developer_memorydebug.integer) + // _Mem_CheckSentinelsGlobal(filename, fileline); + pool->totalsize += size; + realsize = alignment + sizeof(memheader_t) + size + sizeof(sentinel2); + pool->realsize += realsize; + base = (unsigned char *)Clump_AllocBlock(realsize); + if (base == NULL) + { + Mem_PrintList(0); + Mem_PrintStats(); + Mem_PrintList(1<<30); + Mem_PrintStats(); + Sys_Error("Mem_Alloc: out of memory (alloc of size %f (%.3fMB) at %s:%i)", (double)realsize, (double)realsize / (1 << 20), filename, fileline); + } + // calculate address that aligns the end of the memheader_t to the specified alignment + mem = (memheader_t*)((((size_t)base + sizeof(memheader_t) + (alignment-1)) & ~(alignment-1)) - sizeof(memheader_t)); + mem->baseaddress = (void*)base; + mem->filename = filename; + mem->fileline = fileline; + mem->size = size; + mem->pool = pool; + + // calculate sentinels (detects buffer overruns, in a way that is hard to exploit) + sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel); + sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size); + mem->sentinel = sentinel1; + memcpy((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2)); + + // append to head of list + mem->next = pool->chain; + mem->prev = NULL; + pool->chain = mem; + if (mem->next) + mem->next->prev = mem; + + if (mem_mutex) + Thread_UnlockMutex(mem_mutex); + + // copy the shared portion in the case of a realloc, then memset the rest + sharedsize = 0; + remainsize = size; + if (olddata) + { + oldmem = (memheader_t*)olddata - 1; + sharedsize = min(oldmem->size, size); + memcpy((void *)((unsigned char *) mem + sizeof(memheader_t)), olddata, sharedsize); + remainsize -= sharedsize; + _Mem_Free(olddata, filename, fileline); + } + memset((void *)((unsigned char *) mem + sizeof(memheader_t) + sharedsize), 0, remainsize); + return (void *)((unsigned char *) mem + sizeof(memheader_t)); } -//============================================================================ - -#define HUNK_SENTINAL 0x1df001ed +// only used by _Mem_Free and _Mem_FreePool +static void _Mem_FreeBlock(memheader_t *mem, const char *filename, int fileline) +{ + mempool_t *pool; + size_t size; + size_t realsize; + unsigned int sentinel1; + unsigned int sentinel2; + + // check sentinels (detects buffer overruns, in a way that is hard to exploit) + sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel); + sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size); + if (mem->sentinel != sentinel1) + Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); + if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2))) + Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline); + + pool = mem->pool; + if (developer_memory.integer) + Con_DPrintf("Mem_Free: pool %s, alloc %s:%i, free %s:%i, size %i bytes\n", pool->name, mem->filename, mem->fileline, filename, fileline, (int)(mem->size)); + // unlink memheader from doubly linked list + if ((mem->prev ? mem->prev->next != mem : pool->chain != mem) || (mem->next && mem->next->prev != mem)) + Sys_Error("Mem_Free: not allocated or double freed (free at %s:%i)", filename, fileline); + if (mem_mutex) + Thread_LockMutex(mem_mutex); + if (mem->prev) + mem->prev->next = mem->next; + else + pool->chain = mem->next; + if (mem->next) + mem->next->prev = mem->prev; + // memheader has been unlinked, do the actual free now + size = mem->size; + realsize = sizeof(memheader_t) + size + sizeof(sentinel2); + pool->totalsize -= size; + pool->realsize -= realsize; + Clump_FreeBlock(mem->baseaddress, realsize); + if (mem_mutex) + Thread_UnlockMutex(mem_mutex); +} -typedef struct +void _Mem_Free(void *data, const char *filename, int fileline) { - int sentinal; - int size; // including sizeof(hunk_t), -1 = not allocated - char name[24]; -} hunk_t; - -byte *hunk_base; -int hunk_size; - -int hunk_low_used; -int hunk_high_used; + if (data == NULL) + { + Con_DPrintf("Mem_Free: data == NULL (called at %s:%i)\n", filename, fileline); + return; + } -void R_FreeTextures (void); + if (developer_memorydebug.integer) + { + //_Mem_CheckSentinelsGlobal(filename, fileline); + if (!Mem_IsAllocated(NULL, data)) + Sys_Error("Mem_Free: data is not allocated (called at %s:%i)", filename, fileline); + } -/* -============== -Hunk_Check + _Mem_FreeBlock((memheader_t *)((unsigned char *) data - sizeof(memheader_t)), filename, fileline); +} -Run consistancy and sentinal trashing checks -============== -*/ -void Hunk_Check (void) +mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const char *filename, int fileline) { - hunk_t *h; - - for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) + mempool_t *pool; + if (developer_memorydebug.integer) + _Mem_CheckSentinelsGlobal(filename, fileline); + pool = (mempool_t *)Clump_AllocBlock(sizeof(mempool_t)); + if (pool == NULL) { - if (h->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trashed sentinal"); - if (h->size < sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); - h = (hunk_t *)((byte *)h+h->size); + Mem_PrintList(0); + Mem_PrintStats(); + Mem_PrintList(1<<30); + Mem_PrintStats(); + Sys_Error("Mem_AllocPool: out of memory (allocpool at %s:%i)", filename, fileline); } + memset(pool, 0, sizeof(mempool_t)); + pool->sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1); + pool->sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2); + pool->filename = filename; + pool->fileline = fileline; + pool->flags = flags; + pool->chain = NULL; + pool->totalsize = 0; + pool->realsize = sizeof(mempool_t); + strlcpy (pool->name, name, sizeof (pool->name)); + pool->parent = parent; + pool->next = poolchain; + poolchain = pool; + return pool; } -/* -============== -Hunk_Print +void _Mem_FreePool(mempool_t **poolpointer, const char *filename, int fileline) +{ + mempool_t *pool = *poolpointer; + mempool_t **chainaddress, *iter, *temp; -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, i; - int totalblocks; - char name[25]; - - name[24] = 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) + if (developer_memorydebug.integer) + _Mem_CheckSentinelsGlobal(filename, fileline); + if (pool) { - // - // 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->sentinal != HUNK_SENTINAL) - Sys_Error ("Hunk_Check: trashed sentinal"); - if (h->size < sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) - Sys_Error ("Hunk_Check: bad size"); - - next = (hunk_t *)((byte *)h+h->size); - count++; - totalblocks++; - sum += h->size; - - // - // print the single block - // - // LordHavoc: pad name to full length - for (i = 0;i < 24;i++) - { - if (!h->name[i]) - break; - name[i] = h->name[i]; - } - for (;i < 24;i++) - name[i] = ' '; - //memcpy (name, h->name, 24); - if (all) - Con_Printf ("%8p :%8i %s\n",h, h->size, name); - - // - // print the total - // - if (next == endlow || next == endhigh || strncmp(h->name, next->name, 24)) - { - if (!all) - Con_Printf (" :%8i %s (TOTAL)\n",sum, name); - count = 0; - sum = 0; + // unlink pool from chain + for (chainaddress = &poolchain;*chainaddress && *chainaddress != pool;chainaddress = &((*chainaddress)->next)); + if (*chainaddress != pool) + Sys_Error("Mem_FreePool: pool already free (freepool at %s:%i)", filename, fileline); + if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1)) + Sys_Error("Mem_FreePool: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline); + if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2)) + Sys_Error("Mem_FreePool: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline); + *chainaddress = pool->next; + + // free memory owned by the pool + while (pool->chain) + _Mem_FreeBlock(pool->chain, filename, fileline); + + // free child pools, too + for(iter = poolchain; iter; iter = temp) { + temp = iter->next; + if(iter->parent == pool) + _Mem_FreePool(&temp, filename, fileline); } - h = next; - } + // free the pool itself + Clump_FreeBlock(pool, sizeof(*pool)); -// Con_Printf ("-------------------------\n"); - Con_Printf ("%8i total blocks\n", totalblocks); - + *poolpointer = NULL; + } } -/* -=================== -Hunk_AllocName -=================== -*/ -void *Hunk_AllocName (int size, char *name) +void _Mem_EmptyPool(mempool_t *pool, const char *filename, int fileline) { - hunk_t *h; - -#ifdef PARANOID - Hunk_Check (); -#endif + mempool_t *chainaddress; - if (size < 0) - Sys_Error ("Hunk_Alloc: bad size: %i", size); - - size = sizeof(hunk_t) + ((size+15)&~15); - - if (hunk_size - hunk_low_used - hunk_high_used < size) - Sys_Error ("Hunk_Alloc: failed on %i bytes (name = %s)",size, name); - - h = (hunk_t *)(hunk_base + hunk_low_used); - hunk_low_used += size; - - Cache_FreeLow (hunk_low_used); + if (developer_memorydebug.integer) + { + //_Mem_CheckSentinelsGlobal(filename, fileline); + // check if this pool is in the poolchain + for (chainaddress = poolchain;chainaddress;chainaddress = chainaddress->next) + if (chainaddress == pool) + break; + if (!chainaddress) + Sys_Error("Mem_EmptyPool: pool is already free (emptypool at %s:%i)", filename, fileline); + } + if (pool == NULL) + Sys_Error("Mem_EmptyPool: pool == NULL (emptypool at %s:%i)", filename, fileline); + if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1)) + Sys_Error("Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline); + if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2)) + Sys_Error("Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline); + + // free memory owned by the pool + while (pool->chain) + _Mem_FreeBlock(pool->chain, filename, fileline); + + // empty child pools, too + for(chainaddress = poolchain; chainaddress; chainaddress = chainaddress->next) + if(chainaddress->parent == pool) + _Mem_EmptyPool(chainaddress, filename, fileline); - memset (h, 0, size); - - h->size = size; - h->sentinal = HUNK_SENTINAL; - strncpy (h->name, name, 24); - - return (void *)(h+1); } -int Hunk_LowMark (void) +void _Mem_CheckSentinels(void *data, const char *filename, int fileline) { - return hunk_low_used; -} + memheader_t *mem; + unsigned int sentinel1; + unsigned int sentinel2; -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; -} + if (data == NULL) + Sys_Error("Mem_CheckSentinels: data == NULL (sentinel check at %s:%i)", filename, fileline); -int Hunk_HighMark (void) -{ - return hunk_high_used; + mem = (memheader_t *)((unsigned char *) data - sizeof(memheader_t)); + sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel); + sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size); + if (mem->sentinel != sentinel1) + Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); + if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2))) + Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline); } -void Hunk_FreeToHighMark (int mark) +#if MEMCLUMPING +static void _Mem_CheckClumpSentinels(memclump_t *clump, const char *filename, int fileline) { - 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; + // this isn't really very useful + if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1)) + Sys_Error("Mem_CheckClumpSentinels: trashed sentinel 1 (sentinel check at %s:%i)", filename, fileline); + if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2)) + Sys_Error("Mem_CheckClumpSentinels: trashed sentinel 2 (sentinel check at %s:%i)", filename, fileline); } +#endif - -/* -=================== -Hunk_HighAllocName -=================== -*/ -void *Hunk_HighAllocName (int size, char *name) +void _Mem_CheckSentinelsGlobal(const char *filename, int fileline) { - hunk_t *h; - - if (size < 0) - Sys_Error ("Hunk_HighAllocName: bad size: %i", size); - -#ifdef PARANOID - Hunk_Check (); + memheader_t *mem; +#if MEMCLUMPING + memclump_t *clump; #endif - - size = sizeof(hunk_t) + ((size+15)&~15); - - if (hunk_size - hunk_low_used - hunk_high_used < size) + mempool_t *pool; + for (pool = poolchain;pool;pool = pool->next) { - Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); - return NULL; + if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1)) + Sys_Error("Mem_CheckSentinelsGlobal: trashed pool sentinel 1 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline); + if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2)) + Sys_Error("Mem_CheckSentinelsGlobal: trashed pool sentinel 2 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline); } - - 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; - strncpy (h->name, name, 8); - - return (void *)(h+1); + for (pool = poolchain;pool;pool = pool->next) + for (mem = pool->chain;mem;mem = mem->next) + _Mem_CheckSentinels((void *)((unsigned char *) mem + sizeof(memheader_t)), filename, fileline); +#if MEMCLUMPING + for (pool = poolchain;pool;pool = pool->next) + for (clump = clumpchain;clump;clump = clump->chain) + _Mem_CheckClumpSentinels(clump, filename, fileline); +#endif } -/* -=============================================================================== - -CACHE MEMORY - -=============================================================================== -*/ - -typedef struct cache_system_s +qboolean Mem_IsAllocated(mempool_t *pool, void *data) { - 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; + memheader_t *header; + memheader_t *target; -/* -=========== -Cache_Move -=========== -*/ -void Cache_Move ( cache_system_t *c) -{ - cache_system_t *new; - -// we are clearing up space at the bottom, so only allocate it late - new = Cache_TryAlloc (c->size, true); - if (new) + if (pool) { -// Con_Printf ("cache_move ok\n"); - - memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); - new->user = c->user; - memcpy (new->name, c->name, sizeof(new->name)); - Cache_Free (c->user); - new->user->data = (void *)(new+1); + // search only one pool + target = (memheader_t *)((unsigned char *) data - sizeof(memheader_t)); + for( header = pool->chain ; header ; header = header->next ) + if( header == target ) + return true; } else { -// Con_Printf ("cache_move failed\n"); - - Cache_Free (c->user); // tough luck... + // search all pools + for (pool = poolchain;pool;pool = pool->next) + if (Mem_IsAllocated(pool, data)) + return true; } + return false; } -/* -============ -Cache_FreeLow +void Mem_ExpandableArray_NewArray(memexpandablearray_t *l, mempool_t *mempool, size_t recordsize, int numrecordsperarray) +{ + memset(l, 0, sizeof(*l)); + l->mempool = mempool; + l->recordsize = recordsize; + l->numrecordsperarray = numrecordsperarray; +} -Throw things out until the hunk can be expanded to the given point -============ -*/ -void Cache_FreeLow (int new_low_hunk) +void Mem_ExpandableArray_FreeArray(memexpandablearray_t *l) { - cache_system_t *c; - - while (1) + size_t i; + if (l->maxarrays) { - 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 + for (i = 0;i != l->numarrays;i++) + Mem_Free(l->arrays[i].data); + Mem_Free(l->arrays); } + memset(l, 0, sizeof(*l)); } -/* -============ -Cache_FreeHigh - -Throw things out until the hunk can be expanded to the given point -============ -*/ -void Cache_FreeHigh (int new_high_hunk) +void *Mem_ExpandableArray_AllocRecord(memexpandablearray_t *l) { - cache_system_t *c, *prev; - - prev = NULL; - while (1) + size_t i, j; + for (i = 0;;i++) { - 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 + if (i == l->numarrays) + { + if (l->numarrays == l->maxarrays) + { + memexpandablearray_array_t *oldarrays = l->arrays; + l->maxarrays = max(l->maxarrays * 2, 128); + l->arrays = (memexpandablearray_array_t*) Mem_Alloc(l->mempool, l->maxarrays * sizeof(*l->arrays)); + if (oldarrays) + { + memcpy(l->arrays, oldarrays, l->numarrays * sizeof(*l->arrays)); + Mem_Free(oldarrays); + } + } + l->arrays[i].numflaggedrecords = 0; + l->arrays[i].data = (unsigned char *) Mem_Alloc(l->mempool, (l->recordsize + 1) * l->numrecordsperarray); + l->arrays[i].allocflags = l->arrays[i].data + l->recordsize * l->numrecordsperarray; + l->numarrays++; + } + if (l->arrays[i].numflaggedrecords < l->numrecordsperarray) { - Cache_Move (c); // try to move it - prev = c; + for (j = 0;j < l->numrecordsperarray;j++) + { + if (!l->arrays[i].allocflags[j]) + { + l->arrays[i].allocflags[j] = true; + l->arrays[i].numflaggedrecords++; + memset(l->arrays[i].data + l->recordsize * j, 0, l->recordsize); + return (void *)(l->arrays[i].data + l->recordsize * j); + } + } } } } -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, *new; - -// is the cache completely empty? - - if (!nobottom && cache_head.prev == &cache_head) +/***************************************************************************** + * IF YOU EDIT THIS: + * If this function was to change the size of the "expandable" array, you have + * to update r_shadow.c + * Just do a search for "range =", R_ShadowClearWorldLights would be the first + * function to look at. (And also seems like the only one?) You might have to + * move the call to Mem_ExpandableArray_IndexRange back into for(...) loop's + * condition + */ +void Mem_ExpandableArray_FreeRecord(memexpandablearray_t *l, void *record) // const! +{ + size_t i, j; + unsigned char *p = (unsigned char *)record; + for (i = 0;i != l->numarrays;i++) { - if (hunk_size - hunk_high_used - hunk_low_used < size) - Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); - - new = (cache_system_t *) (hunk_base + hunk_low_used); - memset (new, 0, sizeof(*new)); - new->size = size; - - cache_head.prev = cache_head.next = new; - new->prev = new->next = &cache_head; - - Cache_MakeLRU (new); - return new; + if (p >= l->arrays[i].data && p < (l->arrays[i].data + l->recordsize * l->numrecordsperarray)) + { + j = (p - l->arrays[i].data) / l->recordsize; + if (p != l->arrays[i].data + j * l->recordsize) + Sys_Error("Mem_ExpandableArray_FreeRecord: no such record %p\n", p); + if (!l->arrays[i].allocflags[j]) + Sys_Error("Mem_ExpandableArray_FreeRecord: record %p is already free!\n", p); + l->arrays[i].allocflags[j] = false; + l->arrays[i].numflaggedrecords--; + return; + } } - -// search from the bottom up for space +} - new = (cache_system_t *) (hunk_base + hunk_low_used); - cs = cache_head.next; - - do +size_t Mem_ExpandableArray_IndexRange(const memexpandablearray_t *l) +{ + size_t i, j, k, end = 0; + for (i = 0;i < l->numarrays;i++) { - if (!nobottom || cs != cache_head.next) + for (j = 0, k = 0;k < l->arrays[i].numflaggedrecords;j++) { - if ( (byte *)cs - (byte *)new >= size) - { // found space - memset (new, 0, sizeof(*new)); - new->size = size; - - new->next = cs; - new->prev = cs->prev; - cs->prev->next = new; - cs->prev = new; - - Cache_MakeLRU (new); - - return new; + if (l->arrays[i].allocflags[j]) + { + end = l->numrecordsperarray * i + j + 1; + k++; } } - - // continue looking - new = (cache_system_t *)((byte *)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 - (byte *)new >= size) - { - memset (new, 0, sizeof(*new)); - new->size = size; - - new->next = &cache_head; - new->prev = cache_head.prev; - cache_head.prev->next = new; - cache_head.prev = new; - - Cache_MakeLRU (new); - - return new; } - - return NULL; // couldn't allocate + return end; } -/* -============ -Cache_Flush - -Throw everything out, so new data will be demand cached -============ -*/ -void Cache_Flush (void) +void *Mem_ExpandableArray_RecordAtIndex(const memexpandablearray_t *l, size_t index) { - while (cache_head.next != &cache_head) - Cache_Free ( cache_head.next->user ); // reclaim the space + size_t i, j; + i = index / l->numrecordsperarray; + j = index % l->numrecordsperarray; + if (i >= l->numarrays || !l->arrays[i].allocflags[j]) + return NULL; + return (void *)(l->arrays[i].data + j * l->recordsize); } -/* -============ -Cache_Print +// used for temporary memory allocations around the engine, not for longterm +// storage, if anything in this pool stays allocated during gameplay, it is +// considered a leak +mempool_t *tempmempool; +// only for zone +mempool_t *zonemempool; -============ -*/ -void Cache_Print (void) +void Mem_PrintStats(void) { - cache_system_t *cd; - - for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) + size_t count = 0, size = 0, realsize = 0; + mempool_t *pool; + memheader_t *mem; + Mem_CheckSentinelsGlobal(); + for (pool = poolchain;pool;pool = pool->next) + { + count++; + size += pool->totalsize; + realsize += pool->realsize; + } + Con_Printf("%lu memory pools, totalling %lu bytes (%.3fMB)\n", (unsigned long)count, (unsigned long)size, size / 1048576.0); + Con_Printf("total allocated size: %lu bytes (%.3fMB)\n", (unsigned long)realsize, realsize / 1048576.0); + for (pool = poolchain;pool;pool = pool->next) { - Con_Printf ("%8i : %s\n", cd->size, cd->name); + if ((pool->flags & POOLFLAG_TEMP) && pool->chain) + { + Con_Printf("Memory pool %p has sprung a leak totalling %lu bytes (%.3fMB)! Listing contents...\n", (void *)pool, (unsigned long)pool->totalsize, pool->totalsize / 1048576.0); + for (mem = pool->chain;mem;mem = mem->next) + Con_Printf("%10lu bytes allocated at %s:%i\n", (unsigned long)mem->size, mem->filename, mem->fileline); + } } } -/* -============ -Cache_Report - -============ -*/ -void Cache_Report (void) +void Mem_PrintList(size_t minallocationsize) { - Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); + mempool_t *pool; + memheader_t *mem; + Mem_CheckSentinelsGlobal(); + Con_Print("memory pool list:\n" + "size name\n"); + for (pool = poolchain;pool;pool = pool->next) + { + Con_Printf("%10luk (%10luk actual) %s (%+li byte change) %s\n", (unsigned long) ((pool->totalsize + 1023) / 1024), (unsigned long)((pool->realsize + 1023) / 1024), pool->name, (long)(pool->totalsize - pool->lastchecksize), (pool->flags & POOLFLAG_TEMP) ? "TEMP" : ""); + pool->lastchecksize = pool->totalsize; + for (mem = pool->chain;mem;mem = mem->next) + if (mem->size >= minallocationsize) + Con_Printf("%10lu bytes allocated at %s:%i\n", (unsigned long)mem->size, mem->filename, mem->fileline); + } } -/* -============ -Cache_Compact - -============ -*/ -void Cache_Compact (void) +static void MemList_f(void) { + switch(Cmd_Argc()) + { + case 1: + Mem_PrintList(1<<30); + Mem_PrintStats(); + break; + case 2: + Mem_PrintList(atoi(Cmd_Argv(1)) * 1024); + Mem_PrintStats(); + break; + default: + Con_Print("MemList_f: unrecognized options\nusage: memlist [all]\n"); + break; + } } -/* -============ -Cache_Init - -============ -*/ -void Cache_Init (void) +static void MemStats_f(void) { - cache_head.next = cache_head.prev = &cache_head; - cache_head.lru_next = cache_head.lru_prev = &cache_head; - - Cmd_AddCommand ("flush", Cache_Flush); + Mem_CheckSentinelsGlobal(); + R_TextureStats_Print(false, false, true); + GL_Mesh_ListVBOs(false); + Mem_PrintStats(); } -/* -============== -Cache_Free -Frees the memory and removes it from the LRU list -============== -*/ -void Cache_Free (cache_user_t *c) +char* Mem_strdup (mempool_t *pool, const char* s) { - 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); + char* p; + size_t sz; + if (s == NULL) + return NULL; + sz = strlen (s) + 1; + p = (char*)Mem_Alloc (pool, sz); + strlcpy (p, s, sz); + return p; } - - /* -============== -Cache_Check -============== +======================== +Memory_Init +======================== */ -void *Cache_Check (cache_user_t *c) +void Memory_Init (void) { - cache_system_t *cs; + static union {unsigned short s;unsigned char b[2];} u; + u.s = 0x100; + mem_bigendian = u.b[0] != 0; - if (!c->data) - return NULL; - - cs = ((cache_system_t *)c->data) - 1; + sentinel_seed = rand(); + poolchain = NULL; + tempmempool = Mem_AllocPool("Temporary Memory", POOLFLAG_TEMP, NULL); + zonemempool = Mem_AllocPool("Zone", 0, NULL); -// move to head of LRU - Cache_UnlinkLRU (cs); - Cache_MakeLRU (cs); - - return c->data; + if (Thread_HasThreads()) + mem_mutex = Thread_CreateMutex(); } - -/* -============== -Cache_Alloc -============== -*/ -void *Cache_Alloc (cache_user_t *c, int size, char *name) +void Memory_Shutdown (void) { - cache_system_t *cs; +// Mem_FreePool (&zonemempool); +// Mem_FreePool (&tempmempool); - if (c->data) - Sys_Error ("Cache_Alloc: allready allocated"); - - if (size <= 0) - Sys_Error ("Cache_Alloc: size %i", size); + if (mem_mutex) + Thread_DestroyMutex(mem_mutex); + mem_mutex = NULL; +} - size = (size + sizeof(cache_system_t) + 15) & ~15; +void Memory_Init_Commands (void) +{ + Cmd_AddCommand ("memstats", MemStats_f, "prints memory system statistics"); + Cmd_AddCommand ("memlist", MemList_f, "prints memory pool information (or if used as memlist 5 lists individual allocations of 5K or larger, 0 lists all allocations)"); + Cvar_RegisterVariable (&developer_memory); + Cvar_RegisterVariable (&developer_memorydebug); + Cvar_RegisterVariable (&developer_memoryreportlargerthanmb); + Cvar_RegisterVariable (&sys_memsize_physical); + Cvar_RegisterVariable (&sys_memsize_virtual); -// find memory for it - while (1) +#if defined(WIN32) +#ifdef _WIN64 { - cs = Cache_TryAlloc (size, false); - if (cs) + MEMORYSTATUSEX status; + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, 8388608); + // then improve + status.dwLength = sizeof(status); + if(GlobalMemoryStatusEx(&status)) { - strncpy (cs->name, name, sizeof(cs->name)-1); - c->data = (void *)(cs+1); - cs->user = c; - break; + Cvar_SetValueQuick(&sys_memsize_physical, status.ullTotalPhys / 1048576.0); + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value, status.ullTotalVirtual / 1048576.0)); } - - // 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); -} - -//============================================================================ - - -void HunkList_f(void) -{ - if (Cmd_Argc() == 2) - if (strcmp(Cmd_Argv(1), "all")) - Con_Printf("usage: hunklist [all]\n"); - else - Hunk_Print(true); - else - Hunk_Print(false); -} - -/* -======================== -Memory_Init -======================== -*/ -void Memory_Init (void *buf, int size) -{ - int p; - int zonesize = DYNAMIC_SIZE; - - hunk_base = buf; - hunk_size = size; - hunk_low_used = 0; - hunk_high_used = 0; - - Cache_Init (); - p = COM_CheckParm ("-zone"); - if (p) + } +#else { - if (p < com_argc-1) - zonesize = atoi (com_argv[p+1]) * 1024; - else - Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); + MEMORYSTATUS status; + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, 2048); + // then improve + status.dwLength = sizeof(status); + GlobalMemoryStatus(&status); + Cvar_SetValueQuick(&sys_memsize_physical, status.dwTotalPhys / 1048576.0); + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value, status.dwTotalVirtual / 1048576.0)); } - mainzone = Hunk_AllocName (zonesize, "zone" ); - Z_ClearZone (mainzone, zonesize); - Cmd_AddCommand ("hunklist", HunkList_f); +#endif +#else + { + // first guess + Cvar_SetValueQuick(&sys_memsize_virtual, (sizeof(void*) == 4) ? 2048 : 268435456); + // then improve + { + // Linux, and BSD with linprocfs mounted + FILE *f = fopen("/proc/meminfo", "r"); + if(f) + { + static char buf[1024]; + while(fgets(buf, sizeof(buf), f)) + { + const char *p = buf; + if(!COM_ParseToken_Console(&p)) + continue; + if(!strcmp(com_token, "MemTotal:")) + { + if(!COM_ParseToken_Console(&p)) + continue; + Cvar_SetValueQuick(&sys_memsize_physical, atof(com_token) / 1024.0); + } + if(!strcmp(com_token, "SwapTotal:")) + { + if(!COM_ParseToken_Console(&p)) + continue; + Cvar_SetValueQuick(&sys_memsize_virtual, min(sys_memsize_virtual.value , atof(com_token) / 1024.0 + sys_memsize_physical.value)); + } + } + fclose(f); + } + } + } +#endif }