ref: 15f26fadf4315e0336fd0e2c68e7162c29bbffcb
dir: /src/heretic/z_zone.c/
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//-----------------------------------------------------------------------------
// Z_zone.c
#include <stdlib.h>
#include "doomdef.h"
/*
==============================================================================
ZONE MEMORY ALLOCATION
There is never any space between memblocks, and there will never be two
contiguous free memblocks.
The rover can be left pointing at a non-empty block
It is of no value to free a cachable block, because it will get overwritten
automatically if needed
==============================================================================
*/
#define ZONEID 0x1d4a11
typedef struct
{
int size; // total bytes malloced, including header
memblock_t blocklist; // start / end cap for linked list
memblock_t *rover;
} memzone_t;
boolean MallocFailureOk;
memzone_t *mainzone;
/*
========================
=
= Z_ClearZone
=
========================
*/
void Z_ClearZone(memzone_t * zone)
{
memblock_t *block;
// set the entire zone to one free block
zone->blocklist.next = zone->blocklist.prev = block =
(memblock_t *) ((byte *) zone + sizeof(memzone_t));
zone->blocklist.user = (void *) zone;
zone->blocklist.tag = PU_STATIC;
zone->rover = block;
block->prev = block->next = &zone->blocklist;
block->user = NULL; // free block
block->size = zone->size - sizeof(memzone_t);
}
/*
========================
=
= Z_Init
=
========================
*/
void Z_Init(void)
{
memblock_t *block;
int size;
MallocFailureOk = false;
mainzone = (memzone_t *) I_ZoneBase(&size);
mainzone->size = size;
// set the entire zone to one free block
mainzone->blocklist.next = mainzone->blocklist.prev = block =
(memblock_t *) ((byte *) mainzone + sizeof(memzone_t));
mainzone->blocklist.user = (void *) mainzone;
mainzone->blocklist.tag = PU_STATIC;
mainzone->rover = block;
block->prev = block->next = &mainzone->blocklist;
block->user = NULL; // free block
block->size = mainzone->size - sizeof(memzone_t);
}
/*
========================
=
= Z_Free
=
========================
*/
void Z_Free(void *ptr)
{
memblock_t *block, *other;
block = (memblock_t *) ((byte *) ptr - sizeof(memblock_t));
if (block->id != ZONEID)
I_Error("Z_Free: freed a pointer without ZONEID");
if (block->user > (void **) 0x100) // smaller values are not pointers
*block->user = 0; // clear the user's mark
block->user = NULL; // mark as free
block->tag = 0;
block->id = 0;
other = block->prev;
if (!other->user)
{ // merge with previous free block
other->size += block->size;
other->next = block->next;
other->next->prev = other;
if (block == mainzone->rover)
mainzone->rover = other;
block = other;
}
other = block->next;
if (!other->user)
{ // merge the next free block onto the end
block->size += other->size;
block->next = other->next;
block->next->prev = block;
if (other == mainzone->rover)
mainzone->rover = block;
}
}
/*
========================
=
= Z_Malloc
=
= You can pass a NULL user if the tag is < PU_PURGELEVEL
========================
*/
#define MINFRAGMENT 64
void *Z_Malloc(int size, int tag, void *user)
{
int extra;
memblock_t *start, *rover, *new, *base;
//
// scan through the block list looking for the first free block
// of sufficient size, throwing out any purgable blocks along the way
//
size += sizeof(memblock_t); // account for size of block header
//
// if there is a free block behind the rover, back up over them
//
base = mainzone->rover;
if (!base->prev->user)
base = base->prev;
rover = base;
start = base->prev;
do
{
if (rover == start)
{ // Scanned all the way around the list
if (MallocFailureOk == true)
{
return NULL;
}
else
{
I_Error("Z_Malloc: failed on allocation of %i bytes", size);
}
}
if (rover->user)
{
if (rover->tag < PU_PURGELEVEL)
// hit a block that can't be purged, so move base past it
base = rover = rover->next;
else
{
// free the rover block (adding the size to base)
base = base->prev; // the rover can be the base block
Z_Free((byte *) rover + sizeof(memblock_t));
base = base->next;
rover = base->next;
}
}
else
rover = rover->next;
}
while (base->user || base->size < size);
//
// found a block big enough
//
extra = base->size - size;
if (extra > MINFRAGMENT)
{ // there will be a free fragment after the allocated block
new = (memblock_t *) ((byte *) base + size);
new->size = extra;
new->user = NULL; // free block
new->tag = 0;
new->prev = base;
new->next = base->next;
new->next->prev = new;
base->next = new;
base->size = size;
}
if (user)
{
base->user = user; // mark as an in use block
*(void **) user = (void *) ((byte *) base + sizeof(memblock_t));
}
else
{
if (tag >= PU_PURGELEVEL)
I_Error("Z_Malloc: an owner is required for purgable blocks");
base->user = (void *) 2; // mark as in use, but unowned
}
base->tag = tag;
mainzone->rover = base->next; // next allocation will start looking here
base->id = ZONEID;
return (void *) ((byte *) base + sizeof(memblock_t));
}
/*
========================
=
= Z_FreeTags
=
========================
*/
void Z_FreeTags(int lowtag, int hightag)
{
memblock_t *block, *next;
for (block = mainzone->blocklist.next; block != &mainzone->blocklist;
block = next)
{
next = block->next; // get link before freeing
if (!block->user)
continue; // free block
if (block->tag >= lowtag && block->tag <= hightag)
Z_Free((byte *) block + sizeof(memblock_t));
}
}
/*
========================
=
= Z_DumpHeap
=
========================
*/
void Z_DumpHeap(int lowtag, int hightag)
{
memblock_t *block;
printf("zone size: %i location: %p\n", mainzone->size, mainzone);
printf("tag range: %i to %i\n", lowtag, hightag);
for (block = mainzone->blocklist.next;; block = block->next)
{
if (block->tag >= lowtag && block->tag <= hightag)
printf("block:%p size:%7i user:%p tag:%3i\n",
block, block->size, block->user, block->tag);
if (block->next == &mainzone->blocklist)
break; // all blocks have been hit
if ((byte *) block + block->size != (byte *) block->next)
printf("ERROR: block size does not touch the next block\n");
if (block->next->prev != block)
printf("ERROR: next block doesn't have proper back link\n");
if (!block->user && !block->next->user)
printf("ERROR: two consecutive free blocks\n");
}
}
/*
========================
=
= Z_FileDumpHeap
=
========================
*/
void Z_FileDumpHeap(FILE * f)
{
memblock_t *block;
fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone);
for (block = mainzone->blocklist.next;; block = block->next)
{
fprintf(f, "block:%p size:%7i user:%p tag:%3i\n",
block, block->size, block->user, block->tag);
if (block->next == &mainzone->blocklist)
break; // all blocks have been hit
if ((byte *) block + block->size != (byte *) block->next)
fprintf(f, "ERROR: block size does not touch the next block\n");
if (block->next->prev != block)
fprintf(f, "ERROR: next block doesn't have proper back link\n");
if (!block->user && !block->next->user)
fprintf(f, "ERROR: two consecutive free blocks\n");
}
}
/*
========================
=
= Z_CheckHeap
=
========================
*/
void Z_CheckHeap(void)
{
memblock_t *block;
for (block = mainzone->blocklist.next;; block = block->next)
{
if (block->next == &mainzone->blocklist)
break; // all blocks have been hit
if ((byte *) block + block->size != (byte *) block->next)
I_Error
("Z_CheckHeap: block size does not touch the next block\n");
if (block->next->prev != block)
I_Error
("Z_CheckHeap: next block doesn't have proper back link\n");
if (!block->user && !block->next->user)
I_Error("Z_CheckHeap: two consecutive free blocks\n");
}
}
/*
========================
=
= Z_ChangeTag
=
========================
*/
void Z_ChangeTag2(void *ptr, int tag)
{
memblock_t *block;
block = (memblock_t *) ((byte *) ptr - sizeof(memblock_t));
if (block->id != ZONEID)
I_Error("Z_ChangeTag: freed a pointer without ZONEID");
if (tag >= PU_PURGELEVEL && (unsigned) block->user < 0x100)
I_Error("Z_ChangeTag: an owner is required for purgable blocks");
block->tag = tag;
}
/*
========================
=
= Z_FreeMemory
=
========================
*/
int Z_FreeMemory(void)
{
memblock_t *block;
int free;
free = 0;
for (block = mainzone->blocklist.next; block != &mainzone->blocklist;
block = block->next)
if (!block->user || block->tag >= PU_PURGELEVEL)
free += block->size;
return free;
}