diff options
Diffstat (limited to 'src/cgame/cg_mem.c')
-rw-r--r-- | src/cgame/cg_mem.c | 215 |
1 files changed, 172 insertions, 43 deletions
diff --git a/src/cgame/cg_mem.c b/src/cgame/cg_mem.c index 3490edb2..e4ef82ab 100644 --- a/src/cgame/cg_mem.c +++ b/src/cgame/cg_mem.c @@ -1,62 +1,191 @@ // Copyright (C) 1999-2000 Id Software, Inc. // // -// g_mem.c +// cg_mem.c // -//TA: hack to provide dynanmic allocation clientside - -/* - * Portions Copyright (C) 2000-2001 Tim Angus - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser 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. - */ - -/* To assertain which portions are licensed under the LGPL and which are - * licensed by Id Software, Inc. please run a diff between the equivalent - * versions of the "Tremulous" modification and the unmodified "Quake3" - * game source code. - */ - +// Golliwog: All rewritten to allow deallocation +// +// TA: (very) minor changes for client side operation +// thanks to Golliwog of Quake 3 Fortress ( http://www.q3f.com ) +// for this. + + #include "cg_local.h" +#define POOLSIZE (256 * 1024) +#define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value +#define ROUNDBITS 31 // Round to 32 bytes + +struct freememnode +{ + // Size of ROUNDBITS + int cookie, size; // Size includes node (obviously) + struct freememnode *prev, *next; +}; -#define POOLSIZE (256 * 1024) +static char memoryPool[ POOLSIZE ]; +static struct freememnode *freehead; +static int freemem; -static char memoryPool[POOLSIZE]; -static int allocPoint; +void *CG_Alloc( int size ) +{ + // Find a free block and allocate. + // Does two passes, attempts to fill same-sized free slot first. -void *CG_Alloc( int size ) { - char *p; + struct freememnode *fmn, *prev, *next, *smallest; + int allocsize, smallestsize; + char *endptr; + int *ptr; - /*if ( g_debugAlloc.integer ) { - G_Printf( "CG_Alloc of %i bytes (%i left)\n", size, POOLSIZE - allocPoint - ( ( size + 31 ) & ~31 ) ); - }*/ + allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary + ptr = NULL; - if ( allocPoint + size > POOLSIZE ) { - CG_Error( "CG_Alloc: failed on allocation of %u bytes\n", size ); - return NULL; + smallest = NULL; + smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :) + for( fmn = freehead; fmn; fmn = fmn->next ) + { + if( fmn->cookie != FREEMEMCOOKIE ) + CG_Error( "CG_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; } - p = &memoryPool[allocPoint]; + if( ptr ) + { + freemem -= allocsize; + if( cg_debugAlloc.integer ) + CG_Printf( "CG_Alloc of %i bytes (%i left)\n", allocsize, freemem ); + memset( ptr, 0, allocsize ); + *ptr++ = allocsize; // Store a copy of size for deallocation + return( (void *) ptr ); + } + + CG_Error( "CG_Alloc: failed on allocation of %i bytes\n", size ); + return( NULL ); +} + +void CG_Free( void *ptr ) +{ + // Release allocated memory, add it to the free list. + + struct freememnode *fmn; + char *freeend; + int *freeptr; + + freeptr = ptr; + freeptr--; - allocPoint += ( size + 31 ) & ~31; + freemem += *freeptr; + if( cg_debugAlloc.integer ) + CG_Printf( "CG_Free of %i bytes (%i left)\n", *freeptr, freemem ); - return p; + 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 = (struct freememnode *) 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 CG_InitMemory( void ) { - allocPoint = 0; +void CG_InitMemory( void ) +{ + // Set up the initial node + + freehead = (struct freememnode *) memoryPool; + freehead->cookie = FREEMEMCOOKIE; + freehead->size = POOLSIZE; + freehead->next = NULL; + freehead->prev = NULL; + freemem = sizeof(memoryPool); } +void CG_DefragmentMemory( void ) +{ + // If there's a frenzy of deallocation and we want to + // allocate something big, this is useful. Otherwise... + // not much use. + + struct freememnode *startfmn, *endfmn, *fmn; + + for( startfmn = freehead; startfmn; ) + { + endfmn = (struct freememnode *)(((char *) startfmn) + startfmn->size); + for( fmn = freehead; fmn; ) + { + if( fmn->cookie != FREEMEMCOOKIE ) + CG_Error( "CG_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(struct freememnode) ); // 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 + } +} |