2 //**************************************************************************
4 //** z_zone.c : Heretic 2 : Raven Software, Corp.
11 //**************************************************************************
17 ==============================================================================
19 ZONE MEMORY ALLOCATION
21 There is never any space between memblocks, and there will never be two
22 contiguous free memblocks.
24 The rover can be left pointing at a non-empty block
26 It is of no value to free a cachable block, because it will get overwritten
27 automatically if needed
29 ==============================================================================
32 #define ZONEID 0x1d4a11
37 int size; // total bytes malloced, including header
38 memblock_t blocklist; // start / end cap for linked list
45 ========================
49 ========================
53 void Z_ClearZone (memzone_t *zone)
57 // set the entire zone to one free block
59 zone->blocklist.next = zone->blocklist.prev = block =
60 (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
61 zone->blocklist.user = (void *)zone;
62 zone->blocklist.tag = PU_STATIC;
65 block->prev = block->next = &zone->blocklist;
66 block->user = NULL; // free block
67 block->size = zone->size - sizeof(memzone_t);
73 ========================
77 ========================
85 mainzone = (memzone_t *)I_ZoneBase (&size);
86 mainzone->size = size;
88 // set the entire zone to one free block
90 mainzone->blocklist.next = mainzone->blocklist.prev = block =
91 (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
92 mainzone->blocklist.user = (void *)mainzone;
93 mainzone->blocklist.tag = PU_STATIC;
94 mainzone->rover = block;
96 block->prev = block->next = &mainzone->blocklist;
97 block->user = NULL; // free block
98 block->size = mainzone->size - sizeof(memzone_t);
103 ========================
107 ========================
110 void Z_Free (void *ptr)
112 memblock_t *block, *other;
114 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
115 if (block->id != ZONEID)
116 I_Error ("Z_Free: freed a pointer without ZONEID");
118 if (block->user > (void **)0x100) // smaller values are not pointers
119 *block->user = 0; // clear the user's mark
120 block->user = NULL; // mark as free
126 { // merge with previous free block
127 other->size += block->size;
128 other->next = block->next;
129 other->next->prev = other;
130 if (block == mainzone->rover)
131 mainzone->rover = other;
137 { // merge the next free block onto the end
138 block->size += other->size;
139 block->next = other->next;
140 block->next->prev = block;
141 if (other == mainzone->rover)
142 mainzone->rover = block;
148 ========================
152 = You can pass a NULL user if the tag is < PU_PURGELEVEL
153 ========================
156 #define MINFRAGMENT 64
158 void *Z_Malloc (int size, int tag, void *user)
161 memblock_t *start, *rover, *new, *base;
164 // scan through the block list looking for the first free block
165 // of sufficient size, throwing out any purgable blocks along the way
167 size += sizeof(memblock_t); // account for size of block header
171 // if there is a free block behind the rover, back up over them
173 base = mainzone->rover;
174 if (!base->prev->user)
182 if (rover == start) // scaned all the way around the list
183 I_Error ("Z_Malloc: failed on allocation of %i bytes",size);
186 if (rover->tag < PU_PURGELEVEL)
187 // hit a block that can't be purged, so move base past it
188 base = rover = rover->next;
191 // free the rover block (adding the size to base)
192 base = base->prev; // the rover can be the base block
193 Z_Free ((byte *)rover+sizeof(memblock_t));
200 } while (base->user || base->size < size);
203 // found a block big enough
205 extra = base->size - size;
206 if (extra > MINFRAGMENT)
207 { // there will be a free fragment after the allocated block
208 new = (memblock_t *) ((byte *)base + size );
210 new->user = NULL; // free block
213 new->next = base->next;
214 new->next->prev = new;
221 base->user = user; // mark as an in use block
222 *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
226 if (tag >= PU_PURGELEVEL)
227 I_Error ("Z_Malloc: an owner is required for purgable blocks");
228 base->user = (void *)2; // mark as in use, but unowned
232 mainzone->rover = base->next; // next allocation will start looking here
235 return (void *) ((byte *)base + sizeof(memblock_t));
240 ========================
244 ========================
247 void Z_FreeTags (int lowtag, int hightag)
249 memblock_t *block, *next;
251 for (block = mainzone->blocklist.next ; block != &mainzone->blocklist
254 next = block->next; // get link before freeing
256 continue; // free block
257 if (block->tag >= lowtag && block->tag <= hightag)
258 Z_Free ( (byte *)block+sizeof(memblock_t));
263 ========================
267 ========================
271 void Z_DumpHeap (int lowtag, int hightag)
275 printf ("zone size: %i location: %p\n",mainzone->size,mainzone);
276 printf ("tag range: %i to %i\n",lowtag, hightag);
278 for (block = mainzone->blocklist.next ; ; block = block->next)
280 if (block->tag >= lowtag && block->tag <= hightag)
281 printf ("block:%p size:%7i user:%p tag:%3i\n",
282 block, block->size, block->user, block->tag);
284 if (block->next == &mainzone->blocklist)
285 break; // all blocks have been hit
286 if ( (byte *)block + block->size != (byte *)block->next)
287 printf ("ERROR: block size does not touch the next block\n");
288 if ( block->next->prev != block)
289 printf ("ERROR: next block doesn't have proper back link\n");
290 if (!block->user && !block->next->user)
291 printf ("ERROR: two consecutive free blocks\n");
297 ========================
301 ========================
305 void Z_FileDumpHeap (FILE *f)
309 fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone);
311 for (block = mainzone->blocklist.next ; ; block = block->next)
313 fprintf (f,"block:%p size:%7i user:%p tag:%3i\n",
314 block, block->size, block->user, block->tag);
316 if (block->next == &mainzone->blocklist)
317 break; // all blocks have been hit
318 if ( (byte *)block + block->size != (byte *)block->next)
319 fprintf (f,"ERROR: block size does not touch the next block\n");
320 if ( block->next->prev != block)
321 fprintf (f,"ERROR: next block doesn't have proper back link\n");
322 if (!block->user && !block->next->user)
323 fprintf (f,"ERROR: two consecutive free blocks\n");
329 ========================
333 ========================
336 void Z_CheckHeap (void)
340 for (block = mainzone->blocklist.next ; ; block = block->next)
342 if (block->next == &mainzone->blocklist)
343 break; // all blocks have been hit
344 if ( (byte *)block + block->size != (byte *)block->next)
345 I_Error ("Z_CheckHeap: block size does not touch the next block\n");
346 if ( block->next->prev != block)
347 I_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
348 if (!block->user && !block->next->user)
349 I_Error ("Z_CheckHeap: two consecutive free blocks\n");
355 ========================
359 ========================
362 void Z_ChangeTag2 (void *ptr, int tag)
366 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
367 if (block->id != ZONEID)
368 I_Error ("Z_ChangeTag: freed a pointer without ZONEID");
369 if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100)
370 I_Error ("Z_ChangeTag: an owner is required for purgable blocks");
376 ========================
380 ========================
384 int Z_FreeMemory (void)
390 for (block = mainzone->blocklist.next ; block != &mainzone->blocklist
391 ; block = block->next)
392 if (!block->user || block->tag >= PU_PURGELEVEL)