gzdoom/code/z_zone.c

538 lines
11 KiB
C
Raw Normal View History

1998-04-07 00:00:00 +00:00
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Zone Memory Allocation. Neat.
//
//-----------------------------------------------------------------------------
#include "z_zone.h"
#include "i_system.h"
#include "doomdef.h"
#include <stdlib.h>
#include <malloc.h>
1999-02-17 00:00:00 +00:00
/*
==============================================================================
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
==============================================================================
*/
1998-04-07 00:00:00 +00:00
#define ZONEID 0x1d4a11
typedef struct
{
1999-02-17 00:00:00 +00:00
size_t size; // total bytes malloced, including header
memblock_t blocklist; // start / end cap for linked list
memblock_t *rover;
1998-04-07 00:00:00 +00:00
} memzone_t;
1998-12-22 00:00:00 +00:00
static memzone_t *mainzone;
static size_t zonesize;
1998-04-07 00:00:00 +00:00
//
// Z_ClearZone
//
1999-02-17 00:00:00 +00:00
/*
1998-04-07 00:00:00 +00:00
void Z_ClearZone (memzone_t* zone)
{
1998-12-22 00:00:00 +00:00
memblock_t *block;
1998-04-07 00:00:00 +00:00
// 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;
// NULL indicates a free block.
block->user = NULL;
block->size = zone->size - sizeof(memzone_t);
}
1999-02-17 00:00:00 +00:00
*/
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
static void STACK_ARGS Z_Close (void)
1998-12-22 00:00:00 +00:00
{
free (mainzone);
mainzone = NULL;
}
1998-04-07 00:00:00 +00:00
//
// Z_Init
//
void Z_Init (void)
{
1999-02-17 00:00:00 +00:00
memblock_t *block;
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
mainzone = (memzone_t *)I_ZoneBase (&zonesize);
mainzone->size = zonesize;
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
// set the entire zone to one free block
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
mainzone->blocklist.next = mainzone->blocklist.prev = block =
(memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
1998-04-07 00:00:00 +00:00
mainzone->blocklist.user = (void *)mainzone;
mainzone->blocklist.tag = PU_STATIC;
mainzone->rover = block;
block->prev = block->next = &mainzone->blocklist;
1999-02-17 00:00:00 +00:00
block->user = NULL; // NULL indicates a free block.
1998-04-07 00:00:00 +00:00
block->size = mainzone->size - sizeof(memzone_t);
1998-12-22 00:00:00 +00:00
atexit (Z_Close);
1998-04-07 00:00:00 +00:00
}
//
// Z_Free
//
1999-02-17 00:00:00 +00:00
void Z_Free (void *ptr)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
memblock_t *block, *other;
1999-02-17 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
//#ifdef _DEBUG
// Z_CheckHeap ();
//#endif
1998-04-07 00:00:00 +00:00
block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
if (block->id != ZONEID)
1999-02-17 00:00:00 +00:00
I_FatalError ("Z_Free: freed a pointer without ZONEID");
1998-04-07 00:00:00 +00:00
if (block->user > (void **)0x100)
{
// smaller values are not pointers
1998-12-22 00:00:00 +00:00
// Note: OS-dependent?
1998-04-07 00:00:00 +00:00
// clear the user's mark
*block->user = 0;
}
// mark as free
block->user = NULL;
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;
}
1999-02-17 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
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;
}
}
1999-02-17 00:00:00 +00:00
/*
========================
=
= Z_Malloc
=
= You can pass a NULL user if the tag is < PU_PURGELEVEL
========================
*/
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
#define MINFRAGMENT 64
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
void *Z_Malloc (size_t size, int tag, void *user)
1998-04-07 00:00:00 +00:00
{
int extra;
1999-02-17 00:00:00 +00:00
memblock_t *start;
memblock_t *rover;
memblock_t *newblock;
memblock_t *base;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
//#ifdef _DEBUG
// Z_CheckHeap ();
//#endif
1998-04-07 00:00:00 +00:00
size = (size + 3) & ~3;
1999-02-17 00:00:00 +00:00
//
// 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
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
//
// if there is a free block behind the rover, back up over them
//
1998-04-07 00:00:00 +00:00
base = mainzone->rover;
if (!base->prev->user)
base = base->prev;
1999-02-17 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
rover = base;
start = base->prev;
1999-02-17 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
do
{
if (rover == start)
{
// scanned all the way around the list
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_Malloc: failed on allocation of %i bytes", size);
1998-04-07 00:00:00 +00:00
}
if (rover->user)
{
if (rover->tag < PU_PURGELEVEL)
{
1999-02-17 00:00:00 +00:00
// hit a block that can't be purged, so move past it
1998-04-07 00:00:00 +00:00
base = rover = rover->next;
}
else
{
1999-02-17 00:00:00 +00:00
// free the rover block (adding the size to base)
base = base->prev; // the rover can be the base block
1998-04-07 00:00:00 +00:00
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
newblock = (memblock_t *) ((byte *)base + size );
newblock->size = extra;
// NULL indicates free block.
newblock->user = NULL;
newblock->tag = 0;
newblock->prev = base;
newblock->next = base->next;
newblock->next->prev = newblock;
base->next = newblock;
base->size = size;
}
if (user)
{
// mark as an in use block
1999-02-17 00:00:00 +00:00
base->user = user;
1998-04-07 00:00:00 +00:00
*(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
}
else
{
if (tag >= PU_PURGELEVEL)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_Malloc: an owner is required for purgable blocks");
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
base->user = (void *)2; // mark as in use, but unowned
1998-04-07 00:00:00 +00:00
}
base->tag = tag;
1999-02-17 00:00:00 +00:00
mainzone->rover = base->next; // next allocation will start looking here
1998-04-07 00:00:00 +00:00
base->id = ZONEID;
1999-02-17 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
//#ifdef _DEBUG
// Z_CheckHeap ();
//#endif
1998-04-07 00:00:00 +00:00
return (void *) ((byte *)base + sizeof(memblock_t));
}
1999-02-17 00:00:00 +00:00
/*
========================
=
= Z_FreeTags
=
========================
*/
1998-12-22 00:00:00 +00:00
void Z_FreeTags (int lowtag, int hightag)
1998-04-07 00:00:00 +00:00
{
memblock_t* block;
memblock_t* next;
1999-02-17 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
//#ifdef _DEBUG
// Z_CheckHeap ();
//#endif
1998-04-07 00:00:00 +00:00
for (block = mainzone->blocklist.next ;
block != &mainzone->blocklist ;
block = next)
{
1999-02-17 00:00:00 +00:00
next = block->next; // get link before freeing
1998-04-07 00:00:00 +00:00
if (!block->user)
1999-02-17 00:00:00 +00:00
continue; // free block
1998-04-07 00:00:00 +00:00
if (block->tag >= lowtag && block->tag <= hightag)
1998-12-22 00:00:00 +00:00
Z_Free ((byte *)block+sizeof(memblock_t));
1998-04-07 00:00:00 +00:00
}
}
1999-02-17 00:00:00 +00:00
/*
========================
=
= Z_DumpHeap
=
= Note: TFileDumpHeap( stdout ) ?
========================
*/
1998-12-22 00:00:00 +00:00
void Z_DumpHeap (int lowtag, int hightag)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
memblock_t *block;
1998-04-07 00:00:00 +00:00
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH, "zone size: %i location: %p\n", mainzone->size,mainzone);
Printf (PRINT_HIGH, "tag range: %i to %i\n", lowtag, hightag);
1998-04-07 00:00:00 +00:00
for (block = mainzone->blocklist.next ; ; block = block->next)
{
if (block->tag >= lowtag && block->tag <= hightag)
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH, "block:%p size:%7i user:%p tag:%3i\n",
1998-04-07 00:00:00 +00:00
block, block->size, block->user, block->tag);
if (block->next == &mainzone->blocklist)
{
// all blocks have been hit
break;
}
if ( (byte *)block + block->size != (byte *)block->next)
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH, "ERROR: block size does not touch the next block\n");
1998-04-07 00:00:00 +00:00
if ( block->next->prev != block)
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH, "ERROR: next block doesn't have proper back link\n");
1998-04-07 00:00:00 +00:00
if (!block->user && !block->next->user)
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH, "ERROR: two consecutive free blocks\n");
1998-04-07 00:00:00 +00:00
}
}
//
// Z_FileDumpHeap
//
1998-12-22 00:00:00 +00:00
void Z_FileDumpHeap (FILE *f)
1998-04-07 00:00:00 +00:00
{
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)
{
// all blocks have been hit
break;
}
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");
}
}
1999-02-17 00:00:00 +00:00
/*
========================
=
= Z_CheckHeap
=
========================
*/
1998-04-07 00:00:00 +00:00
void Z_CheckHeap (void)
{
1999-02-17 00:00:00 +00:00
memblock_t *block;
1998-04-07 00:00:00 +00:00
for (block = mainzone->blocklist.next ; ; block = block->next)
{
if (block->next == &mainzone->blocklist)
{
// all blocks have been hit
break;
}
1999-02-17 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
if ( (byte *)block + block->size != (byte *)block->next)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_CheckHeap: block size does not touch the next block\n");
1998-04-07 00:00:00 +00:00
if ( block->next->prev != block)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_CheckHeap: next block doesn't have proper back link\n"
"(Next is %p, Back is %p, This is %p", block->next, block->next->prev, block);
1998-04-07 00:00:00 +00:00
if (!block->user && !block->next->user)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_CheckHeap: two consecutive free blocks\n");
1998-04-07 00:00:00 +00:00
}
}
1999-02-17 00:00:00 +00:00
/*
========================
=
= Z_ChangeTag
=
========================
*/
1998-12-22 00:00:00 +00:00
void Z_ChangeTag2 (void *ptr, int tag)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
memblock_t *block;
1999-02-17 00:00:00 +00:00
1998-04-07 00:00:00 +00:00
block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
if (block->id != ZONEID)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_ChangeTag: freed a pointer without ZONEID");
1998-04-07 00:00:00 +00:00
if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100)
1998-12-22 00:00:00 +00:00
I_FatalError ("Z_ChangeTag: an owner is required for purgable blocks");
1998-04-07 00:00:00 +00:00
block->tag = tag;
}
//
// Z_FreeMemory
//
1998-12-22 00:00:00 +00:00
static size_t numblocks;
static size_t largestpfree, pfree, usedpblocks; // Purgable blocks
static size_t largestefree, efree, usedeblocks; // Empty (Unused) blocks
static size_t largestlsize, lsize, usedlblocks; // Locked blocks
size_t Z_FreeMemory (void)
1998-04-07 00:00:00 +00:00
{
1998-12-22 00:00:00 +00:00
memblock_t *block;
BOOL lastpurgable = false;
1998-04-07 00:00:00 +00:00
1998-12-22 00:00:00 +00:00
numblocks =
largestpfree = pfree = usedpblocks =
largestefree = efree = usedeblocks =
largestlsize = lsize = usedlblocks = 0;
1998-04-07 00:00:00 +00:00
1998-07-14 00:00:00 +00:00
//#ifdef _DEBUG
// Z_CheckHeap ();
//#endif
1998-04-07 00:00:00 +00:00
for (block = mainzone->blocklist.next ;
block != &mainzone->blocklist;
block = block->next)
{
1998-12-22 00:00:00 +00:00
numblocks++;
if (block->tag >= PU_PURGELEVEL) {
usedpblocks++;
pfree += block->size;
if (lastpurgable) {
largestpfree += block->size;
} else if (block->size > largestpfree) {
largestpfree = block->size;
lastpurgable = true;
}
} else if (!block->user) {
usedeblocks++;
efree += block->size;
if (block->size > largestefree)
largestefree = block->size;
lastpurgable = false;
} else {
usedlblocks++;
lsize += block->size;
if (block->size > largestlsize)
largestlsize = block->size;
lastpurgable = false;
}
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
return pfree + efree;
1998-04-07 00:00:00 +00:00
}
1998-12-22 00:00:00 +00:00
void Cmd_Mem (void *plyr, int argc, char **argv)
{
Z_FreeMemory ();
1999-02-17 00:00:00 +00:00
Printf (PRINT_HIGH,
"%u blocks:\n"
1998-12-22 00:00:00 +00:00
"% 5u used (%u, %u)\n"
" % 5u purgable (%u, %u)\n"
" % 5u locked (%u, %u)\n"
"% 5u unused (%u, %u)\n"
"% 5u p-free (%u, %u)\n",
numblocks,
usedpblocks+usedlblocks, pfree+lsize,
largestpfree > largestlsize ? largestpfree : largestlsize,
usedpblocks, pfree, largestpfree,
usedlblocks, lsize, largestlsize,
usedeblocks, efree, largestefree,
usedpblocks + usedeblocks, pfree + efree,
largestpfree > largestefree ? largestpfree : largestefree
);
}