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