1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
20 // Zone Memory Allocation. Neat.
22 //-----------------------------------------------------------------------------
25 rcsid[] = "$Id: z_zone.c,v 1.4 1997/02/03 16:47:58 b1 Exp $";
33 // ZONE MEMORY ALLOCATION
35 // There is never any space between memblocks,
36 // and there will never be two contiguous free memblocks.
37 // The rover can be left pointing at a non-empty block.
39 // It is of no value to free a cachable block,
40 // because it will get overwritten automatically if needed.
43 #define ZONEID 0x1d4a11
48 // total bytes malloced, including header
51 // start / end cap for linked list
67 void Z_ClearZone (memzone_t* zone)
71 // set the entire zone to one free block
72 zone->blocklist.next =
73 zone->blocklist.prev =
74 block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
76 zone->blocklist.user = (void *)zone;
77 zone->blocklist.tag = PU_STATIC;
80 block->prev = block->next = &zone->blocklist;
82 // NULL indicates a free block.
85 block->size = zone->size - sizeof(memzone_t);
98 mainzone = (memzone_t *)I_ZoneBase (&size);
99 mainzone->size = size;
101 // set the entire zone to one free block
102 mainzone->blocklist.next =
103 mainzone->blocklist.prev =
104 block = (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
106 mainzone->blocklist.user = (void *)mainzone;
107 mainzone->blocklist.tag = PU_STATIC;
108 mainzone->rover = block;
110 block->prev = block->next = &mainzone->blocklist;
112 // NULL indicates a free block.
115 block->size = mainzone->size - sizeof(memzone_t);
122 void Z_Free (void* ptr)
127 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
129 if (block->id != ZONEID)
130 I_Error ("Z_Free: freed a pointer without ZONEID");
132 if (block->user > (void **)0x100)
134 // smaller values are not pointers
135 // Note: OS-dependend?
137 // clear the user's mark
150 // merge with previous free block
151 other->size += block->size;
152 other->next = block->next;
153 other->next->prev = other;
155 if (block == mainzone->rover)
156 mainzone->rover = other;
164 // merge the next free block onto the end
165 block->size += other->size;
166 block->next = other->next;
167 block->next->prev = block;
169 if (other == mainzone->rover)
170 mainzone->rover = block;
178 // You can pass a NULL user if the tag is < PU_PURGELEVEL.
180 #define MINFRAGMENT 64
191 memblock_t* newblock;
194 size = (size + 7) & ~7;
196 // scan through the block list,
197 // looking for the first free block
198 // of sufficient size,
199 // throwing out any purgable blocks along the way.
201 // account for size of block header
202 size += sizeof(memblock_t);
204 // if there is a free block behind the rover,
206 base = mainzone->rover;
208 if (!base->prev->user)
218 // scanned all the way around the list
219 I_Error ("Z_Malloc: failed on allocation of %i bytes", size);
224 if (rover->tag < PU_PURGELEVEL)
226 // hit a block that can't be purged,
227 // so move base past it
228 base = rover = rover->next;
232 // free the rover block (adding the size to base)
234 // the rover can be the base block
236 Z_Free ((byte *)rover+sizeof(memblock_t));
243 } while (base->user || base->size < size);
246 // found a block big enough
247 extra = base->size - size;
249 if (extra > MINFRAGMENT)
251 // there will be a free fragment after the allocated block
252 newblock = (memblock_t *) ((byte *)base + size );
253 newblock->size = extra;
255 // NULL indicates free block.
256 newblock->user = NULL;
258 newblock->prev = base;
259 newblock->next = base->next;
260 newblock->next->prev = newblock;
262 base->next = newblock;
268 // mark as an in use block
270 *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
274 if (tag >= PU_PURGELEVEL)
275 I_Error ("Z_Malloc: an owner is required for purgable blocks");
277 // mark as in use, but unowned
278 base->user = (void *)2;
282 // next allocation will start looking here
283 mainzone->rover = base->next;
287 return (void *) ((byte *)base + sizeof(memblock_t));
303 for (block = mainzone->blocklist.next ;
304 block != &mainzone->blocklist ;
307 // get link before freeing
314 if (block->tag >= lowtag && block->tag <= hightag)
315 Z_Free ( (byte *)block+sizeof(memblock_t));
323 // Note: TFileDumpHeap( stdout ) ?
332 printf ("zone size: %i location: %p\n",
333 mainzone->size,mainzone);
335 printf ("tag range: %i to %i\n",
338 for (block = mainzone->blocklist.next ; ; block = block->next)
340 if (block->tag >= lowtag && block->tag <= hightag)
341 printf ("block:%p size:%7i user:%p tag:%3i\n",
342 block, block->size, block->user, block->tag);
344 if (block->next == &mainzone->blocklist)
346 // all blocks have been hit
350 if ( (byte *)block + block->size != (byte *)block->next)
351 printf ("ERROR: block size does not touch the next block\n");
353 if ( block->next->prev != block)
354 printf ("ERROR: next block doesn't have proper back link\n");
356 if (!block->user && !block->next->user)
357 printf ("ERROR: two consecutive free blocks\n");
365 void Z_FileDumpHeap (FILE* f)
369 fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone);
371 for (block = mainzone->blocklist.next ; ; block = block->next)
373 fprintf (f,"block:%p size:%7i user:%p tag:%3i\n",
374 block, block->size, block->user, block->tag);
376 if (block->next == &mainzone->blocklist)
378 // all blocks have been hit
382 if ( (byte *)block + block->size != (byte *)block->next)
383 fprintf (f,"ERROR: block size does not touch the next block\n");
385 if ( block->next->prev != block)
386 fprintf (f,"ERROR: next block doesn't have proper back link\n");
388 if (!block->user && !block->next->user)
389 fprintf (f,"ERROR: two consecutive free blocks\n");
398 void Z_CheckHeap (void)
402 for (block = mainzone->blocklist.next ; ; block = block->next)
404 if (block->next == &mainzone->blocklist)
406 // all blocks have been hit
410 if ( (byte *)block + block->size != (byte *)block->next)
411 I_Error ("Z_CheckHeap: block size does not touch the next block\n");
413 if ( block->next->prev != block)
414 I_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
416 if (!block->user && !block->next->user)
417 I_Error ("Z_CheckHeap: two consecutive free blocks\n");
434 block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
436 if (block->id != ZONEID)
437 I_Error ("Z_ChangeTag: freed a pointer without ZONEID");
439 if (tag >= PU_PURGELEVEL && (uintptr)block->user < 0x100)
440 I_Error ("Z_ChangeTag: an owner is required for purgable blocks");
450 int Z_FreeMemory (void)
457 for (block = mainzone->blocklist.next ;
458 block != &mainzone->blocklist;
461 if (!block->user || block->tag >= PU_PURGELEVEL)