1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
|
//KK-OAX
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
This file is part of the Open Arena source code.
Copied from Tremulous under GPL version 2 including any later version.
Open Arena source code 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.
Open Arena source code 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 Open Arena source code; 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"
#include "g_local.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 (unsigned int)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;
/*
* Returns qtrue if BG_Alloc will succeed, qfalse otherwise
*/
qboolean BG_CanAlloc( unsigned int size) {
freeMemNode_t *fmn;
int allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary
for( fmn = freeHead; fmn; fmn = fmn->next ) {
if( fmn->cookie != FREEMEMCOOKIE ) {
//Memory curroption
return qfalse;
}
if( fmn->size >= allocsize ) {
//At least one useable block
return qtrue;
}
}
return qfalse;
}
void *BG_Alloc( unsigned 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
}
}
//KK-OAX This was moved from g_mem.c to keep functionality from being broken.
void Svcmd_GameMem_f( void ) {
int usedMem;
usedMem = POOLSIZE - freeMem;
G_Printf( "Game memory status: %i out of %i bytes allocated\n", usedMem, POOLSIZE );
}
|