Kevin Shanahan ff86701895 zone: constify Z_Realloc
Signed-off-by: Kevin Shanahan <kmshanah@disenchant.net>
2012-12-21 16:59:25 +10:30

1087 lines
23 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.
*/
#include "cmd.h"
#include "common.h"
#include "console.h"
#include "mathlib.h"
#include "quakedef.h"
#include "sys.h"
#include "zone.h"
#define DYNAMIC_SIZE 0x40000 /* 256k */
#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;
static void Cache_FreeLow(int new_low_hunk);
static void Cache_FreeHigh(int new_high_hunk);
/*
* ============================================================================
*
* 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.
* ============================================================================
*/
static memzone_t *mainzone;
static void Z_ClearZone(memzone_t *zone, int size);
/*
* ========================
* Z_ClearZone
* ========================
*/
static 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 *)((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);
}
/*
* ========================
* Z_Free
* ========================
*/
void
Z_Free(const void *ptr)
{
memblock_t *block, *other;
if (!ptr)
Sys_Error("%s: NULL pointer", __func__);
block = (memblock_t *)((const byte *)ptr - sizeof(memblock_t));
if (block->id != ZONEID)
Sys_Error("%s: freed a pointer without ZONEID", __func__);
if (block->tag == 0)
Sys_Error("%s: freed a freed pointer", __func__);
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_CheckHeap
* ========================
*/
static void
Z_CheckHeap(void)
{
memblock_t *block;
for (block = mainzone->blocklist.next;; block = block->next) {
if (block->next == &mainzone->blocklist)
break; /* all blocks have been hit */
if ((byte *)block + block->size != (byte *)block->next)
Sys_Error("%s: block size does not touch the next block",
__func__);
if (block->next->prev != block)
Sys_Error("%s: next block doesn't have proper back link",
__func__);
if (!block->tag && !block->next->tag)
Sys_Error("%s: two consecutive free blocks", __func__);
}
}
static void *
Z_TagMalloc(int size, int tag)
{
int extra;
memblock_t *start, *rover, *new, *base;
if (!tag)
Sys_Error("%s: tried to use a 0 tag", __func__);
/*
* 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 (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->tag = tag; /* no longer a free block */
mainzone->rover = base->next; /* next allocation starts looking here */
base->id = ZONEID;
/* marker for memory trash testing */
*(int *)((byte *)base + base->size - 4) = ZONEID;
return (void *)((byte *)base + sizeof(memblock_t));
}
/*
* ========================
* Z_Malloc
* ========================
*/
void *
Z_Malloc(int size)
{
void *buf;
Z_CheckHeap(); /* DEBUG */
buf = Z_TagMalloc(size, 1);
if (!buf)
Sys_Error("%s: failed on allocation of %i bytes", __func__, size);
memset(buf, 0, size);
return buf;
}
/*
* ========================
* Z_Realloc
* ========================
*/
void *
Z_Realloc(const void *ptr, int size)
{
memblock_t *block;
int orig_size;
void *ret;
if (!ptr)
return Z_Malloc(size);
block = (memblock_t *)((byte *)ptr - sizeof(memblock_t));
if (block->id != ZONEID)
Sys_Error("%s: realloced a pointer without ZONEID", __func__);
if (!block->tag)
Sys_Error("%s: realloced a freed pointer", __func__);
orig_size = block->size;
orig_size -= sizeof(memblock_t);
orig_size -= 4;
Z_Free(ptr);
ret = Z_TagMalloc(size, 1);
if (!ret)
Sys_Error("%s: failed on allocation of %i bytes", __func__, size);
if (ret != ptr)
memmove(ret, ptr, qmin(orig_size, size));
return ret;
}
/* FIXME - unused; Make a console command? */
#if 0
/*
* ========================
* Z_Print
* ========================
*/
static 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 ((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");
}
}
#endif
/* ======================================================================= */
#define HUNK_SENTINAL 0x1df001ed
#define HUNK_NAMELEN 8
typedef struct {
int sentinal;
int size; /* including sizeof(hunk_t), -1 = not allocated */
char name[HUNK_NAMELEN]; /* not necessarily zero terminated */
} hunk_t;
static byte *hunk_base;
static int hunk_size;
static int hunk_low_used;
static int hunk_high_used;
static qboolean hunk_tempactive;
static int hunk_tempmark;
/*
* ==============
* Hunk_Check
*
* Run consistancy and sentinal trashing checks
* ==============
*/
void
Hunk_Check(void)
{
hunk_t *h;
for (h = (hunk_t *)hunk_base; (byte *)h != hunk_base + hunk_low_used;) {
if (h->sentinal != HUNK_SENTINAL)
Sys_Error("%s: trashed sentinal", __func__);
if (h->size < sizeof(hunk_t) ||
h->size + (byte *)h - hunk_base > hunk_size)
Sys_Error("%s: bad size", __func__);
h = (hunk_t *)((byte *)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.
* ==============
*/
static void
Hunk_Print(qboolean all)
{
hunk_t *h, *next, *endlow, *starthigh, *endhigh;
int count, sum, pwidth;
int totalblocks;
char safename[HUNK_NAMELEN + 1]; /* Zero terminated copy of hunk name */
count = 0;
sum = 0;
totalblocks = 0;
memset(safename, 0, sizeof(safename));
/* Don't put in wide spaces if not printing pointers */
pwidth = all ? (sizeof(void *) * 2 + 2) : 8;
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("%*s :%10i total hunk size\n", pwidth, "", 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("%*s :%10i REMAINING\n", pwidth, "",
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("%s: trashed sentinal", __func__);
if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
Sys_Error("%s: bad size", __func__);
next = (hunk_t *)((byte *)h + h->size);
count++;
totalblocks++;
sum += h->size;
/*
* print the single block
*/
memcpy(safename, h->name, HUNK_NAMELEN);
if (all)
Con_Printf("%*p :%10i %-*s\n", pwidth, h, h->size,
HUNK_NAMELEN, safename);
/*
* print the total
*/
if (next == endlow || next == endhigh ||
strncmp(h->name, next->name, HUNK_NAMELEN)) {
if (!all)
Con_Printf("%*s :%10i %-*s (TOTAL)\n", pwidth, "", sum,
HUNK_NAMELEN, safename);
count = 0;
sum = 0;
}
h = next;
}
Con_Printf("-------------------------\n");
Con_Printf("%8i total blocks\n", totalblocks);
}
static void
Hunk_f(void)
{
if (Cmd_Argc() == 2) {
if (!strcmp(Cmd_Argv(1), "print")) {
Hunk_Print(false);
return;
}
if (!strcmp(Cmd_Argv(1), "printall")) {
Hunk_Print(true);
return;
}
}
Con_Printf("Usage: hunk print|printall\n");
}
/*
* ===================
* Hunk_AllocName
* ===================
*/
void *
Hunk_AllocName(int size, const char *name)
{
hunk_t *h;
#ifdef PARANOID
Hunk_Check();
#endif
if (size < 0)
Sys_Error("%s: bad size: %i", __func__, size);
size = sizeof(hunk_t) + ((size + 15) & ~15);
if (hunk_size - hunk_low_used - hunk_high_used < size) {
/* Sys_Error ("%s: failed on %i bytes", __func__, size); */
#ifdef _WIN32
Sys_Error("Not enough RAM allocated. Try starting using "
"\"-heapsize 16000\" on the command line.");
#else
Sys_Error("Not enough RAM allocated. Try starting using "
"\"-mem 16\" on the command line.");
#endif
}
h = (hunk_t *)(hunk_base + hunk_low_used);
hunk_low_used += size;
Cache_FreeLow(hunk_low_used);
memset(h, 0, size);
h->size = size;
h->sentinal = HUNK_SENTINAL;
memset(h->name, 0, HUNK_NAMELEN);
memcpy(h->name, name, qmin((int)strlen(name), HUNK_NAMELEN));
return (void *)(h + 1);
}
/*
* ===================
* Hunk_Alloc
* ===================
*/
void *
Hunk_Alloc(int size)
{
return Hunk_AllocName(size, "unknown");
}
int
Hunk_LowMark(void)
{
return hunk_low_used;
}
void
Hunk_FreeToLowMark(int mark)
{
if (mark < 0 || mark > hunk_low_used)
Sys_Error("%s: bad mark %i", __func__, mark);
memset(hunk_base + mark, 0, hunk_low_used - mark);
hunk_low_used = mark;
}
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("%s: bad mark %i", __func__, 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, const char *name)
{
hunk_t *h;
if (size < 0)
Sys_Error("%s: bad size: %i", __func__, size);
if (hunk_tempactive) {
Hunk_FreeToHighMark(hunk_tempmark);
hunk_tempactive = false;
}
#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;
strncpy(h->name, name, HUNK_NAMELEN - 1);
h->name[HUNK_NAMELEN - 1] = 0;
return (void *)(h + 1);
}
/*
* =================
* Hunk_TempAlloc
*
* Return space from the top of the hunk
* =================
*/
void *
Hunk_TempAlloc(int size)
{
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;
}
/*
* =====================
* Hunk_TempAllocExtend
*
* Extend the existing temp hunk allocation.
* Size is the number of extra bytes required
* =====================
*/
void *
Hunk_TempAllocExtend(int size)
{
hunk_t *old, *new;
if (!hunk_tempactive)
Sys_Error("%s: temp hunk not active", __func__);
old = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
if (old->sentinal != HUNK_SENTINAL)
Sys_Error("%s: old sentinal trashed\n", __func__);
if (strncmp(old->name, "temp", HUNK_NAMELEN))
Sys_Error("%s: old hunk name trashed\n", __func__);
size = (size + 15) & ~15;
if (hunk_size - hunk_low_used - hunk_high_used < size) {
Con_Printf("%s: failed on %i bytes\n", __func__, size);
return NULL;
}
hunk_high_used += size;
Cache_FreeHigh(hunk_high_used);
new = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
memmove(new, old, sizeof(hunk_t));
new->size += size;
return (void *)(new + 1);
}
/*
* ===========================================================================
*
* CACHE MEMORY
*
* ===========================================================================
*/
#define CACHE_NAMELEN 32
typedef struct cache_system_s {
int size; /* including this header */
cache_user_t *user;
char name[CACHE_NAMELEN];
struct cache_system_s *prev, *next;
struct cache_system_s *lru_prev, *lru_next; /* for LRU flushing */
} cache_system_t;
static cache_system_t cache_head;
static cache_system_t *Cache_TryAlloc(int size, qboolean nobottom);
static inline cache_system_t *
Cache_System(const cache_user_t *c)
{
return (cache_system_t *)((byte *)c->data - c->pad) - 1;
}
static inline void *
Cache_Data(const cache_system_t *c)
{
return (byte *)(c + 1) + c->user->pad;
}
/*
* ===========
* Cache_Move
* ===========
*/
static void
Cache_Move(cache_system_t *c)
{
cache_system_t *new;
int pad;
/* we are clearing up space at the bottom, so only allocate it late */
new = Cache_TryAlloc(c->size, true);
if (new) {
memcpy(new + 1, c + 1, c->size - sizeof(cache_system_t));
new->user = c->user;
memcpy(new->name, c->name, sizeof(new->name));
pad = c->user->pad;
Cache_Free(c->user);
new->user->pad = pad;
new->user->data = Cache_Data(new);
} else {
/* tough luck... */
Cache_Free(c->user);
}
}
/*
* ============
* Cache_FreeLow
*
* Throw things out until the hunk can be expanded to the given point
* ============
*/
static 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 ((byte *)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
* ============
*/
static 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 ((byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
return; /* there is space to grow the hunk */
if (c == prev)
Cache_Free(c->user); /* didn't move out of the way */
else {
Cache_Move(c); /* try to move it */
prev = c;
}
}
}
static void
Cache_UnlinkLRU(cache_system_t *cs)
{
if (!cs->lru_next || !cs->lru_prev)
Sys_Error("%s: NULL link", __func__);
cs->lru_next->lru_prev = cs->lru_prev;
cs->lru_prev->lru_next = cs->lru_next;
cs->lru_prev = cs->lru_next = NULL;
}
static void
Cache_MakeLRU(cache_system_t *cs)
{
if (cs->lru_next || cs->lru_prev)
Sys_Error("%s: active link", __func__);
cache_head.lru_next->lru_prev = cs;
cs->lru_next = cache_head.lru_next;
cs->lru_prev = &cache_head;
cache_head.lru_next = cs;
}
/*
* ============
* Cache_TryAlloc
*
* Looks for a free block of memory between the high and low hunk marks
* Size should already include the header and padding
* ============
*/
static 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 (hunk_size - hunk_high_used - hunk_low_used < size)
Sys_Error("%s: %i is greater than free hunk", __func__, 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;
}
/* search from the bottom up for space */
new = (cache_system_t *)(hunk_base + hunk_low_used);
cs = cache_head.next;
do {
if (!nobottom || cs != cache_head.next) {
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;
}
}
/* 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 */
}
/*
* ============
* 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
* ============
*/
static 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));
}
/* FIXME - Unused? */
#if 0
/*
* ============
* Cache_Compact
* ============
*/
static void
Cache_Compact(void)
{
}
#endif
/*
* ============
* Cache_Init
* ============
*/
static void
Cache_Init(void)
{
cache_head.next = cache_head.prev = &cache_head;
cache_head.lru_next = cache_head.lru_prev = &cache_head;
}
/*
* ==============
* 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("%s: not allocated", __func__);
cs = Cache_System(c);
cs->prev->next = cs->next;
cs->next->prev = cs->prev;
cs->next = cs->prev = NULL;
c->pad = 0;
c->data = NULL;
Cache_UnlinkLRU(cs);
}
/*
* ==============
* Cache_Check
* ==============
*/
void *
Cache_Check(const cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
return NULL;
cs = Cache_System(c);
/* 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, const char *name)
{
return Cache_AllocPadded(c, 0, size, name);
}
/*
* ==============
* Cache_AllocPadded
* ==============
*/
void *
Cache_AllocPadded(cache_user_t *c, int pad, int size, const char *name)
{
cache_system_t *cs;
if (c->data)
Sys_Error("%s: allready allocated", __func__);
if (size <= 0)
Sys_Error("%s: size %i", __func__, size);
size = (size + pad + 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);
cs->user = c;
c->pad = pad;
c->data = Cache_Data(cs);
break;
}
/* free the least recently used cache data */
if (cache_head.lru_prev == &cache_head)
Sys_Error("%s: out of memory", __func__);
/* not enough memory at all */
Cache_Free(cache_head.lru_prev->user);
}
return Cache_Check(c);
}
static void
Cache_f(void)
{
if (Cmd_Argc() == 2) {
if (!strcmp(Cmd_Argv(1), "print")) {
Cache_Print();
return;
}
if (!strcmp(Cmd_Argv(1), "flush")) {
Cache_Flush();
return;
}
}
Con_Printf("Usage: cache print|flush\n");
}
/* ========================================================================= */
/*
* ========================
* 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) {
if (p < com_argc - 1)
zonesize = Q_atoi(com_argv[p + 1]) * 1024;
else
Sys_Error("%s: you must specify a size in KB after -zone",
__func__);
}
mainzone = Hunk_AllocName(zonesize, "zone");
Z_ClearZone(mainzone, zonesize);
/* Needs to be added after the zone init... */
Cmd_AddCommand("flush", Cache_Flush);
Cmd_AddCommand("hunk", Hunk_f);
Cmd_AddCommand("cache", Cache_f);
}