• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
Z_ClearZone(memzone_t * zone,int size)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 */
Z_Free(void * ptr)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 */
Z_Malloc(int size)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 
Z_TagMalloc(int size,int tag)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 */
Z_Print(memzone_t * zone)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 */
Z_CheckHeap(void)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 */
Hunk_Check(void)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 */
Hunk_Print(qboolean all)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 */
Hunk_AllocName(int size,char * name)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 */
Hunk_Alloc(int size)439 void *Hunk_Alloc (int size)
440 {
441 	return Hunk_AllocName (size, "unknown");
442 }
443 
Hunk_LowMark(void)444 int	Hunk_LowMark (void)
445 {
446 	return hunk_low_used;
447 }
448 
Hunk_FreeToLowMark(int mark)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 
Hunk_HighMark(void)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 
Hunk_FreeToHighMark(int mark)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 */
Hunk_HighAllocName(int size,char * name)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 */
Hunk_TempAlloc(int size)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 */
Cache_Move(cache_system_t * c)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 */
Cache_FreeLow(int new_low_hunk)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 */
Cache_FreeHigh(int new_high_hunk)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 
Cache_UnlinkLRU(cache_system_t * cs)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 
Cache_MakeLRU(cache_system_t * cs)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 */
Cache_TryAlloc(int size,qboolean nobottom)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 */
Cache_Flush(void)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 */
Cache_Print(void)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 */
Cache_Report(void)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 */
Cache_Compact(void)804 void Cache_Compact (void)
805 {
806 }
807 
808 /*
809 ============
810 Cache_Init
811 
812 ============
813 */
Cache_Init(void)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 */
Cache_Free(cache_user_t * c)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 */
Cache_Check(cache_user_t * c)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 */
Cache_Alloc(cache_user_t * c,int size,char * name)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 */
Memory_Init(void * buf,int size)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