summaryrefslogtreecommitdiff
path: root/src/game/bg_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/bg_alloc.c')
-rw-r--r--src/game/bg_alloc.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/game/bg_alloc.c b/src/game/bg_alloc.c
new file mode 100644
index 00000000..080bf83a
--- /dev/null
+++ b/src/game/bg_alloc.c
@@ -0,0 +1,204 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2006 Tim Angus
+
+This file is part of Tremulous.
+
+Tremulous 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.
+
+Tremulous 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 Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "../qcommon/q_shared.h"
+#include "bg_public.h"
+
+#ifdef GAME
+# define POOLSIZE ( 1024 * 1024 )
+#else
+# define POOLSIZE ( 256 * 1024 )
+#endif
+
+#define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value
+#define ROUNDBITS 31 // Round to 32 bytes
+
+typedef struct freeMemNode_s
+{
+ // Size of ROUNDBITS
+ int cookie, size; // Size includes node (obviously)
+ struct freeMemNode_s *prev, *next;
+} freeMemNode_t;
+
+static char memoryPool[POOLSIZE];
+static freeMemNode_t *freeHead;
+static int freeMem;
+
+void *BG_Alloc( int size )
+{
+ // Find a free block and allocate.
+ // Does two passes, attempts to fill same-sized free slot first.
+
+ freeMemNode_t *fmn, *prev, *next, *smallest;
+ int allocsize, smallestsize;
+ char *endptr;
+ int *ptr;
+
+ allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary
+ ptr = NULL;
+
+ smallest = NULL;
+ smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :)
+ for( fmn = freeHead; fmn; fmn = fmn->next )
+ {
+ if( fmn->cookie != FREEMEMCOOKIE )
+ Com_Error( ERR_DROP, "BG_Alloc: Memory corruption detected!\n" );
+
+ if( fmn->size >= allocsize )
+ {
+ // We've got a block
+ if( fmn->size == allocsize )
+ {
+ // Same size, just remove
+
+ prev = fmn->prev;
+ next = fmn->next;
+ if( prev )
+ prev->next = next; // Point previous node to next
+ if( next )
+ next->prev = prev; // Point next node to previous
+ if( fmn == freeHead )
+ freeHead = next; // Set head pointer to next
+ ptr = (int *) fmn;
+ break; // Stop the loop, this is fine
+ }
+ else
+ {
+ // Keep track of the smallest free slot
+ if( fmn->size < smallestsize )
+ {
+ smallest = fmn;
+ smallestsize = fmn->size;
+ }
+ }
+ }
+ }
+
+ if( !ptr && smallest )
+ {
+ // We found a slot big enough
+ smallest->size -= allocsize;
+ endptr = (char *) smallest + smallest->size;
+ ptr = (int *) endptr;
+ }
+
+ if( ptr )
+ {
+ freeMem -= allocsize;
+ memset( ptr, 0, allocsize );
+ *ptr++ = allocsize; // Store a copy of size for deallocation
+ return( (void *) ptr );
+ }
+
+ Com_Error( ERR_DROP, "BG_Alloc: failed on allocation of %i bytes\n", size );
+ return( NULL );
+}
+
+void BG_Free( void *ptr )
+{
+ // Release allocated memory, add it to the free list.
+
+ freeMemNode_t *fmn;
+ char *freeend;
+ int *freeptr;
+
+ freeptr = ptr;
+ freeptr--;
+
+ freeMem += *freeptr;
+
+ for( fmn = freeHead; fmn; fmn = fmn->next )
+ {
+ freeend = ((char *) fmn) + fmn->size;
+ if( freeend == (char *) freeptr )
+ {
+ // Released block can be merged to an existing node
+
+ fmn->size += *freeptr; // Add size of node.
+ return;
+ }
+ }
+ // No merging, add to head of list
+
+ fmn = (freeMemNode_t *) freeptr;
+ fmn->size = *freeptr; // Set this first to avoid corrupting *freeptr
+ fmn->cookie = FREEMEMCOOKIE;
+ fmn->prev = NULL;
+ fmn->next = freeHead;
+ freeHead->prev = fmn;
+ freeHead = fmn;
+}
+
+void BG_InitMemory( void )
+{
+ // Set up the initial node
+
+ freeHead = (freeMemNode_t *)memoryPool;
+ freeHead->cookie = FREEMEMCOOKIE;
+ freeHead->size = POOLSIZE;
+ freeHead->next = NULL;
+ freeHead->prev = NULL;
+ freeMem = sizeof( memoryPool );
+}
+
+void BG_DefragmentMemory( void )
+{
+ // If there's a frenzy of deallocation and we want to
+ // allocate something big, this is useful. Otherwise...
+ // not much use.
+
+ freeMemNode_t *startfmn, *endfmn, *fmn;
+
+ for( startfmn = freeHead; startfmn; )
+ {
+ endfmn = (freeMemNode_t *)(((char *) startfmn) + startfmn->size);
+ for( fmn = freeHead; fmn; )
+ {
+ if( fmn->cookie != FREEMEMCOOKIE )
+ Com_Error( ERR_DROP, "BG_DefragmentMemory: Memory corruption detected!\n" );
+
+ if( fmn == endfmn )
+ {
+ // We can add fmn onto startfmn.
+
+ if( fmn->prev )
+ fmn->prev->next = fmn->next;
+ if( fmn->next )
+ {
+ if( !(fmn->next->prev = fmn->prev) )
+ freeHead = fmn->next; // We're removing the head node
+ }
+ startfmn->size += fmn->size;
+ memset( fmn, 0, sizeof(freeMemNode_t) ); // A redundant call, really.
+
+ startfmn = freeHead;
+ endfmn = fmn = NULL; // Break out of current loop
+ }
+ else
+ fmn = fmn->next;
+ }
+
+ if( endfmn )
+ startfmn = startfmn->next; // endfmn acts as a 'restart' flag here
+ }
+}