Home | History | Annotate | Download | only in client
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // Z_zone.c
     21 
     22 #include "quakedef.h"
     23 
     24 #define	DYNAMIC_SIZE	0x20000
     25 
     26 #define	ZONEID	0x1d4a11
     27 #define MINFRAGMENT	64
     28 
     29 typedef struct memblock_s
     30 {
     31 	int		size;           // including the header and possibly tiny fragments
     32 	int     tag;            // a tag of 0 is a free block
     33 	int     id;        		// should be ZONEID
     34 	struct memblock_s       *next, *prev;
     35 	int		pad;			// pad to 64 bit boundary
     36 } memblock_t;
     37 
     38 typedef struct
     39 {
     40 	int		size;		// total bytes malloced, including header
     41 	memblock_t	blocklist;		// start / end cap for linked list
     42 	memblock_t	*rover;
     43 } memzone_t;
     44 
     45 void Cache_FreeLow (int new_low_hunk);
     46 void Cache_FreeHigh (int new_high_hunk);
     47 
     48 
     49 /*
     50 ==============================================================================
     51 
     52 						ZONE MEMORY ALLOCATION
     53 
     54 There is never any space between memblocks, and there will never be two
     55 contiguous free memblocks.
     56 
     57 The rover can be left pointing at a non-empty block
     58 
     59 The zone calls are pretty much only used for small strings and structures,
     60 all big things are allocated on the hunk.
     61 ==============================================================================
     62 */
     63 
     64 memzone_t	*mainzone;
     65 
     66 void Z_ClearZone (memzone_t *zone, int size);
     67 
     68 
     69 /*
     70 ========================
     71 Z_ClearZone
     72 ========================
     73 */
     74 void Z_ClearZone (memzone_t *zone, int size)
     75 {
     76 	memblock_t	*block;
     77 
     78 // set the entire zone to one free block
     79 
     80 	zone->blocklist.next = zone->blocklist.prev = block =
     81 		(memblock_t *)( (byte *)zone + sizeof(memzone_t) );
     82 	zone->blocklist.tag = 1;	// in use block
     83 	zone->blocklist.id = 0;
     84 	zone->blocklist.size = 0;
     85 	zone->rover = block;
     86 
     87 	block->prev = block->next = &zone->blocklist;
     88 	block->tag = 0;			// free block
     89 	block->id = ZONEID;
     90 	block->size = size - sizeof(memzone_t);
     91 }
     92 
     93 
     94 /*
     95 ========================
     96 Z_Free
     97 ========================
     98 */
     99 void Z_Free (void *ptr)
    100 {
    101 	memblock_t	*block, *other;
    102 
    103 	if (!ptr)
    104 		Sys_Error ("Z_Free: NULL pointer");
    105 
    106 	block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
    107 	if (block->id != ZONEID)
    108 		Sys_Error ("Z_Free: freed a pointer without ZONEID");
    109 	if (block->tag == 0)
    110 		Sys_Error ("Z_Free: freed a freed pointer");
    111 
    112 	block->tag = 0;		// mark as free
    113 
    114 	other = block->prev;
    115 	if (!other->tag)
    116 	{	// merge with previous free block
    117 		other->size += block->size;
    118 		other->next = block->next;
    119 		other->next->prev = other;
    120 		if (block == mainzone->rover)
    121 			mainzone->rover = other;
    122 		block = other;
    123 	}
    124 
    125 	other = block->next;
    126 	if (!other->tag)
    127 	{	// merge the next free block onto the end
    128 		block->size += other->size;
    129 		block->next = other->next;
    130 		block->next->prev = block;
    131 		if (other == mainzone->rover)
    132 			mainzone->rover = block;
    133 	}
    134 }
    135 
    136 
    137 /*
    138 ========================
    139 Z_Malloc
    140 ========================
    141 */
    142 void *Z_Malloc (int size)
    143 {
    144 	void	*buf;
    145 
    146 Z_CheckHeap ();	// DEBUG
    147 	buf = Z_TagMalloc (size, 1);
    148 	if (!buf)
    149 		Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
    150 	Q_memset (buf, 0, size);
    151 
    152 	return buf;
    153 }
    154 
    155 void *Z_TagMalloc (int size, int tag)
    156 {
    157 	int		extra;
    158 	memblock_t	*start, *rover, *new, *base;
    159 
    160 	if (!tag)
    161 		Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
    162 
    163 //
    164 // scan through the block list looking for the first free block
    165 // of sufficient size
    166 //
    167 	size += sizeof(memblock_t);	// account for size of block header
    168 	size += 4;					// space for memory trash tester
    169 	size = (size + 7) & ~7;		// align to 8-byte boundary
    170 
    171 	base = rover = mainzone->rover;
    172 	start = base->prev;
    173 
    174 	do
    175 	{
    176 		if (rover == start)	// scaned all the way around the list
    177 			return NULL;
    178 		if (rover->tag)
    179 			base = rover = rover->next;
    180 		else
    181 			rover = rover->next;
    182 	} while (base->tag || base->size < size);
    183 
    184 //
    185 // found a block big enough
    186 //
    187 	extra = base->size - size;
    188 	if (extra >  MINFRAGMENT)
    189 	{	// there will be a free fragment after the allocated block
    190 		new = (memblock_t *) ((byte *)base + size );
    191 		new->size = extra;
    192 		new->tag = 0;			// free block
    193 		new->prev = base;
    194 		new->id = ZONEID;
    195 		new->next = base->next;
    196 		new->next->prev = new;
    197 		base->next = new;
    198 		base->size = size;
    199 	}
    200 
    201 	base->tag = tag;				// no longer a free block
    202 
    203 	mainzone->rover = base->next;	// next allocation will start looking here
    204 
    205 	base->id = ZONEID;
    206 
    207 // marker for memory trash testing
    208 	*(int *)((byte *)base + base->size - 4) = ZONEID;
    209 
    210 	return (void *) ((byte *)base + sizeof(memblock_t));
    211 }
    212 
    213 
    214 /*
    215 ========================
    216 Z_Print
    217 ========================
    218 */
    219 void Z_Print (memzone_t *zone)
    220 {
    221 	memblock_t	*block;
    222 
    223 	Con_Printf ("zone size: %i  location: %p\n",mainzone->size,mainzone);
    224 
    225 	for (block = zone->blocklist.next ; ; block = block->next)
    226 	{
    227 		Con_Printf ("block:%p    size:%7i    tag:%3i\n",
    228 			block, block->size, block->tag);
    229 
    230 		if (block->next == &zone->blocklist)
    231 			break;			// all blocks have been hit
    232 		if ( (byte *)block + block->size != (byte *)block->next)
    233 			Con_Printf ("ERROR: block size does not touch the next block\n");
    234 		if ( block->next->prev != block)
    235 			Con_Printf ("ERROR: next block doesn't have proper back link\n");
    236 		if (!block->tag && !block->next->tag)
    237 			Con_Printf ("ERROR: two consecutive free blocks\n");
    238 	}
    239 }
    240 
    241 
    242 /*
    243 ========================
    244 Z_CheckHeap
    245 ========================
    246 */
    247 void Z_CheckHeap (void)
    248 {
    249 	memblock_t	*block;
    250 
    251 	for (block = mainzone->blocklist.next ; ; block = block->next)
    252 	{
    253 		if (block->next == &mainzone->blocklist)
    254 			break;			// all blocks have been hit
    255 		if ( (byte *)block + block->size != (byte *)block->next)
    256 			Sys_Error ("Z_CheckHeap: block size does not touch the next block\n");
    257 		if ( block->next->prev != block)
    258 			Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
    259 		if (!block->tag && !block->next->tag)
    260 			Sys_Error ("Z_CheckHeap: two consecutive free blocks\n");
    261 	}
    262 }
    263 
    264 //============================================================================
    265 
    266 #define	HUNK_SENTINAL	0x1df001ed
    267 
    268 typedef struct
    269 {
    270 	int		sentinal;
    271 	int		size;		// including sizeof(hunk_t), -1 = not allocated
    272 	char	name[8];
    273 } hunk_t;
    274 
    275 byte	*hunk_base;
    276 int		hunk_size;
    277 
    278 int		hunk_low_used;
    279 int		hunk_high_used;
    280 
    281 qboolean	hunk_tempactive;
    282 int		hunk_tempmark;
    283 
    284 void R_FreeTextures (void);
    285 
    286 /*
    287 ==============
    288 Hunk_Check
    289 
    290 Run consistancy and sentinal trahing checks
    291 ==============
    292 */
    293 void Hunk_Check (void)
    294 {
    295 	hunk_t	*h;
    296 
    297 	for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; )
    298 	{
    299 		if (h->sentinal != HUNK_SENTINAL)
    300 			Sys_Error ("Hunk_Check: trahsed sentinal");
    301 		if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
    302 			Sys_Error ("Hunk_Check: bad size");
    303 		h = (hunk_t *)((byte *)h+h->size);
    304 	}
    305 }
    306 
    307 /*
    308 ==============
    309 Hunk_Print
    310 
    311 If "all" is specified, every single allocation is printed.
    312 Otherwise, allocations with the same name will be totaled up before printing.
    313 ==============
    314 */
    315 void Hunk_Print (qboolean all)
    316 {
    317 	hunk_t	*h, *next, *endlow, *starthigh, *endhigh;
    318 	int		count, sum;
    319 	int		totalblocks;
    320 	char	name[9];
    321 
    322 	name[8] = 0;
    323 	count = 0;
    324 	sum = 0;
    325 	totalblocks = 0;
    326 
    327 	h = (hunk_t *)hunk_base;
    328 	endlow = (hunk_t *)(hunk_base + hunk_low_used);
    329 	starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
    330 	endhigh = (hunk_t *)(hunk_base + hunk_size);
    331 
    332 	Con_Printf ("          :%8i total hunk size\n", hunk_size);
    333 	Con_Printf ("-------------------------\n");
    334 
    335 	while (1)
    336 	{
    337 	//
    338 	// skip to the high hunk if done with low hunk
    339 	//
    340 		if ( h == endlow )
    341 		{
    342 			Con_Printf ("-------------------------\n");
    343 			Con_Printf ("          :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
    344 			Con_Printf ("-------------------------\n");
    345 			h = starthigh;
    346 		}
    347 
    348 	//
    349 	// if totally done, break
    350 	//
    351 		if ( h == endhigh )
    352 			break;
    353 
    354 	//
    355 	// run consistancy checks
    356 	//
    357 		if (h->sentinal != HUNK_SENTINAL)
    358 			Sys_Error ("Hunk_Check: trahsed sentinal");
    359 		if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
    360 			Sys_Error ("Hunk_Check: bad size");
    361 
    362 		next = (hunk_t *)((byte *)h+h->size);
    363 		count++;
    364 		totalblocks++;
    365 		sum += h->size;
    366 
    367 	//
    368 	// print the single block
    369 	//
    370 		memcpy (name, h->name, 8);
    371 		if (all)
    372 			Con_Printf ("%8p :%8i %8s\n",h, h->size, name);
    373 
    374 	//
    375 	// print the total
    376 	//
    377 		if (next == endlow || next == endhigh ||
    378 		strncmp (h->name, next->name, 8) )
    379 		{
    380 			if (!all)
    381 				Con_Printf ("          :%8i %8s (TOTAL)\n",sum, name);
    382 			count = 0;
    383 			sum = 0;
    384 		}
    385 
    386 		h = next;
    387 	}
    388 
    389 	Con_Printf ("-------------------------\n");
    390 	Con_Printf ("%8i total blocks\n", totalblocks);
    391 
    392 }
    393 
    394 /*
    395 ===================
    396 Hunk_AllocName
    397 ===================
    398 */
    399 void *Hunk_AllocName (int size, char *name)
    400 {
    401 	hunk_t	*h;
    402 
    403 #ifdef PARANOID
    404 	Hunk_Check ();
    405 #endif
    406 
    407 	if (size < 0)
    408 		Sys_Error ("Hunk_Alloc: bad size: %i", size);
    409 
    410 	size = sizeof(hunk_t) + ((size+15)&~15);
    411 
    412 	if (hunk_size - hunk_low_used - hunk_high_used < size)
    413 //		Sys_Error ("Hunk_Alloc: failed on %i bytes",size);
    414 #ifdef _WIN32
    415 	  	Sys_Error ("Not enough RAM allocated.  Try starting using \"-heapsize 16000\" on the QuakeWorld command line.");
    416 #else
    417 	  	Sys_Error ("Not enough RAM allocated.  Try starting using \"-mem 16\" on the QuakeWorld command line.");
    418 #endif
    419 
    420 	h = (hunk_t *)(hunk_base + hunk_low_used);
    421 	hunk_low_used += size;
    422 
    423 	Cache_FreeLow (hunk_low_used);
    424 
    425 	memset (h, 0, size);
    426 
    427 	h->size = size;
    428 	h->sentinal = HUNK_SENTINAL;
    429 	Q_strncpy (h->name, name, 8);
    430 
    431 	return (void *)(h+1);
    432 }
    433 
    434 /*
    435 ===================
    436 Hunk_Alloc
    437 ===================
    438 */
    439 void *Hunk_Alloc (int size)
    440 {
    441 	return Hunk_AllocName (size, "unknown");
    442 }
    443 
    444 int	Hunk_LowMark (void)
    445 {
    446 	return hunk_low_used;
    447 }
    448 
    449 void Hunk_FreeToLowMark (int mark)
    450 {
    451 	if (mark < 0 || mark > hunk_low_used)
    452 		Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
    453 	memset (hunk_base + mark, 0, hunk_low_used - mark);
    454 	hunk_low_used = mark;
    455 }
    456 
    457 int	Hunk_HighMark (void)
    458 {
    459 	if (hunk_tempactive)
    460 	{
    461 		hunk_tempactive = false;
    462 		Hunk_FreeToHighMark (hunk_tempmark);
    463 	}
    464 
    465 	return hunk_high_used;
    466 }
    467 
    468 void Hunk_FreeToHighMark (int mark)
    469 {
    470 	if (hunk_tempactive)
    471 	{
    472 		hunk_tempactive = false;
    473 		Hunk_FreeToHighMark (hunk_tempmark);
    474 	}
    475 	if (mark < 0 || mark > hunk_high_used)
    476 		Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
    477 	memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
    478 	hunk_high_used = mark;
    479 }
    480 
    481 
    482 /*
    483 ===================
    484 Hunk_HighAllocName
    485 ===================
    486 */
    487 void *Hunk_HighAllocName (int size, char *name)
    488 {
    489 	hunk_t	*h;
    490 
    491 	if (size < 0)
    492 		Sys_Error ("Hunk_HighAllocName: bad size: %i", size);
    493 
    494 	if (hunk_tempactive)
    495 	{
    496 		Hunk_FreeToHighMark (hunk_tempmark);
    497 		hunk_tempactive = false;
    498 	}
    499 
    500 #ifdef PARANOID
    501 	Hunk_Check ();
    502 #endif
    503 
    504 	size = sizeof(hunk_t) + ((size+15)&~15);
    505 
    506 	if (hunk_size - hunk_low_used - hunk_high_used < size)
    507 	{
    508 		Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size);
    509 		return NULL;
    510 	}
    511 
    512 	hunk_high_used += size;
    513 	Cache_FreeHigh (hunk_high_used);
    514 
    515 	h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
    516 
    517 	memset (h, 0, size);
    518 	h->size = size;
    519 	h->sentinal = HUNK_SENTINAL;
    520 	Q_strncpy (h->name, name, 8);
    521 
    522 	return (void *)(h+1);
    523 }
    524 
    525 
    526 /*
    527 =================
    528 Hunk_TempAlloc
    529 
    530 Return space from the top of the hunk
    531 =================
    532 */
    533 void *Hunk_TempAlloc (int size)
    534 {
    535 	void	*buf;
    536 
    537 	size = (size+15)&~15;
    538 
    539 	if (hunk_tempactive)
    540 	{
    541 		Hunk_FreeToHighMark (hunk_tempmark);
    542 		hunk_tempactive = false;
    543 	}
    544 
    545 	hunk_tempmark = Hunk_HighMark ();
    546 
    547 	buf = Hunk_HighAllocName (size, "temp");
    548 
    549 	hunk_tempactive = true;
    550 
    551 	return buf;
    552 }
    553 
    554 /*
    555 ===============================================================================
    556 
    557 CACHE MEMORY
    558 
    559 ===============================================================================
    560 */
    561 
    562 typedef struct cache_system_s
    563 {
    564 	int						size;		// including this header
    565 	cache_user_t			*user;
    566 	char					name[16];
    567 	struct cache_system_s	*prev, *next;
    568 	struct cache_system_s	*lru_prev, *lru_next;	// for LRU flushing
    569 } cache_system_t;
    570 
    571 cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
    572 
    573 cache_system_t	cache_head;
    574 
    575 /*
    576 ===========
    577 Cache_Move
    578 ===========
    579 */
    580 void Cache_Move ( cache_system_t *c)
    581 {
    582 	cache_system_t		*new;
    583 
    584 // we are clearing up space at the bottom, so only allocate it late
    585 	new = Cache_TryAlloc (c->size, true);
    586 	if (new)
    587 	{
    588 //		Con_Printf ("cache_move ok\n");
    589 
    590 		Q_memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) );
    591 		new->user = c->user;
    592 		Q_memcpy (new->name, c->name, sizeof(new->name));
    593 		Cache_Free (c->user);
    594 		new->user->data = (void *)(new+1);
    595 	}
    596 	else
    597 	{
    598 //		Con_Printf ("cache_move failed\n");
    599 
    600 		Cache_Free (c->user);		// tough luck...
    601 	}
    602 }
    603 
    604 /*
    605 ============
    606 Cache_FreeLow
    607 
    608 Throw things out until the hunk can be expanded to the given point
    609 ============
    610 */
    611 void Cache_FreeLow (int new_low_hunk)
    612 {
    613 	cache_system_t	*c;
    614 
    615 	while (1)
    616 	{
    617 		c = cache_head.next;
    618 		if (c == &cache_head)
    619 			return;		// nothing in cache at all
    620 		if ((byte *)c >= hunk_base + new_low_hunk)
    621 			return;		// there is space to grow the hunk
    622 		Cache_Move ( c );	// reclaim the space
    623 	}
    624 }
    625 
    626 /*
    627 ============
    628 Cache_FreeHigh
    629 
    630 Throw things out until the hunk can be expanded to the given point
    631 ============
    632 */
    633 void Cache_FreeHigh (int new_high_hunk)
    634 {
    635 	cache_system_t	*c, *prev;
    636 
    637 	prev = NULL;
    638 	while (1)
    639 	{
    640 		c = cache_head.prev;
    641 		if (c == &cache_head)
    642 			return;		// nothing in cache at all
    643 		if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
    644 			return;		// there is space to grow the hunk
    645 		if (c == prev)
    646 			Cache_Free (c->user);	// didn't move out of the way
    647 		else
    648 		{
    649 			Cache_Move (c);	// try to move it
    650 			prev = c;
    651 		}
    652 	}
    653 }
    654 
    655 void Cache_UnlinkLRU (cache_system_t *cs)
    656 {
    657 	if (!cs->lru_next || !cs->lru_prev)
    658 		Sys_Error ("Cache_UnlinkLRU: NULL link");
    659 
    660 	cs->lru_next->lru_prev = cs->lru_prev;
    661 	cs->lru_prev->lru_next = cs->lru_next;
    662 
    663 	cs->lru_prev = cs->lru_next = NULL;
    664 }
    665 
    666 void Cache_MakeLRU (cache_system_t *cs)
    667 {
    668 	if (cs->lru_next || cs->lru_prev)
    669 		Sys_Error ("Cache_MakeLRU: active link");
    670 
    671 	cache_head.lru_next->lru_prev = cs;
    672 	cs->lru_next = cache_head.lru_next;
    673 	cs->lru_prev = &cache_head;
    674 	cache_head.lru_next = cs;
    675 }
    676 
    677 /*
    678 ============
    679 Cache_TryAlloc
    680 
    681 Looks for a free block of memory between the high and low hunk marks
    682 Size should already include the header and padding
    683 ============
    684 */
    685 cache_system_t *Cache_TryAlloc (int size, qboolean nobottom)
    686 {
    687 	cache_system_t	*cs, *new;
    688 
    689 // is the cache completely empty?
    690 
    691 	if (!nobottom && cache_head.prev == &cache_head)
    692 	{
    693 		if (hunk_size - hunk_high_used - hunk_low_used < size)
    694 			Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
    695 
    696 		new = (cache_system_t *) (hunk_base + hunk_low_used);
    697 		memset (new, 0, sizeof(*new));
    698 		new->size = size;
    699 
    700 		cache_head.prev = cache_head.next = new;
    701 		new->prev = new->next = &cache_head;
    702 
    703 		Cache_MakeLRU (new);
    704 		return new;
    705 	}
    706 
    707 // search from the bottom up for space
    708 
    709 	new = (cache_system_t *) (hunk_base + hunk_low_used);
    710 	cs = cache_head.next;
    711 
    712 	do
    713 	{
    714 		if (!nobottom || cs != cache_head.next)
    715 		{
    716 			if ( (byte *)cs - (byte *)new >= size)
    717 			{	// found space
    718 				memset (new, 0, sizeof(*new));
    719 				new->size = size;
    720 
    721 				new->next = cs;
    722 				new->prev = cs->prev;
    723 				cs->prev->next = new;
    724 				cs->prev = new;
    725 
    726 				Cache_MakeLRU (new);
    727 
    728 				return new;
    729 			}
    730 		}
    731 
    732 	// continue looking
    733 		new = (cache_system_t *)((byte *)cs + cs->size);
    734 		cs = cs->next;
    735 
    736 	} while (cs != &cache_head);
    737 
    738 // try to allocate one at the very end
    739 	if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size)
    740 	{
    741 		memset (new, 0, sizeof(*new));
    742 		new->size = size;
    743 
    744 		new->next = &cache_head;
    745 		new->prev = cache_head.prev;
    746 		cache_head.prev->next = new;
    747 		cache_head.prev = new;
    748 
    749 		Cache_MakeLRU (new);
    750 
    751 		return new;
    752 	}
    753 
    754 	return NULL;		// couldn't allocate
    755 }
    756 
    757 /*
    758 ============
    759 Cache_Flush
    760 
    761 Throw everything out, so new data will be demand cached
    762 ============
    763 */
    764 void Cache_Flush (void)
    765 {
    766 	while (cache_head.next != &cache_head)
    767 		Cache_Free ( cache_head.next->user );	// reclaim the space
    768 }
    769 
    770 
    771 /*
    772 ============
    773 Cache_Print
    774 
    775 ============
    776 */
    777 void Cache_Print (void)
    778 {
    779 	cache_system_t	*cd;
    780 
    781 	for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
    782 	{
    783 		Con_Printf ("%8i : %s\n", cd->size, cd->name);
    784 	}
    785 }
    786 
    787 /*
    788 ============
    789 Cache_Report
    790 
    791 ============
    792 */
    793 void Cache_Report (void)
    794 {
    795 	Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
    796 }
    797 
    798 /*
    799 ============
    800 Cache_Compact
    801 
    802 ============
    803 */
    804 void Cache_Compact (void)
    805 {
    806 }
    807 
    808 /*
    809 ============
    810 Cache_Init
    811 
    812 ============
    813 */
    814 void Cache_Init (void)
    815 {
    816 	cache_head.next = cache_head.prev = &cache_head;
    817 	cache_head.lru_next = cache_head.lru_prev = &cache_head;
    818 
    819 	Cmd_AddCommand ("flush", Cache_Flush);
    820 }
    821 
    822 /*
    823 ==============
    824 Cache_Free
    825 
    826 Frees the memory and removes it from the LRU list
    827 ==============
    828 */
    829 void Cache_Free (cache_user_t *c)
    830 {
    831 	cache_system_t	*cs;
    832 
    833 	if (!c->data)
    834 		Sys_Error ("Cache_Free: not allocated");
    835 
    836 	cs = ((cache_system_t *)c->data) - 1;
    837 
    838 	cs->prev->next = cs->next;
    839 	cs->next->prev = cs->prev;
    840 	cs->next = cs->prev = NULL;
    841 
    842 	c->data = NULL;
    843 
    844 	Cache_UnlinkLRU (cs);
    845 }
    846 
    847 
    848 
    849 /*
    850 ==============
    851 Cache_Check
    852 ==============
    853 */
    854 void *Cache_Check (cache_user_t *c)
    855 {
    856 	cache_system_t	*cs;
    857 
    858 	if (!c->data)
    859 		return NULL;
    860 
    861 	cs = ((cache_system_t *)c->data) - 1;
    862 
    863 // move to head of LRU
    864 	Cache_UnlinkLRU (cs);
    865 	Cache_MakeLRU (cs);
    866 
    867 	return c->data;
    868 }
    869 
    870 
    871 /*
    872 ==============
    873 Cache_Alloc
    874 ==============
    875 */
    876 void *Cache_Alloc (cache_user_t *c, int size, char *name)
    877 {
    878 	cache_system_t	*cs;
    879 
    880 	if (c->data)
    881 		Sys_Error ("Cache_Alloc: allready allocated");
    882 
    883 	if (size <= 0)
    884 		Sys_Error ("Cache_Alloc: size %i", size);
    885 
    886 	size = (size + sizeof(cache_system_t) + 15) & ~15;
    887 
    888 // find memory for it
    889 	while (1)
    890 	{
    891 		cs = Cache_TryAlloc (size, false);
    892 		if (cs)
    893 		{
    894 			strncpy (cs->name, name, sizeof(cs->name)-1);
    895 			c->data = (void *)(cs+1);
    896 			cs->user = c;
    897 			break;
    898 		}
    899 
    900 	// free the least recently used cahedat
    901 		if (cache_head.lru_prev == &cache_head)
    902 			Sys_Error ("Cache_Alloc: out of memory");
    903 													// not enough memory at all
    904 		Cache_Free ( cache_head.lru_prev->user );
    905 	}
    906 
    907 	return Cache_Check (c);
    908 }
    909 
    910 //============================================================================
    911 
    912 
    913 /*
    914 ========================
    915 Memory_Init
    916 ========================
    917 */
    918 void Memory_Init (void *buf, int size)
    919 {
    920 	int p;
    921 	int zonesize = DYNAMIC_SIZE;
    922 
    923 	hunk_base = buf;
    924 	hunk_size = size;
    925 	hunk_low_used = 0;
    926 	hunk_high_used = 0;
    927 
    928 	Cache_Init ();
    929 	p = COM_CheckParm ("-zone");
    930 	if (p)
    931 	{
    932 		if (p < com_argc-1)
    933 			zonesize = Q_atoi (com_argv[p+1]) * 1024;
    934 		else
    935 			Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
    936 	}
    937 	mainzone = Hunk_AllocName ( zonesize, "zone" );
    938 	Z_ClearZone (mainzone, zonesize);
    939 }
    940 
    941