7 ==============================================================================
11 There is never any space between memblocks, and there will never be two
12 contiguous free memblocks.
14 The rover can be left pointing at a non-empty block
16 It is of no value to free a cachable block, because it will get overwritten
17 automatically if needed
19 ==============================================================================
22 #define ZONEID 0x1d4a11
26 int size; // total bytes malloced, including header
27 memblock_t blocklist; // start / end cap for linked list
31 boolean MallocFailureOk;
35 ========================
39 ========================
42 void Z_ClearZone (memzone_t *zone)
46 // set the entire zone to one free block
48 zone->blocklist.next = zone->blocklist.prev = block =
49 (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
50 zone->blocklist.user = (void *)zone;
51 zone->blocklist.tag = PU_STATIC;
54 block->prev = block->next = &zone->blocklist;
55 block->user = NULL; // free block
56 block->size = zone->size - sizeof(memzone_t);
61 ========================
65 ========================
73 MallocFailureOk = false;
74 mainzone = (memzone_t *)I_ZoneBase (&size);
75 mainzone->size = size;
77 // set the entire zone to one free block
79 mainzone->blocklist.next = mainzone->blocklist.prev = block =
80 (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
81 mainzone->blocklist.user = (void *)mainzone;
82 mainzone->blocklist.tag = PU_STATIC;
83 mainzone->rover = block;
85 block->prev = block->next = &mainzone->blocklist;
86 block->user = NULL; // free block
87 block->size = mainzone->size - sizeof(memzone_t);
92 ========================
96 ========================
99 void Z_Free (void *ptr)
101 memblock_t *block, *other;
103 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
104 if (block->id != ZONEID)
105 I_Error ("Z_Free: freed a pointer without ZONEID");
107 if (block->user > (void **)0x100) // smaller values are not pointers
108 *block->user = 0; // clear the user's mark
109 block->user = NULL; // mark as free
115 { // merge with previous free block
116 other->size += block->size;
117 other->next = block->next;
118 other->next->prev = other;
119 if (block == mainzone->rover)
120 mainzone->rover = other;
126 { // merge the next free block onto the end
127 block->size += other->size;
128 block->next = other->next;
129 block->next->prev = block;
130 if (other == mainzone->rover)
131 mainzone->rover = block;
137 ========================
141 = You can pass a NULL user if the tag is < PU_PURGELEVEL
142 ========================
145 #define MINFRAGMENT 64
147 void *Z_Malloc (int size, int tag, void *user)
150 memblock_t *start, *rover, *new, *base;
153 // scan through the block list looking for the first free block
154 // of sufficient size, throwing out any purgable blocks along the way
156 size += sizeof(memblock_t); // account for size of block header
160 // if there is a free block behind the rover, back up over them
162 base = mainzone->rover;
163 if (!base->prev->user)
172 { // Scanned all the way around the list
173 if(MallocFailureOk == true)
179 I_Error("Z_Malloc: failed on allocation of %i bytes", size);
184 if (rover->tag < PU_PURGELEVEL)
185 // hit a block that can't be purged, so move base past it
186 base = rover = rover->next;
189 // free the rover block (adding the size to base)
190 base = base->prev; // the rover can be the base block
191 Z_Free ((byte *)rover+sizeof(memblock_t));
198 } while (base->user || base->size < size);
201 // found a block big enough
203 extra = base->size - size;
204 if (extra > MINFRAGMENT)
205 { // there will be a free fragment after the allocated block
206 new = (memblock_t *) ((byte *)base + size );
208 new->user = NULL; // free block
211 new->next = base->next;
212 new->next->prev = new;
219 base->user = user; // mark as an in use block
220 *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
224 if (tag >= PU_PURGELEVEL)
225 I_Error ("Z_Malloc: an owner is required for purgable blocks");
226 base->user = (void *)2; // mark as in use, but unowned
230 mainzone->rover = base->next; // next allocation will start looking here
233 return (void *) ((byte *)base + sizeof(memblock_t));
238 ========================
242 ========================
245 void Z_FreeTags (int lowtag, int hightag)
247 memblock_t *block, *next;
249 for (block = mainzone->blocklist.next ; block != &mainzone->blocklist
252 next = block->next; // get link before freeing
254 continue; // free block
255 if (block->tag >= lowtag && block->tag <= hightag)
256 Z_Free ( (byte *)block+sizeof(memblock_t));
261 ========================
265 ========================
268 void Z_DumpHeap (int lowtag, int hightag)
272 printf ("zone size: %i location: %p\n",mainzone->size,mainzone);
273 printf ("tag range: %i to %i\n",lowtag, hightag);
275 for (block = mainzone->blocklist.next ; ; block = block->next)
277 if (block->tag >= lowtag && block->tag <= hightag)
278 printf ("block:%p size:%7i user:%p tag:%3i\n",
279 block, block->size, block->user, block->tag);
281 if (block->next == &mainzone->blocklist)
282 break; // all blocks have been hit
283 if ( (byte *)block + block->size != (byte *)block->next)
284 printf ("ERROR: block size does not touch the next block\n");
285 if ( block->next->prev != block)
286 printf ("ERROR: next block doesn't have proper back link\n");
287 if (!block->user && !block->next->user)
288 printf ("ERROR: two consecutive free blocks\n");
293 ========================
297 ========================
300 void Z_FileDumpHeap (FILE *f)
304 fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone);
306 for (block = mainzone->blocklist.next ; ; block = block->next)
308 fprintf (f,"block:%p size:%7i user:%p tag:%3i\n",
309 block, block->size, block->user, block->tag);
311 if (block->next == &mainzone->blocklist)
312 break; // all blocks have been hit
313 if ( (byte *)block + block->size != (byte *)block->next)
314 fprintf (f,"ERROR: block size does not touch the next block\n");
315 if ( block->next->prev != block)
316 fprintf (f,"ERROR: next block doesn't have proper back link\n");
317 if (!block->user && !block->next->user)
318 fprintf (f,"ERROR: two consecutive free blocks\n");
323 ========================
327 ========================
330 void Z_CheckHeap (void)
334 for (block = mainzone->blocklist.next ; ; block = block->next)
336 if (block->next == &mainzone->blocklist)
337 break; // all blocks have been hit
338 if ( (byte *)block + block->size != (byte *)block->next)
339 I_Error ("Z_CheckHeap: block size does not touch the next block\n");
340 if ( block->next->prev != block)
341 I_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
342 if (!block->user && !block->next->user)
343 I_Error ("Z_CheckHeap: two consecutive free blocks\n");
349 ========================
353 ========================
356 void Z_ChangeTag2 (void *ptr, int tag)
360 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
361 if (block->id != ZONEID)
362 I_Error ("Z_ChangeTag: freed a pointer without ZONEID");
363 if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100)
364 I_Error ("Z_ChangeTag: an owner is required for purgable blocks");
370 ========================
374 ========================
377 int Z_FreeMemory (void)
383 for (block = mainzone->blocklist.next ; block != &mainzone->blocklist
384 ; block = block->next)
385 if (!block->user || block->tag >= PU_PURGELEVEL)
390 void Z_Shutdown (void)
392 free ((void *) mainzone);