2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
24 #define DYNAMIC_SIZE 0xc000
26 #define ZONEID 0x1d4a11
27 #define MINFRAGMENT 64
29 typedef struct memblock_s
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
40 int size
; // total bytes malloced, including header
41 memblock_t blocklist
; // start / end cap for linked list
45 void Cache_FreeLow (int new_low_hunk
);
46 void Cache_FreeHigh (int new_high_hunk
);
50 ==============================================================================
52 ZONE MEMORY ALLOCATION
54 There is never any space between memblocks, and there will never be two
55 contiguous free memblocks.
57 The rover can be left pointing at a non-empty block
59 The zone calls are pretty much only used for small strings and structures,
60 all big things are allocated on the hunk.
61 ==============================================================================
66 void Z_ClearZone (memzone_t
*zone
, int size
);
70 ========================
72 ========================
74 void Z_ClearZone (memzone_t
*zone
, int size
)
78 // set the entire zone to one free block
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;
87 block
->prev
= block
->next
= &zone
->blocklist
;
88 block
->tag
= 0; // free block
90 block
->size
= size
- sizeof(memzone_t
);
95 ========================
97 ========================
99 void Z_Free (void *ptr
)
101 memblock_t
*block
, *other
;
104 Sys_Error ("Z_Free: NULL pointer");
106 block
= (memblock_t
*) ( (byte
*)ptr
- sizeof(memblock_t
));
107 if (block
->id
!= ZONEID
)
108 Sys_Error ("Z_Free: freed a pointer without ZONEID");
110 Sys_Error ("Z_Free: freed a freed pointer");
112 block
->tag
= 0; // mark as free
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
;
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
;
138 ========================
140 ========================
142 void *Z_Malloc (int size
)
146 Z_CheckHeap (); // DEBUG
147 buf
= Z_TagMalloc (size
, 1);
149 Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size
);
150 Q_memset (buf
, 0, size
);
155 void *Z_TagMalloc (int size
, int tag
)
158 memblock_t
*start
, *rover
, *new, *base
;
161 Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
164 // scan through the block list looking for the first free block
165 // of sufficient size
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
171 base
= rover
= mainzone
->rover
;
176 if (rover
== start
) // scaned all the way around the list
179 base
= rover
= rover
->next
;
182 } while (base
->tag
|| base
->size
< size
);
185 // found a block big enough
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
);
192 new->tag
= 0; // free block
195 new->next
= base
->next
;
196 new->next
->prev
= new;
201 base
->tag
= tag
; // no longer a free block
203 mainzone
->rover
= base
->next
; // next allocation will start looking here
207 // marker for memory trash testing
208 *(int *)((byte
*)base
+ base
->size
- 4) = ZONEID
;
210 return (void *) ((byte
*)base
+ sizeof(memblock_t
));
215 ========================
217 ========================
219 void Z_Print (memzone_t
*zone
)
223 Con_Printf ("zone size: %i location: %p\n",mainzone
->size
,mainzone
);
225 for (block
= zone
->blocklist
.next
; ; block
= block
->next
)
227 Con_Printf ("block:%p size:%7i tag:%3i\n",
228 block
, block
->size
, block
->tag
);
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");
243 ========================
245 ========================
247 void Z_CheckHeap (void)
251 for (block
= mainzone
->blocklist
.next
; ; block
= block
->next
)
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");
264 //============================================================================
266 #define HUNK_SENTINAL 0x1df001ed
271 int size
; // including sizeof(hunk_t), -1 = not allocated
281 qboolean hunk_tempactive
;
284 void R_FreeTextures (void);
290 Run consistancy and sentinal trahing checks
293 void Hunk_Check (void)
297 for (h
= (hunk_t
*)hunk_base
; (byte
*)h
!= hunk_base
+ hunk_low_used
; )
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
);
311 If "all" is specified, every single allocation is printed.
312 Otherwise, allocations with the same name will be totaled up before printing.
315 void Hunk_Print (qboolean all
)
317 hunk_t
*h
, *next
, *endlow
, *starthigh
, *endhigh
;
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
);
332 Con_Printf (" :%8i total hunk size\n", hunk_size
);
333 Con_Printf ("-------------------------\n");
338 // skip to the high hunk if done with low hunk
342 Con_Printf ("-------------------------\n");
343 Con_Printf (" :%8i REMAINING\n", hunk_size
- hunk_low_used
- hunk_high_used
);
344 Con_Printf ("-------------------------\n");
349 // if totally done, break
355 // run consistancy checks
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");
362 next
= (hunk_t
*)((byte
*)h
+h
->size
);
368 // print the single block
370 memcpy (name
, h
->name
, 8);
372 Con_Printf ("%8p :%8i %8s\n",h
, h
->size
, name
);
377 if (next
== endlow
|| next
== endhigh
||
378 strncmp (h
->name
, next
->name
, 8) )
381 Con_Printf (" :%8i %8s (TOTAL)\n",sum
, name
);
389 Con_Printf ("-------------------------\n");
390 Con_Printf ("%8i total blocks\n", totalblocks
);
399 void *Hunk_AllocName (int size
, char *name
)
408 Sys_Error ("Hunk_Alloc: bad size: %i", size
);
410 size
= sizeof(hunk_t
) + ((size
+15)&~15);
412 if (hunk_size
- hunk_low_used
- hunk_high_used
< size
)
413 Sys_Error ("Hunk_Alloc: failed on %i bytes",size
);
415 h
= (hunk_t
*)(hunk_base
+ hunk_low_used
);
416 hunk_low_used
+= size
;
418 Cache_FreeLow (hunk_low_used
);
423 h
->sentinal
= HUNK_SENTINAL
;
424 Q_strncpy (h
->name
, name
, 8);
426 return (void *)(h
+1);
434 void *Hunk_Alloc (int size
)
436 return Hunk_AllocName (size
, "unknown");
439 int Hunk_LowMark (void)
441 return hunk_low_used
;
444 void Hunk_FreeToLowMark (int mark
)
446 if (mark
< 0 || mark
> hunk_low_used
)
447 Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark
);
448 memset (hunk_base
+ mark
, 0, hunk_low_used
- mark
);
449 hunk_low_used
= mark
;
452 int Hunk_HighMark (void)
456 hunk_tempactive
= false;
457 Hunk_FreeToHighMark (hunk_tempmark
);
460 return hunk_high_used
;
463 void Hunk_FreeToHighMark (int mark
)
467 hunk_tempactive
= false;
468 Hunk_FreeToHighMark (hunk_tempmark
);
470 if (mark
< 0 || mark
> hunk_high_used
)
471 Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark
);
472 memset (hunk_base
+ hunk_size
- hunk_high_used
, 0, hunk_high_used
- mark
);
473 hunk_high_used
= mark
;
482 void *Hunk_HighAllocName (int size
, char *name
)
487 Sys_Error ("Hunk_HighAllocName: bad size: %i", size
);
491 Hunk_FreeToHighMark (hunk_tempmark
);
492 hunk_tempactive
= false;
499 size
= sizeof(hunk_t
) + ((size
+15)&~15);
501 if (hunk_size
- hunk_low_used
- hunk_high_used
< size
)
503 Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size
);
507 hunk_high_used
+= size
;
508 Cache_FreeHigh (hunk_high_used
);
510 h
= (hunk_t
*)(hunk_base
+ hunk_size
- hunk_high_used
);
514 h
->sentinal
= HUNK_SENTINAL
;
515 Q_strncpy (h
->name
, name
, 8);
517 return (void *)(h
+1);
525 Return space from the top of the hunk
528 void *Hunk_TempAlloc (int size
)
532 size
= (size
+15)&~15;
536 Hunk_FreeToHighMark (hunk_tempmark
);
537 hunk_tempactive
= false;
540 hunk_tempmark
= Hunk_HighMark ();
542 buf
= Hunk_HighAllocName (size
, "temp");
544 hunk_tempactive
= true;
550 ===============================================================================
554 ===============================================================================
557 typedef struct cache_system_s
559 int size
; // including this header
562 struct cache_system_s
*prev
, *next
;
563 struct cache_system_s
*lru_prev
, *lru_next
; // for LRU flushing
566 cache_system_t
*Cache_TryAlloc (int size
, qboolean nobottom
);
568 cache_system_t cache_head
;
575 void Cache_Move ( cache_system_t
*c
)
579 // we are clearing up space at the bottom, so only allocate it late
580 new = Cache_TryAlloc (c
->size
, true);
583 // Con_Printf ("cache_move ok\n");
585 Q_memcpy ( new+1, c
+1, c
->size
- sizeof(cache_system_t
) );
587 Q_memcpy (new->name
, c
->name
, sizeof(new->name
));
588 Cache_Free (c
->user
);
589 new->user
->data
= (void *)(new+1);
593 // Con_Printf ("cache_move failed\n");
595 Cache_Free (c
->user
); // tough luck...
603 Throw things out until the hunk can be expanded to the given point
606 void Cache_FreeLow (int new_low_hunk
)
613 if (c
== &cache_head
)
614 return; // nothing in cache at all
615 if ((byte
*)c
>= hunk_base
+ new_low_hunk
)
616 return; // there is space to grow the hunk
617 Cache_Move ( c
); // reclaim the space
625 Throw things out until the hunk can be expanded to the given point
628 void Cache_FreeHigh (int new_high_hunk
)
630 cache_system_t
*c
, *prev
;
636 if (c
== &cache_head
)
637 return; // nothing in cache at all
638 if ( (byte
*)c
+ c
->size
<= hunk_base
+ hunk_size
- new_high_hunk
)
639 return; // there is space to grow the hunk
641 Cache_Free (c
->user
); // didn't move out of the way
644 Cache_Move (c
); // try to move it
650 void Cache_UnlinkLRU (cache_system_t
*cs
)
652 if (!cs
->lru_next
|| !cs
->lru_prev
)
653 Sys_Error ("Cache_UnlinkLRU: NULL link");
655 cs
->lru_next
->lru_prev
= cs
->lru_prev
;
656 cs
->lru_prev
->lru_next
= cs
->lru_next
;
658 cs
->lru_prev
= cs
->lru_next
= NULL
;
661 void Cache_MakeLRU (cache_system_t
*cs
)
663 if (cs
->lru_next
|| cs
->lru_prev
)
664 Sys_Error ("Cache_MakeLRU: active link");
666 cache_head
.lru_next
->lru_prev
= cs
;
667 cs
->lru_next
= cache_head
.lru_next
;
668 cs
->lru_prev
= &cache_head
;
669 cache_head
.lru_next
= cs
;
676 Looks for a free block of memory between the high and low hunk marks
677 Size should already include the header and padding
680 cache_system_t
*Cache_TryAlloc (int size
, qboolean nobottom
)
682 cache_system_t
*cs
, *new;
684 // is the cache completely empty?
686 if (!nobottom
&& cache_head
.prev
== &cache_head
)
688 if (hunk_size
- hunk_high_used
- hunk_low_used
< size
)
689 Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size
);
691 new = (cache_system_t
*) (hunk_base
+ hunk_low_used
);
692 memset (new, 0, sizeof(*new));
695 cache_head
.prev
= cache_head
.next
= new;
696 new->prev
= new->next
= &cache_head
;
702 // search from the bottom up for space
704 new = (cache_system_t
*) (hunk_base
+ hunk_low_used
);
705 cs
= cache_head
.next
;
709 if (!nobottom
|| cs
!= cache_head
.next
)
711 if ( (byte
*)cs
- (byte
*)new >= size
)
713 memset (new, 0, sizeof(*new));
717 new->prev
= cs
->prev
;
718 cs
->prev
->next
= new;
728 new = (cache_system_t
*)((byte
*)cs
+ cs
->size
);
731 } while (cs
!= &cache_head
);
733 // try to allocate one at the very end
734 if ( hunk_base
+ hunk_size
- hunk_high_used
- (byte
*)new >= size
)
736 memset (new, 0, sizeof(*new));
739 new->next
= &cache_head
;
740 new->prev
= cache_head
.prev
;
741 cache_head
.prev
->next
= new;
742 cache_head
.prev
= new;
749 return NULL
; // couldn't allocate
756 Throw everything out, so new data will be demand cached
759 void Cache_Flush (void)
761 while (cache_head
.next
!= &cache_head
)
762 Cache_Free ( cache_head
.next
->user
); // reclaim the space
772 void Cache_Print (void)
776 for (cd
= cache_head
.next
; cd
!= &cache_head
; cd
= cd
->next
)
778 Con_Printf ("%8i : %s\n", cd
->size
, cd
->name
);
788 void Cache_Report (void)
790 Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size
- hunk_high_used
- hunk_low_used
) / (float)(1024*1024) );
799 void Cache_Compact (void)
809 void Cache_Init (void)
811 cache_head
.next
= cache_head
.prev
= &cache_head
;
812 cache_head
.lru_next
= cache_head
.lru_prev
= &cache_head
;
814 Cmd_AddCommand ("flush", Cache_Flush
);
821 Frees the memory and removes it from the LRU list
824 void Cache_Free (cache_user_t
*c
)
829 Sys_Error ("Cache_Free: not allocated");
831 cs
= ((cache_system_t
*)c
->data
) - 1;
833 cs
->prev
->next
= cs
->next
;
834 cs
->next
->prev
= cs
->prev
;
835 cs
->next
= cs
->prev
= NULL
;
839 Cache_UnlinkLRU (cs
);
849 void *Cache_Check (cache_user_t
*c
)
856 cs
= ((cache_system_t
*)c
->data
) - 1;
858 // move to head of LRU
859 Cache_UnlinkLRU (cs
);
871 void *Cache_Alloc (cache_user_t
*c
, int size
, char *name
)
876 Sys_Error ("Cache_Alloc: allready allocated");
879 Sys_Error ("Cache_Alloc: size %i", size
);
881 size
= (size
+ sizeof(cache_system_t
) + 15) & ~15;
883 // find memory for it
886 cs
= Cache_TryAlloc (size
, false);
889 strncpy (cs
->name
, name
, sizeof(cs
->name
)-1);
890 c
->data
= (void *)(cs
+1);
895 // free the least recently used cahedat
896 if (cache_head
.lru_prev
== &cache_head
)
897 Sys_Error ("Cache_Alloc: out of memory");
898 // not enough memory at all
899 Cache_Free ( cache_head
.lru_prev
->user
);
902 return Cache_Check (c
);
905 //============================================================================
909 ========================
911 ========================
913 void Memory_Init (void *buf
, int size
)
916 int zonesize
= DYNAMIC_SIZE
;
924 p
= COM_CheckParm ("-zone");
928 zonesize
= Q_atoi (com_argv
[p
+1]) * 1024;
930 Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
932 mainzone
= Hunk_AllocName (zonesize
, "zone" );
933 Z_ClearZone (mainzone
, zonesize
);