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