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>
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
{
|
|
|
|
// total bytes malloced, including header
|
|
|
|
int size;
|
|
|
|
|
|
|
|
// start / end cap for linked list
|
|
|
|
memblock_t blocklist;
|
|
|
|
|
|
|
|
memblock_t* rover;
|
|
|
|
|
|
|
|
} 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
|
|
|
|
//
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-12-22 00:00:00 +00:00
|
|
|
static void Z_Close (void)
|
|
|
|
{
|
|
|
|
free (mainzone);
|
|
|
|
mainzone = NULL;
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Z_Init
|
|
|
|
//
|
|
|
|
void Z_Init (void)
|
|
|
|
{
|
|
|
|
memblock_t* block;
|
|
|
|
|
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
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// NULL indicates a free block.
|
|
|
|
block->user = NULL;
|
|
|
|
|
|
|
|
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
|
|
|
|
//
|
1998-07-14 00:00:00 +00:00
|
|
|
void Z_Free2 (void* ptr, char *f, int l)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
1998-12-22 00:00:00 +00:00
|
|
|
memblock_t *block, *other;
|
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
|
|
|
|
|
|
|
block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
|
|
|
|
|
|
|
|
if (block->id != ZONEID)
|
1998-12-22 00:00:00 +00:00
|
|
|
I_FatalError ("Z_Free: freed a pointer without ZONEID\n%s, line %i", f, l);
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
memblock_t* start;
|
|
|
|
memblock_t* rover;
|
|
|
|
memblock_t* newblock;
|
|
|
|
memblock_t* base;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
// scan through the block list,
|
|
|
|
// looking for the first free block
|
|
|
|
// of sufficient size,
|
|
|
|
// throwing out any purgable blocks along the way.
|
|
|
|
|
|
|
|
// account for size of block header
|
|
|
|
size += sizeof(memblock_t);
|
|
|
|
|
|
|
|
// 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
|
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)
|
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
// the rover can be the base block
|
|
|
|
base = base->prev;
|
|
|
|
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
|
|
|
|
base->user = user;
|
|
|
|
*(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
|
|
|
|
|
|
|
// mark as in use, but unowned
|
|
|
|
base->user = (void *)2;
|
|
|
|
}
|
|
|
|
base->tag = tag;
|
|
|
|
|
|
|
|
// next allocation will start looking here
|
|
|
|
mainzone->rover = base->next;
|
|
|
|
|
|
|
|
base->id = ZONEID;
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
// get link before freeing
|
|
|
|
next = block->next;
|
|
|
|
|
|
|
|
// free block?
|
|
|
|
if (!block->user)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
1998-12-22 00:00:00 +00:00
|
|
|
Printf ("zone size: %i location: %p\n", mainzone->size,mainzone);
|
|
|
|
Printf ("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)
|
|
|
|
Printf ("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)
|
|
|
|
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
|
|
|
|
//
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Z_CheckHeap
|
|
|
|
//
|
|
|
|
void Z_CheckHeap (void)
|
|
|
|
{
|
|
|
|
memblock_t* block;
|
|
|
|
|
|
|
|
for (block = mainzone->blocklist.next ; ; block = block->next)
|
|
|
|
{
|
|
|
|
if (block->next == &mainzone->blocklist)
|
|
|
|
{
|
|
|
|
// all blocks have been hit
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
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 ();
|
|
|
|
|
|
|
|
Printf ("%u blocks:\n"
|
|
|
|
"% 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
|
|
|
|
);
|
|
|
|
}
|