63b3b0c024
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9520 a1c6a512-1295-4272-9138-f99709370657
666 lines
19 KiB
C
666 lines
19 KiB
C
/* Emacs style mode select -*- C++ -*-
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
*
|
|
* PrBoom a Doom port merged with LxDoom and LSDLDoom
|
|
* based on BOOM, a modified and improved DOOM engine
|
|
* Copyright (C) 1999 by
|
|
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
|
|
* Copyright (C) 1999-2000 by
|
|
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
|
|
*
|
|
* 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.
|
|
*
|
|
* DESCRIPTION:
|
|
* Zone Memory Allocation. Neat.
|
|
*
|
|
* Neat enough to be rewritten by Lee Killough...
|
|
*
|
|
* Must not have been real neat :)
|
|
*
|
|
* Made faster and more general, and added wrappers for all of Doom's
|
|
* memory allocation functions, including malloc() and similar functions.
|
|
* Added line and file numbers, in case of error. Added performance
|
|
* statistics and tunables.
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "z_zone.h"
|
|
#include "z_bmalloc.h"
|
|
#include "doomdef.h"
|
|
#include "i_system.h"
|
|
#include "rockmacros.h"
|
|
#include "m_argv.h"
|
|
|
|
// Tunables
|
|
|
|
// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE)
|
|
#define CACHE_ALIGN 32
|
|
|
|
// Minimum chunk size at which blocks are allocated
|
|
#define CHUNK_SIZE 32
|
|
|
|
// Minimum size a block must be to become part of a split
|
|
#define MIN_BLOCK_SPLIT (1024)
|
|
|
|
// Minimum RAM machine is assumed to have
|
|
/* cph - Select zone size. 6megs is usable, but with the SDL version
|
|
* storing sounds in the zone, 8 is more sensible */
|
|
#define MIN_RAM (8*1024*1024)
|
|
|
|
// Amount to subtract when retrying failed attempts to allocate initial pool
|
|
#define RETRY_AMOUNT (256*1024)
|
|
|
|
// signature for block header
|
|
#define ZONEID 0x931d4a11
|
|
|
|
// Number of mallocs & frees kept in history buffer (must be a power of 2)
|
|
#define ZONE_HISTORY 4
|
|
|
|
// End Tunables
|
|
|
|
typedef struct memblock {
|
|
|
|
#ifdef ZONEIDCHECK
|
|
unsigned id;
|
|
#endif
|
|
|
|
struct memblock *next,*prev;
|
|
size_t size;
|
|
void **user;
|
|
unsigned char tag,vm;
|
|
|
|
#ifdef INSTRUMENTED
|
|
unsigned short extra;
|
|
const char *file;
|
|
int line;
|
|
#endif
|
|
|
|
} memblock_t;
|
|
|
|
/* size of block header
|
|
* cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on
|
|
* 64bit architectures */
|
|
static const size_t HEADER_SIZE IDATA_ATTR= (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1);
|
|
|
|
static memblock_t *rover IBSS_ATTR; // roving pointer to memory blocks
|
|
static memblock_t *zone IBSS_ATTR; // pointer to first block
|
|
static memblock_t *zonebase IBSS_ATTR; // pointer to entire zone memory
|
|
static size_t zonebase_size IBSS_ATTR; // zone memory allocated size
|
|
|
|
#ifdef INSTRUMENTED
|
|
|
|
// statistics for evaluating performance
|
|
static size_t free_memory;
|
|
static size_t active_memory;
|
|
static size_t purgable_memory;
|
|
static size_t inactive_memory;
|
|
static size_t virtual_memory;
|
|
|
|
static void Z_PrintStats(void) // Print allocation statistics
|
|
{
|
|
unsigned long total_memory = free_memory + active_memory +
|
|
purgable_memory + inactive_memory +
|
|
virtual_memory;
|
|
double s = 100.0 / total_memory;
|
|
|
|
doom_printf("%-5u\t%6.01f%%\tstatic\n"
|
|
"%-5u\t%6.01f%%\tpurgable\n"
|
|
"%-5u\t%6.01f%%\tfree\n"
|
|
"%-5u\t%6.01f%%\tfragmentary\n"
|
|
"%-5u\t%6.01f%%\tvirtual\n"
|
|
"%-5lu\t\ttotal\n",
|
|
active_memory,
|
|
active_memory*s,
|
|
purgable_memory,
|
|
purgable_memory*s,
|
|
free_memory,
|
|
free_memory*s,
|
|
inactive_memory,
|
|
inactive_memory*s,
|
|
virtual_memory,
|
|
virtual_memory*s,
|
|
total_memory
|
|
);
|
|
}
|
|
|
|
#ifdef HEAPDUMP
|
|
void W_PrintLump(FILE* fp, void* p);
|
|
|
|
void Z_DumpMemory(void)
|
|
{
|
|
static int dump;
|
|
memblock_t* block = zone;
|
|
char buf[80];
|
|
FILE* fp;
|
|
size_t total_cache = 0, total_free = 0, total_malloc = 0;
|
|
|
|
sprintf(buf, "memdump.%d", dump++);
|
|
fp = fopen(buf, "w");
|
|
do {
|
|
switch (block->tag) {
|
|
case PU_FREE:
|
|
fprintf(fp, "free %d\n", block->size);
|
|
total_free += block->size;
|
|
break;
|
|
case PU_CACHE:
|
|
fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size);
|
|
total_cache += block->size;
|
|
break;
|
|
case PU_LEVEL:
|
|
fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size);
|
|
total_malloc += block->size;
|
|
break;
|
|
default:
|
|
fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size);
|
|
total_malloc += block->size;
|
|
if (!strcmp(block->file,"w_wad.c")) W_PrintLump(fp, (char*)block + HEADER_SIZE);
|
|
fputc('\n', fp);
|
|
break;
|
|
}
|
|
block=block->next;
|
|
} while (block != zone);
|
|
fprintf(fp, "malloc %d, cache %d, free %d, total %d\n",
|
|
total_malloc, total_cache, total_free,
|
|
total_malloc + total_cache + total_free);
|
|
fclose(fp);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef INSTRUMENTED
|
|
|
|
// killough 4/26/98: Add history information
|
|
|
|
enum {malloc_history, free_history, NUM_HISTORY_TYPES};
|
|
|
|
static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY];
|
|
static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY];
|
|
static int history_index[NUM_HISTORY_TYPES];
|
|
static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"};
|
|
|
|
void Z_DumpHistory(char *buf)
|
|
{
|
|
int i,j;
|
|
char s[1024];
|
|
strcat(buf,"\n");
|
|
for (i=0;i<NUM_HISTORY_TYPES;i++)
|
|
{
|
|
sprintf(s,"\nLast several %s:\n\n", desc[i]);
|
|
strcat(buf,s);
|
|
for (j=0; j<ZONE_HISTORY; j++)
|
|
{
|
|
int k = (history_index[i]-j-1) & (ZONE_HISTORY-1);
|
|
if (file_history[i][k])
|
|
{
|
|
sprintf(s, "File: %s, Line: %d\n", file_history[i][k],
|
|
line_history[i][k]);
|
|
strcat(buf,s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
|
|
void Z_DumpHistory(char *buf)
|
|
{
|
|
(void)buf;
|
|
}
|
|
|
|
#endif
|
|
|
|
void Z_Close(void)
|
|
{
|
|
// (free)(zonebase);
|
|
zone = rover = zonebase = NULL;
|
|
}
|
|
|
|
void Z_Init(void)
|
|
{
|
|
unsigned int size;
|
|
#ifdef INSTRUMENTED
|
|
if (!(HEADER_SIZE >= sizeof(memblock_t) && MIN_RAM > LEAVE_ASIDE))
|
|
I_Error("Z_Init: Sanity check failed");
|
|
#endif
|
|
|
|
// atexit(Z_Close); // exit handler
|
|
|
|
// Allocate the memory
|
|
|
|
zonebase=rb->plugin_get_audio_buffer(&size);
|
|
size-=2*(HEADER_SIZE + CACHE_ALIGN); // Leave space for header and CACHE_ALIGN
|
|
size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size
|
|
size += HEADER_SIZE + CACHE_ALIGN;
|
|
|
|
zonebase_size=size;
|
|
|
|
printf("Z_Init: Allocated %dKb zone memory\n", (long unsigned)size >> 10);
|
|
|
|
// Align on cache boundary
|
|
|
|
zone = (memblock_t *) ((unsigned long)zonebase + CACHE_ALIGN -
|
|
((unsigned long)zonebase & (CACHE_ALIGN-1)));
|
|
|
|
rover = zone; // Rover points to base of zone mem
|
|
zone->next = zone->prev = zone; // Single node
|
|
zone->size = size; // All memory in one block
|
|
zone->tag = PU_FREE; // A free block
|
|
zone->vm = 0;
|
|
|
|
#ifdef ZONEIDCHECK
|
|
zone->id = 0;
|
|
#endif
|
|
|
|
#ifdef INSTRUMENTED
|
|
free_memory = size;
|
|
inactive_memory = zonebase_size - size;
|
|
active_memory = purgable_memory = virtual_memory = 0;
|
|
#endif
|
|
}
|
|
|
|
/* Z_Malloc
|
|
* You can pass a NULL user if the tag is < PU_PURGELEVEL.
|
|
*
|
|
* cph - the algorithm here was a very simple first-fit round-robin
|
|
* one - just keep looping around, freeing everything we can until
|
|
* we get a large enough space
|
|
*
|
|
* This has been changed now; we still do the round-robin first-fit,
|
|
* but we only free the blocks we actually end up using; we don't
|
|
* free all the stuff we just pass on the way.
|
|
*/
|
|
|
|
void *(Z_Malloc)(size_t size, int tag, void **user
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
register memblock_t *block;
|
|
memblock_t *start, *first_of_free;
|
|
register size_t contig_free;
|
|
|
|
#ifdef INSTRUMENTED
|
|
size_t size_orig = size;
|
|
#ifdef CHECKHEAP
|
|
Z_CheckHeap();
|
|
#endif
|
|
|
|
file_history[malloc_history][history_index[malloc_history]] = file;
|
|
line_history[malloc_history][history_index[malloc_history]++] = line;
|
|
history_index[malloc_history] &= ZONE_HISTORY-1;
|
|
#endif
|
|
|
|
#ifdef ZONEIDCHECK
|
|
if (tag >= PU_PURGELEVEL && !user)
|
|
I_Error ("Z_Malloc: An owner is required for purgable blocks"
|
|
#ifdef INSTRUMENTED
|
|
"Source: %s:%d", file, line
|
|
#endif
|
|
);
|
|
#endif
|
|
|
|
if (!size)
|
|
return user ? *user = NULL : NULL; // malloc(0) returns NULL
|
|
|
|
size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size
|
|
|
|
block = rover;
|
|
|
|
if (block->prev->tag == PU_FREE)
|
|
block = block->prev;
|
|
|
|
start = block;
|
|
first_of_free = NULL; contig_free = 0;
|
|
|
|
do {
|
|
/* If we just wrapped, we're not contiguous with the previous block */
|
|
if (block == zone) contig_free = 0;
|
|
|
|
if (block->tag < PU_PURGELEVEL && block->tag != PU_FREE) {
|
|
/* Not free(able), so no free space here */
|
|
contig_free = 0;
|
|
} else {
|
|
/* Add to contiguous chunk of free space */
|
|
if (!contig_free) first_of_free = block;
|
|
contig_free += block->size;
|
|
|
|
/* First fit */
|
|
if (contig_free >= size)
|
|
break;
|
|
}
|
|
}
|
|
while ((block = block->next) != start); // detect cycles as failure
|
|
|
|
if (contig_free >= size) {
|
|
/* We have a block of free(able) memory on the heap which will suffice */
|
|
block = first_of_free;
|
|
|
|
/* If the previous block is adjacent and free, step back and include it */
|
|
if (block != zone && block->prev->tag == PU_FREE)
|
|
block = block->prev;
|
|
|
|
/* Free current block if needed */
|
|
if (block->tag != PU_FREE) Z_Free((char *) block + HEADER_SIZE);
|
|
|
|
/* Note: guaranteed that block->prev is either
|
|
* not free or not contiguous
|
|
*
|
|
* At every step, block->next must be not free, else it would
|
|
* have been merged with our block
|
|
* No range check needed because we know it works by the previous loop */
|
|
while (block->size < size)
|
|
Z_Free((char *)(block->next) + HEADER_SIZE);
|
|
|
|
/* Now, carve up the block */
|
|
{
|
|
size_t extra = block->size - size;
|
|
if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) {
|
|
memblock_t *newb = (memblock_t *)((char *) block +
|
|
HEADER_SIZE + size);
|
|
|
|
(newb->next = block->next)->prev = newb;
|
|
(newb->prev = block)->next = newb; // Split up block
|
|
block->size = size;
|
|
newb->size = extra - HEADER_SIZE;
|
|
newb->tag = PU_FREE;
|
|
newb->vm = 0;
|
|
|
|
#ifdef INSTRUMENTED
|
|
inactive_memory += HEADER_SIZE;
|
|
free_memory -= HEADER_SIZE;
|
|
#endif
|
|
}
|
|
|
|
rover = block->next; // set roving pointer for next search
|
|
|
|
#ifdef INSTRUMENTED
|
|
inactive_memory += block->extra = block->size - size_orig;
|
|
if (tag >= PU_PURGELEVEL)
|
|
purgable_memory += size_orig;
|
|
else
|
|
active_memory += size_orig;
|
|
free_memory -= block->size;
|
|
#endif
|
|
}
|
|
} else { // We don't have enough contiguous free blocks
|
|
I_Error ("Z_Malloc: Failure trying to allocate %d bytes",(unsigned long) size);
|
|
rb->sleep(300);
|
|
}
|
|
|
|
#ifdef INSTRUMENTED
|
|
block->file = file;
|
|
block->line = line;
|
|
#endif
|
|
|
|
#ifdef ZONEIDCHECK
|
|
block->id = ZONEID; // signature required in block header
|
|
#endif
|
|
block->tag = tag; // tag
|
|
block->user = user; // user
|
|
block = (memblock_t *)((char *) block + HEADER_SIZE);
|
|
if (user) // if there is a user
|
|
*user = block; // set user to point to new block
|
|
|
|
#ifdef INSTRUMENTED
|
|
Z_PrintStats(); // print memory allocation stats
|
|
// scramble memory -- weed out any bugs
|
|
memset(block, gametic & 0xff, size);
|
|
#endif
|
|
return block;
|
|
}
|
|
|
|
void (Z_Free)(void *p
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef INSTRUMENTED
|
|
#ifdef CHECKHEAP
|
|
Z_CheckHeap();
|
|
#endif
|
|
file_history[free_history][history_index[free_history]] = file;
|
|
line_history[free_history][history_index[free_history]++] = line;
|
|
history_index[free_history] &= ZONE_HISTORY-1;
|
|
#endif
|
|
|
|
if (p)
|
|
{
|
|
memblock_t *other, *block = (memblock_t *)((char *) p - HEADER_SIZE);
|
|
|
|
#ifdef ZONEIDCHECK
|
|
if (block->id != ZONEID)
|
|
I_Error("Z_Free: freed a pointer without ZONEID"
|
|
#ifdef INSTRUMENTED
|
|
"\nSource: %s:%d"
|
|
"\nSource of malloc: %s:%d"
|
|
, file, line, block->file, block->line
|
|
#endif
|
|
);
|
|
block->id = 0; // Nullify id so another free fails
|
|
#endif
|
|
|
|
#ifdef INSTRUMENTED
|
|
/* scramble memory -- weed out any bugs */
|
|
memset(p, gametic & 0xff, block->size);
|
|
#endif
|
|
|
|
if (block->user) // Nullify user if one exists
|
|
*block->user = NULL;
|
|
|
|
{
|
|
|
|
#ifdef INSTRUMENTED
|
|
free_memory += block->size;
|
|
inactive_memory -= block->extra;
|
|
if (block->tag >= PU_PURGELEVEL)
|
|
purgable_memory -= block->size - block->extra;
|
|
else
|
|
active_memory -= block->size - block->extra;
|
|
#endif
|
|
|
|
block->tag = PU_FREE; // Mark block freed
|
|
|
|
if (block != zone)
|
|
{
|
|
other = block->prev; // Possibly merge with previous block
|
|
if (other->tag == PU_FREE)
|
|
{
|
|
if (rover == block) // Move back rover if it points at block
|
|
rover = other;
|
|
(other->next = block->next)->prev = other;
|
|
other->size += block->size + HEADER_SIZE;
|
|
block = other;
|
|
|
|
#ifdef INSTRUMENTED
|
|
inactive_memory -= HEADER_SIZE;
|
|
free_memory += HEADER_SIZE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
other = block->next; // Possibly merge with next block
|
|
if (other->tag == PU_FREE && other != zone)
|
|
{
|
|
if (rover == other) // Move back rover if it points at next block
|
|
rover = block;
|
|
(block->next = other->next)->prev = block;
|
|
block->size += other->size + HEADER_SIZE;
|
|
|
|
#ifdef INSTRUMENTED
|
|
inactive_memory -= HEADER_SIZE;
|
|
free_memory += HEADER_SIZE;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef INSTRUMENTED
|
|
Z_PrintStats(); // print memory allocation stats
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void (Z_FreeTags)(int lowtag, int hightag
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
/* cph - move rover to start of zone; we like to encourage static
|
|
* data to stay in one place, at the start of the heap
|
|
*/
|
|
memblock_t *block = rover = zone;
|
|
|
|
#ifdef HEAPDUMP
|
|
Z_DumpMemory();
|
|
#endif
|
|
|
|
if (lowtag <= PU_FREE)
|
|
lowtag = PU_FREE+1;
|
|
|
|
do // Scan through list, searching for tags in range
|
|
if (block->tag >= lowtag && block->tag <= hightag)
|
|
{
|
|
memblock_t *prev = block->prev, *cur = block;
|
|
#ifdef INSTRUMENTED
|
|
(Z_Free)((char *) block + HEADER_SIZE, file, line);
|
|
#else
|
|
(Z_Free)((char *) block + HEADER_SIZE);
|
|
#endif
|
|
/* cph - be more careful here, we were skipping blocks!
|
|
* If the current block was not merged with the previous,
|
|
* cur is still a valid pointer, prev->next == cur, and cur is
|
|
* already free so skip to the next.
|
|
* If the current block was merged with the previous,
|
|
* the next block to analyse is prev->next.
|
|
* Note that the while() below does the actual step forward
|
|
*/
|
|
block = (prev->next == cur) ? cur : prev;
|
|
}
|
|
while ((block=block->next) != zone);
|
|
}
|
|
|
|
void (Z_ChangeTag)(void *ptr, int tag
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE);
|
|
|
|
#ifdef INSTRUMENTED
|
|
#ifdef CHECKHEAP
|
|
Z_CheckHeap();
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef ZONEIDCHECK
|
|
if (block->id != ZONEID)
|
|
I_Error ("Z_ChangeTag: freed a pointer without ZONEID"
|
|
#ifdef INSTRUMENTED
|
|
"\nSource: %s:%d"
|
|
"\nSource of malloc: %s:%d"
|
|
, file, line, block->file, block->line
|
|
#endif
|
|
);
|
|
|
|
if (tag >= PU_PURGELEVEL && !block->user)
|
|
I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n"
|
|
#ifdef INSTRUMENTED
|
|
"Source: %s:%d"
|
|
"\nSource of malloc: %s:%d"
|
|
, file, line, block->file, block->line
|
|
#endif
|
|
);
|
|
|
|
#endif // ZONEIDCHECK
|
|
|
|
{
|
|
#ifdef INSTRUMENTED
|
|
if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL)
|
|
{
|
|
active_memory -= block->size - block->extra;
|
|
purgable_memory += block->size - block->extra;
|
|
}
|
|
else
|
|
if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL)
|
|
{
|
|
active_memory += block->size - block->extra;
|
|
purgable_memory -= block->size - block->extra;
|
|
}
|
|
#endif
|
|
}
|
|
block->tag = tag;
|
|
}
|
|
|
|
void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
void *p = (Z_Malloc)(n, tag, user DA(file, line));
|
|
if (ptr)
|
|
{
|
|
memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE);
|
|
memcpy(p, ptr, n <= block->size ? n : block->size);
|
|
(Z_Free)(ptr DA(file, line));
|
|
if (user) // in case Z_Free nullified same user
|
|
*user=p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
return
|
|
(n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL;
|
|
}
|
|
|
|
char *(Z_Strdup)(const char *s, int tag, void **user
|
|
#ifdef INSTRUMENTED
|
|
, const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s);
|
|
}
|
|
|
|
void (Z_CheckHeap)(
|
|
#ifdef INSTRUMENTED
|
|
const char *file, int line
|
|
#endif
|
|
)
|
|
{
|
|
memblock_t *block = zone; // Start at base of zone mem
|
|
do // Consistency check (last node treated special)
|
|
if ((block->next != zone &&
|
|
(memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next)
|
|
|| block->next->prev != block || block->prev->next != block)
|
|
I_Error("Z_ChkHp: B size %d touch %d\n", block+HEADER_SIZE+block->size, block->next
|
|
#ifdef INSTRUMENTED
|
|
"Source: %s:%d"
|
|
"\nSource of offending block: %s:%d"
|
|
, file, line, block->file, block->line
|
|
#endif
|
|
);
|
|
while ((block=block->next) != zone);
|
|
}
|