Main Page | Alphabetical List | Compound List | File List | Compound Members | File Members

Ram.c

Go to the documentation of this file.
00001 /****************************************************************************************/
00002 /*  RAM.C                                                                               */
00003 /*                                                                                      */
00004 /*  Author:                                                                             */
00005 /*  Description: Replacement for malloc, realloc and free                               */
00006 /*                                                                                      */
00007 /*  The contents of this file are subject to the Genesis3D Public License               */
00008 /*  Version 1.01 (the "License"); you may not use this file except in                   */
00009 /*  compliance with the License. You may obtain a copy of the License at                */
00010 /*  http://www.genesis3d.com                                                            */
00011 /*                                                                                      */
00012 /*  Software distributed under the License is distributed on an "AS IS"                 */
00013 /*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
00014 /*  the License for the specific language governing rights and limitations              */
00015 /*  under the License.                                                                  */
00016 /*                                                                                      */
00017 /*  The Original Code is Genesis3D, released March 25, 1999.                            */
00018 /*Genesis3D Version 1.1 released November 15, 1999                            */
00019 /*  Copyright (C) 1999 WildTangent, Inc. All Rights Reserved           */
00020 /*                                                                                      */
00021 /****************************************************************************************/
00022 #include <memory.h>
00023 #include <malloc.h>
00024 #include <assert.h>
00025 
00026 #ifndef NDEBUG
00027 #define _CRTDBG_MAP_ALLOC
00028 #include <crtdbg.h>
00029 #endif
00030 
00031 #include "ram.h"
00032 
00033 /*
00034   This controls the MINIMAL_CONFIG flag.  Basically, all overflow, underflow,
00035   and size checking code is always enabled except when NDEBUG is defined...
00036 */
00037 #ifdef NDEBUG
00038   // debugging's turned off, so make it minimal config
00039   #ifndef MINIMAL_CONFIG
00040     #define MINIMAL_CONFIG
00041   #endif
00042 #else
00043   // debugging on, so do full checking
00044   #ifdef MINIMAL_CONFIG
00045     #undef MINIMAL_CONFIG
00046   #endif
00047 #endif
00048 
00049 // stupid stuff...
00050 #ifndef GENESISDLLVERSION
00051 void *StupidUnusedPointer;
00052 #endif
00053 
00054 // critical allocation stuff...
00055 static int geRam_CriticalAllocationCount = 0;
00056 
00057 static geRam_CriticalCallbackFunction geRam_CriticalCallback = NULL;
00058 
00059 /*
00060   increments or decrements a counter.  if the counter is >0
00061   the critical callback function (if set) is called for a failed memory allocation.
00062   add is added to the current counter value.  the new counter value is returned.
00063 */
00064 GENESISAPI int geRam_EnableCriticalCallback(int add)
00065 {
00066         geRam_CriticalAllocationCount += add;
00067         return geRam_CriticalAllocationCount;
00068 }
00069 
00070 
00071 /*
00072   Set the critical callback function.  geRam_Allocate will call this function
00073   if it's unable to allocate memory.  Returns the previous critical callback fcn.
00074 */
00075 GENESISAPI geRam_CriticalCallbackFunction geRam_SetCriticalCallback
00076     (
00077       geRam_CriticalCallbackFunction critical_callback
00078     )
00079 {
00080     geRam_CriticalCallbackFunction OldCallback;
00081 
00082     OldCallback = geRam_CriticalCallback;
00083         geRam_CriticalCallback = critical_callback;
00084     return OldCallback;
00085 }
00086 
00087 /*
00088   If an allocation fails, this function will be called.  If the critical callback
00089   function is not NULL, then that function will be called.
00090 */
00091 static int geRam_DoCriticalCallback
00092     (
00093       void
00094     )
00095 {
00096     if ((geRam_CriticalAllocationCount != 0) && (geRam_CriticalCallback != NULL))
00097     {
00098         return geRam_CriticalCallback ();
00099     }
00100     else
00101     {
00102         return 0;
00103     }
00104 }
00105 
00106 
00107 GENESISAPI void * geRam_AllocateClear(uint32 size)
00108 {
00109 void * mem;
00110         size = (size + 3)&(~(uint32)3);
00111         mem = geRam_Allocate(size);
00112         if ( mem )
00113         {
00114                 memset(mem,0,size);
00115         }
00116 return mem;
00117 }
00118 
00119 #ifdef MINIMAL_CONFIG
00120 
00121     /*
00122       Minimal configuration acts almost exactly like standard malloc, free,
00123       and realloc.  The only difference is the critical allocation stuff.
00124     */
00125 
00126 
00127     /*
00128       Allocate memory of the given size.  In debug mode, the memory is filled
00129       with 0xA5, and we keep track of the amount of memory allocated.
00130     */
00131     void *geRam_Allocate
00132         (
00133           uint32 size
00134         )
00135     {
00136         void *p;
00137 
00138         do
00139         {
00140             p = malloc(size);
00141         } while ((p == NULL) && (geRam_DoCriticalCallback ()));
00142 
00143         return p;
00144     }
00145 
00146     // free an allocated block
00147     void geRam_Free_
00148         (
00149           void *ptr
00150         )
00151     {
00152       free (ptr);
00153     }
00154 
00155     // reallocate a block...
00156     // This acts like the standard realloc
00157 GENESISAPI     void *geRam_Realloc
00158         (
00159           void *ptr,
00160           uint32 newsize
00161         )
00162     {
00163         char *p;
00164         char * NewPtr;
00165 
00166         if (ptr == NULL)
00167         {
00168             return geRam_Allocate (newsize);
00169         }
00170 
00171         // if newsize is NULL, then it's a free and return NULL
00172         if (newsize == 0)
00173         {
00174             geRam_Free (ptr);
00175             return NULL;
00176         }
00177 
00178         p = ptr;
00179         do
00180         {
00181             NewPtr = (char *)realloc (p, newsize);
00182         } while ((NewPtr == NULL) && (geRam_DoCriticalCallback ()));
00183 
00184         return NewPtr;
00185     }
00186 
00187 #else  // MINIMAL_CONFIG
00188      /*
00189        For debugging implementations, we add a header and trailer to the
00190        allocated memory blocks so that we compute memory usage, and catch
00191        simple over- and under-run errors.
00192      */
00193 
00194     // yes, this will break if we use more than 2 gigabytes of RAM...
00195     int32 geRam_CurrentlyUsed       = 0;  // total ram currently in use
00196     int32 geRam_MaximumUsed         = 0;  // max total ram allocated at any time
00197     int32 geRam_NumberOfAllocations     = 0;  // current number of blocks allocated
00198     int32 geRam_MaximumNumberOfAllocations = 0;  // max number of allocations at any time
00199 
00200     // header and trailer stuff...
00201     static char MemStamp[] = {"!CHECKME!"};
00202     static const int MemStampSize = sizeof (MemStamp);
00203     static const int SizeSize = sizeof (uint32);
00204         // <> CB : pad sized to multiples of 8 !!!
00205     #define HEADER_SIZE         (((SizeSize             + MemStampSize)+7)&(~(uint32)7))
00206     #define EXTRA_SIZE          (((HEADER_SIZE  + MemStampSize)+7)&(~(uint32)7))
00207     static const unsigned char AllocFillerByte = (unsigned char)0xA5;
00208     static const unsigned char FreeFillerByte  = (unsigned char)0xB6;
00209     /*
00210       A memory block is allocated that's size + (2*MemStampSize)+SizeSize bytes.
00211       It's then filled with 0xA5.  The size stamp is placed at the head of the block,
00212       with the MemStamp being placed directly after the size at the front, and
00213       also at the end of the block.  The layout is:
00214 
00215       <size><MemStamp><<allocated memory>><MemStamp>
00216     */
00217     typedef enum 
00218         {
00219                 DONT_INITIALIZE = 0, 
00220                 INITIALIZE_MEMORY = 1
00221         } geRam_MemoryInitialization;
00222 
00223     static void geRam_SetupBlock
00224           (
00225             char * p,
00226             uint32 size,
00227             geRam_MemoryInitialization InitMem
00228           )
00229     {
00230         if (InitMem == INITIALIZE_MEMORY)
00231         {
00232             // fill the memory block
00233             memset (p+HEADER_SIZE, AllocFillerByte, size);
00234         }
00235 
00236         // add the size at the front
00237         *((uint32 *)p) = size;
00238 
00239         // copy the memstamp to the front of the block
00240         memcpy (p+SizeSize, MemStamp, MemStampSize);
00241 
00242         // and to the end of the block
00243         memcpy (p+HEADER_SIZE+size, MemStamp, MemStampSize);
00244     }
00245 
00246 #ifndef NDEBUG
00247 GENESISAPI      void* _geRam_DebugAllocate(uint32 size, const char* pFile, int line)
00248         {
00249       char *p;
00250 
00251       do
00252       {
00253           p = (char*)_malloc_dbg (size + EXTRA_SIZE, _NORMAL_BLOCK, pFile, line);
00254       } while ((p == NULL) && geRam_DoCriticalCallback ());
00255 
00256       if (p == NULL)
00257       {
00258          return NULL;
00259       }
00260 
00261       // setup size stamps and memory overwrite checks
00262       geRam_SetupBlock (p, size, INITIALIZE_MEMORY);
00263 
00264       // and update the allocations stuff
00265       geRam_NumberOfAllocations++;
00266       geRam_CurrentlyUsed += size;
00267 
00268       if (geRam_NumberOfAllocations > geRam_MaximumNumberOfAllocations)
00269       {
00270           geRam_MaximumNumberOfAllocations = geRam_NumberOfAllocations;
00271       }
00272       if (geRam_CurrentlyUsed > geRam_MaximumUsed)
00273       {
00274           geRam_MaximumUsed = geRam_CurrentlyUsed;
00275       }
00276 
00277       return p+HEADER_SIZE;
00278         }
00279 
00280 #else // NDEBUG
00281 
00282 GENESISAPI     void * geRam_Allocate (uint32 size)
00283     {
00284       char *p;
00285 
00286       do
00287       {
00288           p = (char*)malloc (size + EXTRA_SIZE);
00289       } while ((p == NULL) && geRam_DoCriticalCallback ());
00290 
00291       if (p == NULL)
00292       {
00293          return NULL;
00294       }
00295 
00296       // setup size stamps and memory overwrite checks
00297       geRam_SetupBlock (p, size, INITIALIZE_MEMORY);
00298 
00299       // and update the allocations stuff
00300       geRam_NumberOfAllocations++;
00301       geRam_CurrentlyUsed += size;
00302 
00303       if (geRam_NumberOfAllocations > geRam_MaximumNumberOfAllocations)
00304       {
00305           geRam_MaximumNumberOfAllocations = geRam_NumberOfAllocations;
00306       }
00307       if (geRam_CurrentlyUsed > geRam_MaximumUsed)
00308       {
00309           geRam_MaximumUsed = geRam_CurrentlyUsed;
00310       }
00311 
00312       return p+HEADER_SIZE;
00313     }
00314 #endif // NDEBUG
00315 
00316     static char * ram_verify_block
00317           (
00318             void * ptr
00319           )
00320     {
00321         char * p = ptr;
00322         uint32 size;
00323 
00324         if (p == NULL)
00325         {
00326             assert (0 && "freeing NULL");
00327             return NULL;
00328         }
00329 
00330         // make p point to the beginning of the block
00331         p -= HEADER_SIZE;
00332 
00333         // get size from block
00334         size = *((uint32 *)p);
00335 
00336         // check stamp at front
00337         if (memcmp (p+SizeSize, MemStamp, MemStampSize) != 0)
00338         {
00339             assert (0 && "ram_verify_block:  Memory block corrupted at front");
00340             return NULL;
00341         }
00342 
00343         // and at back
00344         if (memcmp (p+HEADER_SIZE+size, MemStamp, MemStampSize) != 0)
00345         {
00346             assert (0 && "ram_verify_block:  Memory block corrupted at tail");
00347             return NULL;
00348         }
00349 
00350         return p;
00351     }
00352 
00353 GENESISAPI     void geRam_Free_ (void *ptr)
00354     {
00355         char *p;
00356         uint32 size;
00357 
00358         // make sure it's a valid block...
00359         p = ram_verify_block (ptr);
00360         if (p == NULL)
00361         {
00362             return;
00363         }
00364 
00365         // gotta get the size before you free it
00366         size = *((uint32 *)p);
00367 
00368         // fill it with trash...
00369         memset (p, FreeFillerByte, size+EXTRA_SIZE);
00370 
00371         // free the memory
00372         free (p);
00373 
00374         // update allocations
00375         geRam_NumberOfAllocations--;
00376         assert ((geRam_NumberOfAllocations >= 0) && "free()d more ram than you allocated!");
00377 
00378         geRam_CurrentlyUsed -= size;
00379         assert ((geRam_CurrentlyUsed >= 0) && "free()d more ram than you allocated!");
00380     }
00381 
00382 #ifndef NDEBUG
00383 
00384 GENESISAPI     void * _geRam_DebugRealloc (void *ptr, uint32 newsize, const char* pFile, int line)
00385     {
00386         char *p;
00387         char * NewPtr;
00388         uint32 size;
00389 
00390         // if realloc is called with NULL, just treat it like an alloc
00391         if (ptr == NULL)
00392         {
00393             return _geRam_DebugAllocate(newsize, pFile, line);
00394         }
00395 
00396         // verify the block
00397         p = ram_verify_block (ptr);
00398         if (p == NULL)
00399         {
00400             return NULL;
00401         }
00402 
00403         // if newsize is NULL, then it's a free and return NULL
00404         if (newsize == 0)
00405         {
00406             geRam_Free (ptr);
00407             return NULL;
00408         }
00409 
00410         // gotta get the size before I realloc it...
00411         size = *((uint32 *)p);
00412 
00413         do
00414         {
00415             NewPtr = (char *)_realloc_dbg(p, newsize+EXTRA_SIZE, _NORMAL_BLOCK, pFile, line);
00416         } while ((NewPtr == NULL) && geRam_DoCriticalCallback ());
00417 
00418         // if allocation failed, return NULL...
00419         if (NewPtr == NULL)
00420         {
00421             return NULL;
00422         }
00423 
00424         geRam_SetupBlock (NewPtr, newsize, DONT_INITIALIZE);
00425 
00426         geRam_CurrentlyUsed += (newsize - size);
00427         if (geRam_CurrentlyUsed > geRam_MaximumUsed)
00428         {
00429             geRam_MaximumUsed = geRam_CurrentlyUsed;
00430         }
00431         assert ((geRam_CurrentlyUsed >= 0) && "free()d more ram than you allocated!");
00432 
00433         return NewPtr + HEADER_SIZE;
00434     }
00435 
00436 #else // NDEBUG
00437 
00438 GENESISAPI     void * geRam_Realloc (void *ptr, uint32 newsize)
00439     {
00440         char *p;
00441         char * NewPtr;
00442         uint32 size;
00443 
00444         // if realloc is called with NULL, just treat it like an alloc
00445         if (ptr == NULL)
00446         {
00447             return geRam_Allocate (newsize);
00448         }
00449 
00450         // verify the block
00451         p = ram_verify_block (ptr);
00452         if (p == NULL)
00453         {
00454             return NULL;
00455         }
00456 
00457         // if newsize is NULL, then it's a free and return NULL
00458         if (newsize == 0)
00459         {
00460             geRam_Free (ptr);
00461             return NULL;
00462         }
00463 
00464         // gotta get the size before I realloc it...
00465         size = *((uint32 *)p);
00466 
00467         do
00468         {
00469             NewPtr = (char *)realloc (p, newsize+EXTRA_SIZE);
00470         } while ((NewPtr == NULL) && geRam_DoCriticalCallback ());
00471 
00472         // if allocation failed, return NULL...
00473         if (NewPtr == NULL)
00474         {
00475             return NULL;
00476         }
00477 
00478         geRam_SetupBlock (NewPtr, newsize, DONT_INITIALIZE);
00479 
00480         geRam_CurrentlyUsed += (newsize - size);
00481         if (geRam_CurrentlyUsed > geRam_MaximumUsed)
00482         {
00483             geRam_MaximumUsed = geRam_CurrentlyUsed;
00484         }
00485         assert ((geRam_CurrentlyUsed >= 0) && "free()d more ram than you allocated!");
00486 
00487         return NewPtr + HEADER_SIZE;
00488     }
00489 
00490 #endif // NDEBUG
00491 
00492 #ifndef NDEBUG
00493 
00494 GENESISAPI void geRam_ReportAllocations(void)
00495 {
00496         _CrtDumpMemoryLeaks();
00497 }
00498 
00499 #endif
00500 
00501     // for external programs that allocate memory some other way.
00502     // Here they can use ram to keep track of the memory.
00503 GENESISAPI     void geRam_AddAllocation (int n, uint32 size)
00504     {
00505         // and update the allocations stuff
00506         geRam_NumberOfAllocations += n;
00507         geRam_CurrentlyUsed += size;
00508 
00509         if (geRam_NumberOfAllocations > geRam_MaximumNumberOfAllocations)
00510         {
00511             geRam_MaximumNumberOfAllocations = geRam_NumberOfAllocations;
00512         }
00513         if (geRam_CurrentlyUsed > geRam_MaximumUsed)
00514         {
00515             geRam_MaximumUsed = geRam_CurrentlyUsed;
00516         }
00517     }
00518 
00519 #endif // MINIMAL_CONFIG
00520 
00521 
00522 #ifndef NDEBUG
00523 geBoolean geRam_IsValidPtr(void *ptr)
00524 {
00525 char * p = ptr;
00526 uint32 size;
00527 
00528         if (p == NULL) return GE_FALSE;
00529 
00530         // make p point to the beginning of the block
00531         p -= HEADER_SIZE;
00532 
00533         // get size from block
00534         size = *((uint32 *)p);
00535 
00536         // check stamp at front
00537         if (memcmp (p+SizeSize, MemStamp, MemStampSize) != 0)
00538         {
00539                 return GE_FALSE;
00540         }
00541 
00542         // and at back
00543         if (memcmp (p+HEADER_SIZE+size, MemStamp, MemStampSize) != 0)
00544         {
00545                 return GE_FALSE;
00546         }
00547 
00548 return GE_TRUE;
00549 }
00550 #endif

Generated on Tue Sep 30 12:36:15 2003 for GTestAndEngine by doxygen 1.3.2