summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/z_zone.c
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-03-28 15:44:01 +0000
committerDave Chapman <dave@dchapman.com>2006-03-28 15:44:01 +0000
commit47f4a458d636a889e955e68f896708f1276febc0 (patch)
tree99f770c02ef606f0abbdcd332ac39e69830d8007 /apps/plugins/doom/z_zone.c
parentfff7d6157d56f233cad5c2003475e47a5ff809a7 (diff)
Patch #2969 - Doom! Currently only working on the H300.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9312 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/doom/z_zone.c')
-rw-r--r--apps/plugins/doom/z_zone.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/apps/plugins/doom/z_zone.c b/apps/plugins/doom/z_zone.c
new file mode 100644
index 0000000000..036c995743
--- /dev/null
+++ b/apps/plugins/doom/z_zone.c
@@ -0,0 +1,666 @@
+/* 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 *) ((char *) zonebase + CACHE_ALIGN -
+ ((unsigned) 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);
+}