home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / idlib / Heap.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  54KB  |  2,316 lines

  1.  
  2. #include "../idlib/precompiled.h"
  3. #pragma hdrstop
  4.  
  5. // RAVEN BEGIN
  6. // jsinger: attempt to eliminate cross-DLL allocation issues
  7. #ifdef RV_UNIFIED_ALLOCATOR
  8. void *(*Memory::mAllocator)(size_t size);
  9. void (*Memory::mDeallocator)(void *ptr);
  10. size_t (*Memory::mMSize)(void *ptr);
  11. bool Memory::sOK = true;
  12.  
  13. void Memory::Error(const char *errStr)
  14. {
  15.     // If you land here it's because you allocated dynamic memory in a DLL
  16.     // before the Memory system was initialized and this will lead to instability
  17.     // later.  We can't assert here since that will cause the DLL to fail loading
  18.     // so set a break point on the BreakHere=0xdeadbeef; line in order to debug
  19.     int BreakHere;
  20.     BreakHere=0xdeadbeef;
  21.  
  22.     if(common != NULL)
  23.     {
  24.         // this can't be an error or the game will fail to load the DLL and crash.
  25.         // While a crash is the desired behavior, a crash without any meaningful
  26.         // message to the user indicating what went wrong is not desired.
  27.         common->Warning(errStr);
  28.     }
  29.     else
  30.     {
  31.         // if common isn't initialized then we set a flag so that we can notify once it
  32.         // gets initialized
  33.         sOK = false;    
  34.     }
  35. }
  36. #endif
  37. // RAVEN END
  38. #ifndef _RV_MEM_SYS_SUPPORT
  39.  
  40. #ifndef USE_LIBC_MALLOC
  41.     #ifdef _XBOX
  42.         #define USE_LIBC_MALLOC        1
  43.     #else
  44. // RAVEN BEGIN
  45. // scork:  changed this to a 1, since 0 will crash Radiant if you BSP large maps ~3 times because of high watermark-mem that never gets freed.
  46.         #define USE_LIBC_MALLOC        1
  47. // RAVEN END
  48.     #endif
  49. #endif
  50.  
  51. #ifndef CRASH_ON_STATIC_ALLOCATION
  52. //    #define CRASH_ON_STATIC_ALLOCATION
  53. #endif
  54.  
  55. // RAVEN BEGIN
  56. // jnewquist: send all allocations through one place on the Xenon
  57. inline
  58. void *local_malloc(size_t size)
  59. {
  60. #ifndef _XENON
  61. // RAVEN BEGIN
  62. // jsinger: attempt to eliminate cross-DLL allocation issues
  63.     #ifdef RV_UNIFIED_ALLOCATOR
  64.         return Memory::Allocate(size);
  65.     #else
  66.         void *addr = malloc(size);
  67.         if( !addr && size ) {
  68.             common->FatalError( "Out of memory" );
  69.         }
  70.         return( addr );
  71.     #endif  // RV_UNIFIED_ALLOCATOR
  72. // RAVEN END
  73. #else
  74. #endif
  75. }
  76.  
  77. inline
  78. void local_free(void *ptr)
  79. {
  80. #ifndef _XENON
  81. // RAVEN BEGIN
  82. // jsinger: attempt to eliminate cross-DLL allocation issues
  83.     #ifdef RV_UNIFIED_ALLOCATOR
  84.         Memory::Free(ptr);
  85.     #else
  86.         return free(ptr);
  87.     #endif  // RV_UNIFIED_ALLOCATOR
  88. // RAVEN END
  89. #else
  90. #endif
  91. }
  92. // RAVEN END
  93.  
  94. //===============================================================
  95. //
  96. //    idHeap
  97. //
  98. //===============================================================
  99.  
  100. // RAVEN BEGIN
  101. // amccarthy:  Added space in headers in debug for allocation tag storage
  102. #ifdef _DEBUG
  103. #define SMALL_HEADER_SIZE        ( (int) ( sizeof( byte ) + sizeof( byte )+ sizeof( byte ) ) )
  104. #define MEDIUM_HEADER_SIZE        ( (int) ( sizeof( mediumHeapEntry_s ) + sizeof( byte )+ sizeof( byte ) ) )
  105. #define LARGE_HEADER_SIZE        ( (int) ( sizeof( dword * ) + sizeof( byte )+ sizeof( byte ) ) )
  106.  
  107. #define MALLOC_HEADER_SIZE        ( (int) ( 11*sizeof( byte ) + sizeof( byte ) + sizeof( dword )))
  108. #else
  109. #define SMALL_HEADER_SIZE        ( (int) ( sizeof( byte ) + sizeof( byte ) ) )
  110. #define MEDIUM_HEADER_SIZE        ( (int) ( sizeof( mediumHeapEntry_s ) + sizeof( byte ) ) )
  111. #define LARGE_HEADER_SIZE        ( (int) ( sizeof( dword * ) + sizeof( byte ) ) )
  112. #endif
  113. // RAVEN END
  114.  
  115. #define ALIGN_SIZE( bytes )        ( ( (bytes) + ALIGN - 1 ) & ~(ALIGN - 1) )
  116. #define SMALL_ALIGN( bytes )    ( ALIGN_SIZE( (bytes) + SMALL_HEADER_SIZE ) - SMALL_HEADER_SIZE )
  117. #define MEDIUM_SMALLEST_SIZE    ( ALIGN_SIZE( 256 ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE ) )
  118.  
  119. // RAVEN BEGIN
  120. // amccarthy: Allocation tag tracking and reporting
  121. #ifdef _DEBUG
  122. int OutstandingXMallocSize=0;
  123. int OutstandingXMallocTagSize[MA_MAX];
  124. int PeakXMallocTagSize[MA_MAX];
  125. int CurrNumAllocations[MA_MAX];
  126.  
  127.  
  128. // Descriptions that go with each tag.  When updating the tag enum in Heap.h please 
  129. // update this list as well.
  130. // (also update the list in rvHeap.cpp)
  131. char *TagNames[] = {
  132.     "none",
  133.     "New operation",
  134.     "default",
  135.     "Lexer",
  136.     "Parser",
  137.     "AAS routing",
  138.     "Class",
  139.     "Script program",
  140.     "Collision Model",
  141.     "CVar",
  142.     "Decl System",
  143.     "File System",
  144.     "Images",
  145.     "Materials",
  146.     "Models",
  147.     "Fonts",
  148.     "Main renderer",
  149.     "Vertex data",
  150.     "Sound",
  151.     "Window",
  152.     "Event loop",
  153.     "Math - Matrices and vectors",
  154.     "Animation",
  155.     "Dynamic Blocks",
  156.     "Strings",
  157.     "GUI",
  158.     "Effects",
  159.     "Entities",
  160.     "Physics",
  161.     "AI",
  162.     "Network",
  163.     "Not Used"
  164. };
  165.  
  166. // jnewquist: Detect when the tag descriptions are out of sync with the enums.
  167. template<int X>
  168. class TagTableCheck
  169. {
  170. private:
  171.     TagTableCheck();
  172. };
  173.  
  174. template<>
  175. class TagTableCheck<1>
  176. {
  177. };
  178.  
  179. // An error here means you need to synchronize TagNames and Mem_Alloc_Types_t 
  180. TagTableCheck<sizeof(TagNames)/sizeof(char*) == MA_MAX> TagTableCheckedHere;
  181. // An error here means there are too many tags.  No more than 32! 
  182. TagTableCheck<MA_DO_NOT_USE<32> TagMaxCheckedHere;
  183.  
  184. void PrintOutstandingMemAlloc()
  185. {
  186.     
  187.     int i;
  188.     unsigned long totalOutstanding = 0;
  189.     for (i=0;i<MA_MAX;i++)
  190.     {
  191.         if (OutstandingXMallocTagSize[i] || PeakXMallocTagSize[i])
  192.         {
  193.             idLib::common->Printf("%-30s peak %9d curr %9d\n",TagNames[i],PeakXMallocTagSize[i],OutstandingXMallocTagSize[i]);
  194.             totalOutstanding += OutstandingXMallocTagSize[i];
  195.         }
  196.     }
  197.     idLib::common->Printf("Mem_Alloc Outstanding: %d\n",totalOutstanding);
  198.     
  199. }
  200.  
  201. const char *GetMemAllocStats(int tag, int &num, int &size, int &peak)
  202. {
  203.     num = CurrNumAllocations[tag];
  204.     size = OutstandingXMallocTagSize[tag];
  205.     peak = PeakXMallocTagSize[tag];
  206.     return TagNames[tag];
  207. }
  208. #endif
  209.  
  210.  
  211. /*
  212. =================
  213. Mem_ShowMemAlloc_f
  214. =================
  215. */
  216. // amccarthy: print out outstanding mem_allocs.
  217. void Mem_ShowMemAlloc_f( const idCmdArgs &args ) {
  218.  
  219. #ifdef _DEBUG
  220.     PrintOutstandingMemAlloc();
  221. #endif
  222. }
  223.  
  224. // jnewquist: memory tag stack for new/delete
  225. #if defined(_DEBUG) && !defined(ENABLE_INTEL_SMP)
  226. MemScopedTag* MemScopedTag::mTop = NULL;
  227. #endif
  228.  
  229. //RAVEN END
  230.  
  231. class idHeap {
  232.  
  233. public:
  234.                     idHeap( void );
  235.                     ~idHeap( void );                // frees all associated data
  236.     void            Init( void );                    // initialize
  237. //RAVEN BEGIN
  238. //amccarthy:  Added allocation tag
  239.     void *            Allocate( const dword bytes, byte tag );    // allocate memory
  240. //RAVEN END
  241.     void            Free( void *p );                // free memory
  242. //RAVEN BEGIN
  243. //amccarthy:  Added allocation tag
  244.     void *            Allocate16( const dword bytes, byte tag );// allocate 16 byte aligned memory
  245. //RAVEN END
  246.     void            Free16( void *p );                // free 16 byte aligned memory
  247.     dword            Msize( void *p );                // return size of data block
  248.     void            Dump( void  );
  249.  
  250.     void             AllocDefragBlock( void );        // hack for huge renderbumps
  251.  
  252. private:
  253.  
  254.     enum {
  255.         ALIGN = 8                                    // memory alignment in bytes
  256.     };
  257.  
  258.     enum {
  259.         INVALID_ALLOC    = 0xdd,
  260.         SMALL_ALLOC        = 0xaa,                        // small allocation
  261.         MEDIUM_ALLOC    = 0xbb,                        // medium allocaction
  262.         LARGE_ALLOC        = 0xcc                        // large allocaction
  263.     };
  264.  
  265.     struct page_s {                                    // allocation page
  266.         void *                data;                    // data pointer to allocated memory
  267.         dword                dataSize;                // number of bytes of memory 'data' points to
  268.         page_s *            next;                    // next free page in same page manager
  269.         page_s *            prev;                    // used only when allocated
  270.         dword                largestFree;            // this data used by the medium-size heap manager
  271.         void *                firstFree;                // pointer to first free entry
  272.     };
  273.  
  274.     struct mediumHeapEntry_s {
  275.         page_s *            page;                    // pointer to page
  276.         dword                size;                    // size of block
  277.         mediumHeapEntry_s *    prev;                    // previous block
  278.         mediumHeapEntry_s *    next;                    // next block
  279.         mediumHeapEntry_s *    prevFree;                // previous free block
  280.         mediumHeapEntry_s *    nextFree;                // next free block
  281.         dword                freeBlock;                // non-zero if free block
  282.     };
  283.  
  284.     // variables
  285.     void *            smallFirstFree[256/ALIGN+1];    // small heap allocator lists (for allocs of 1-255 bytes)
  286.     page_s *        smallCurPage;                    // current page for small allocations
  287.     dword            smallCurPageOffset;                // byte offset in current page
  288.     page_s *        smallFirstUsedPage;                // first used page of the small heap manager
  289.  
  290.     page_s *        mediumFirstFreePage;            // first partially free page
  291.     page_s *        mediumLastFreePage;                // last partially free page
  292.     page_s *        mediumFirstUsedPage;            // completely used page
  293.  
  294.     page_s *        largeFirstUsedPage;                // first page used by the large heap manager
  295.  
  296.     page_s *        swapPage;
  297.  
  298.     dword            pagesAllocated;                    // number of pages currently allocated
  299.     dword            pageSize;                        // size of one alloc page in bytes
  300.  
  301.     dword            pageRequests;                    // page requests
  302.     dword            OSAllocs;                        // number of allocs made to the OS
  303.  
  304.     int                c_heapAllocRunningCount;
  305.  
  306.     void            *defragBlock;                    // a single huge block that can be allocated
  307.                                                     // at startup, then freed when needed
  308.  
  309.     // methods
  310.     page_s *        AllocatePage( dword bytes );    // allocate page from the OS
  311.     void            FreePage( idHeap::page_s *p );    // free an OS allocated page
  312.  
  313. //RAVEN BEGIN
  314. //amccarthy:  Added allocation tags
  315.     void *            SmallAllocate( dword bytes, byte tag );    // allocate memory (1-255 bytes) from small heap manager
  316.     void            SmallFree( void *ptr );            // free memory allocated by small heap manager
  317.  
  318.     void *            MediumAllocateFromPage( idHeap::page_s *p, dword sizeNeeded, byte tag );
  319.     void *            MediumAllocate( dword bytes, byte tag );    // allocate memory (256-32768 bytes) from medium heap manager
  320.     void            MediumFree( void *ptr );        // free memory allocated by medium heap manager
  321.  
  322.     void *            LargeAllocate( dword bytes, byte tag );    // allocate large block from OS directly
  323.     void            LargeFree( void *ptr );            // free memory allocated by large heap manager
  324. //RAVEN END
  325.     void            ReleaseSwappedPages( void );
  326.     void            FreePageReal( idHeap::page_s *p );
  327. };
  328.  
  329.  
  330. /*
  331. ================
  332. idHeap::Init
  333. ================
  334. */
  335. void idHeap::Init () {
  336.     OSAllocs            = 0;
  337.     pageRequests        = 0;
  338.     pageSize            = 65536 - sizeof( idHeap::page_s );
  339.     pagesAllocated        = 0;                                // reset page allocation counter
  340.  
  341.     largeFirstUsedPage    = NULL;                                // init large heap manager
  342.     swapPage            = NULL;
  343.  
  344.     memset( smallFirstFree, 0, sizeof(smallFirstFree) );    // init small heap manager
  345.     smallFirstUsedPage    = NULL;
  346.     smallCurPage        = AllocatePage( pageSize );
  347.     assert( smallCurPage );
  348.     smallCurPageOffset    = SMALL_ALIGN( 0 );
  349.  
  350.     defragBlock = NULL;
  351.  
  352.     mediumFirstFreePage    = NULL;                                // init medium heap manager
  353.     mediumLastFreePage    = NULL;
  354.     mediumFirstUsedPage    = NULL;
  355.  
  356.     c_heapAllocRunningCount = 0;
  357.  
  358. //RAVEN BEGIN
  359. //amccarthy:  initalize allocation tracking
  360. #ifdef _DEBUG
  361.     OutstandingXMallocSize=0;
  362.     int i;
  363.     for (i=0;i<MA_MAX;i++)
  364.     {
  365.         OutstandingXMallocTagSize[i] = 0;
  366.         PeakXMallocTagSize[i] = 0;
  367.         CurrNumAllocations[i] = 0;
  368.     }
  369. #endif
  370. //RAVEN END
  371. }
  372.  
  373. /*
  374. ================
  375. idHeap::idHeap
  376. ================
  377. */
  378. idHeap::idHeap( void ) {
  379.     Init();
  380. }
  381.  
  382. /*
  383. ================
  384. idHeap::~idHeap
  385.  
  386.   returns all allocated memory back to OS
  387. ================
  388. */
  389. idHeap::~idHeap( void ) {
  390.  
  391.     idHeap::page_s    *p;
  392.  
  393.     if ( smallCurPage ) {
  394.         FreePage( smallCurPage );            // free small-heap current allocation page
  395.     }
  396.     p = smallFirstUsedPage;                    // free small-heap allocated pages 
  397.     while( p ) {
  398.         idHeap::page_s *next = p->next;
  399.         FreePage( p );
  400.         p= next;
  401.     }
  402.  
  403.     p = largeFirstUsedPage;                    // free large-heap allocated pages
  404.     while( p ) {
  405.         idHeap::page_s *next = p->next;
  406.         FreePage( p );
  407.         p = next;
  408.     }
  409.  
  410.     p = mediumFirstFreePage;                // free medium-heap allocated pages
  411.     while( p ) {
  412.         idHeap::page_s *next = p->next;
  413.         FreePage( p );
  414.         p = next;
  415.     }
  416.  
  417.     p = mediumFirstUsedPage;                // free medium-heap allocated completely used pages
  418.     while( p ) {
  419.         idHeap::page_s *next = p->next;
  420.         FreePage( p );
  421.         p = next;
  422.     }
  423.  
  424.     ReleaseSwappedPages();            
  425.  
  426.     if ( defragBlock ) {
  427. // RAVEN BEGIN
  428. // jnewquist: send all allocations through one place on the Xenon
  429.         local_free( defragBlock );
  430. // RAVEN END
  431.     }
  432.  
  433.     assert( pagesAllocated == 0 );
  434. }
  435.  
  436. /*
  437. ================
  438. idHeap::AllocDefragBlock
  439. ================
  440. */
  441. void idHeap::AllocDefragBlock( void ) {
  442.     int        size = 0x40000000;
  443.  
  444.     if ( defragBlock ) {
  445.         return;
  446.     }
  447.     while( 1 ) {
  448. // RAVEN BEGIN
  449. // jnewquist: send all allocations through one place on the Xenon
  450.         defragBlock = local_malloc( size );
  451. // RAVEN END
  452.         if ( defragBlock ) {
  453.             break;
  454.         }
  455.         size >>= 1;
  456.     }
  457.     idLib::common->Printf( "Allocated a %i mb defrag block\n", size / (1024*1024) );
  458. }
  459.  
  460. /*
  461. ================
  462. idHeap::Allocate
  463. ================
  464. */
  465. //RAVEN BEGIN
  466. //amccarthy:  Added allocation tag
  467. void *idHeap::Allocate( const dword bytes, byte tag ) {
  468. //RAVEN END
  469.     if ( !bytes ) {
  470.         return NULL;
  471.     }
  472.     c_heapAllocRunningCount++;
  473.  
  474. #if USE_LIBC_MALLOC
  475.  
  476. //RAVEN BEGIN
  477. //amccarthy:  Added allocation tags for malloc
  478. #ifdef _DEBUG
  479.     byte *p = (byte *)local_malloc( bytes + MALLOC_HEADER_SIZE );
  480.     OutstandingXMallocSize += bytes;
  481.     assert( tag > 0 && tag < MA_MAX);
  482.     OutstandingXMallocTagSize[tag] += bytes;
  483.     if (OutstandingXMallocTagSize[tag] > PeakXMallocTagSize[tag])
  484.     {
  485.         PeakXMallocTagSize[tag] = OutstandingXMallocTagSize[tag];
  486.     }
  487.     CurrNumAllocations[tag]++;
  488.     dword *d = (dword *)p;
  489.     d[0] = bytes;
  490.     byte *ret = p+MALLOC_HEADER_SIZE;
  491.     ret[-1] = tag;
  492.     return ret;
  493. #else
  494.     return local_malloc( bytes);
  495. #endif
  496.  
  497. //RAVEN END
  498.  
  499.     
  500. #else
  501. //RAVEN BEGIN
  502. //amccarthy:  Added allocation tag
  503.     if ( !(bytes & ~255) ) {
  504.         return SmallAllocate( bytes, tag );
  505.     }
  506.     if ( !(bytes & ~32767) ) {
  507.         return MediumAllocate( bytes, tag );
  508.     }
  509.     return LargeAllocate( bytes, tag );
  510.     
  511. //RAVEN END
  512. #endif
  513. }
  514.  
  515. /*
  516. ================
  517. idHeap::Free
  518. ================
  519. */
  520. void idHeap::Free( void *p ) {
  521.     if ( !p ) {
  522.         return;
  523.     }
  524.     c_heapAllocRunningCount--;
  525.  
  526. #if USE_LIBC_MALLOC
  527. //RAVEN BEGIN
  528. //amccarthy:  allocation tracking
  529. #ifdef _DEBUG
  530.     byte *ptr = ((byte *)p) - MALLOC_HEADER_SIZE;
  531.     dword size = ((dword*)(ptr))[0];
  532.     byte tag = ((byte *)(p))[-1];
  533.     OutstandingXMallocSize -= size;
  534.     assert( tag > 0 && tag < MA_MAX);
  535.     OutstandingXMallocTagSize[tag] -= size;
  536.     CurrNumAllocations[tag]--;
  537.     local_free( ptr );
  538. #else
  539.     local_free( p );
  540. #endif
  541. //RAVEN END
  542. #else
  543.     switch( ((byte *)(p))[-1] ) {
  544.         case SMALL_ALLOC: {
  545.             SmallFree( p );
  546.             break;
  547.         }
  548.         case MEDIUM_ALLOC: {
  549.             MediumFree( p );
  550.             break;
  551.         }
  552.         case LARGE_ALLOC: {
  553.             LargeFree( p );
  554.             break;
  555.         }
  556.         default: {
  557.             idLib::common->FatalError( "idHeap::Free: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
  558.             break;
  559.         }
  560.     }
  561.     
  562. #endif
  563. }
  564.  
  565. /*
  566. ================
  567. idHeap::Allocate16
  568. ================
  569. */
  570. //RAVEN BEGIN
  571. //amccarthy:  Added allocation tag
  572. void *idHeap::Allocate16( const dword bytes, byte tag ) {
  573. //RAVEN END
  574.     byte *ptr, *alignedPtr;
  575.  
  576. //RAVEN BEGIN
  577. //amccarthy: Added allocation tag
  578. #ifdef  _DEBUG
  579.     ptr = (byte *) Allocate(bytes+16, tag);
  580.     alignedPtr = (byte *) ( ( (int) ptr ) + 15 & ~15 );
  581.     int padSize = alignedPtr - ptr;
  582.     if ( padSize == 0 ) {
  583.         alignedPtr += 16;
  584.         padSize += 16;
  585.     }
  586.     *((byte *)(alignedPtr - 1)) = (byte) padSize;
  587.  
  588.     assert( ( unsigned int )alignedPtr < 0xff000000 );
  589.  
  590.     return (void *) alignedPtr;
  591. #else
  592. //RAVEN END
  593. // RAVEN BEGIN
  594. // jnewquist: send all allocations through one place on the Xenon
  595.     ptr = (byte *) local_malloc( bytes + 16 + 4 );
  596. // RAVEN END
  597.     if ( !ptr ) {
  598.         if ( defragBlock ) {
  599.             idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", bytes );
  600. // RAVEN BEGIN
  601. // jnewquist: send all allocations through one place on the Xenon
  602.             local_free( defragBlock );
  603. // RAVEN END
  604.             defragBlock = NULL;
  605. // RAVEN BEGIN
  606. // jnewquist: send all allocations through one place on the Xenon
  607.             ptr = (byte *) local_malloc( bytes + 16 + 4 );            
  608. // RAVEN END
  609.             AllocDefragBlock();
  610.         }
  611.         if ( !ptr ) {
  612.             common->FatalError( "malloc failure for %i", bytes );
  613.         }
  614.     }
  615.  
  616.     alignedPtr = (byte *) ( ( (int) ptr ) + 15 & ~15 );
  617.       if ( alignedPtr - ptr < 4 ) {
  618.            alignedPtr += 16;
  619.  
  620.        }
  621.       *((int *)(alignedPtr - 4)) = (int) ptr;
  622.        assert( ( unsigned int )alignedPtr < 0xff000000 );
  623.        return (void *) alignedPtr;
  624.  
  625. //RAVEN BEGIN
  626. //amccarthy: allocation tracking added for debug xbox only.
  627. #endif
  628. //RAVEN END
  629.  
  630. }
  631.  
  632. /*
  633. ================
  634. idHeap::Free16
  635. ================
  636. */
  637. void idHeap::Free16( void *p ) {
  638. //RAVEN BEGIN
  639. //amccarthy: allocation tag
  640. #ifdef _DEBUG
  641.     byte* ptr = (byte*)p;
  642.     int padSize = *(ptr-1);
  643.     ptr -= padSize;
  644.     Free( ptr );
  645. #else
  646.     local_free( (void *) *((int *) (( (byte *) p ) - 4)) );
  647. #endif
  648. //RAVEN END
  649. }
  650.  
  651. /*
  652. ================
  653. idHeap::Msize
  654.  
  655.   returns size of allocated memory block
  656.   p    = pointer to memory block
  657.   Notes:    size may not be the same as the size in the original
  658.             allocation request (due to block alignment reasons).
  659. ================
  660. */
  661. dword idHeap::Msize( void *p ) {
  662.  
  663.     if ( !p ) {
  664.         return 0;
  665.     }
  666.  
  667. #if USE_LIBC_MALLOC
  668.     #ifdef _WINDOWS
  669. //RAVEN BEGIN
  670. //amccarthy:  allocation tracking
  671.         #ifdef _DEBUG
  672.             byte *ptr = ((byte *)p) - MALLOC_HEADER_SIZE;
  673. // jsinger: attempt to eliminate cross-DLL allocation issues
  674.             #ifdef RV_UNIFIED_ALLOCATOR
  675.                         return Memory::MSize(ptr);
  676.             #else
  677.                         return _msize(ptr);
  678.             #endif  // RV_UNIFIED_ALLOCATOR
  679.         #else
  680.             #ifdef RV_UNIFIED_ALLOCATOR
  681.                         return Memory::MSize(p);
  682.             #else
  683.                         return _msize(p);
  684.             #endif  // RV_UNIFIED_ALLOCATOR
  685.         #endif
  686. //RAVEN END
  687.     #else
  688.         return 0;
  689.     #endif
  690. #else
  691.     switch( ((byte *)(p))[-1] ) {
  692.         case SMALL_ALLOC: {
  693.             return SMALL_ALIGN( ((byte *)(p))[-SMALL_HEADER_SIZE] * ALIGN );
  694.         }
  695.         case MEDIUM_ALLOC: {
  696.             return ((mediumHeapEntry_s *)(((byte *)(p)) - ALIGN_SIZE( MEDIUM_HEADER_SIZE )))->size - ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  697.         }
  698.         case LARGE_ALLOC: {
  699.             return ((idHeap::page_s*)(*((dword *)(((byte *)p) - ALIGN_SIZE( LARGE_HEADER_SIZE )))))->dataSize - ALIGN_SIZE( LARGE_HEADER_SIZE );
  700.         }
  701.         default: {
  702.             idLib::common->FatalError( "idHeap::Msize: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
  703.             return 0;
  704.         }
  705.     }
  706. #endif
  707. }
  708.  
  709. /*
  710. ================
  711. idHeap::Dump
  712.  
  713.   dump contents of the heap
  714. ================
  715. */
  716. void idHeap::Dump( void ) {
  717.     idHeap::page_s    *pg;
  718.  
  719.     for ( pg = smallFirstUsedPage; pg; pg = pg->next ) {
  720.         idLib::common->Printf( "%p  bytes %-8d  (in use by small heap)\n", pg->data, pg->dataSize);
  721.     }
  722.  
  723.     if ( smallCurPage ) {
  724.         pg = smallCurPage;
  725.         idLib::common->Printf( "%p  bytes %-8d  (small heap active page)\n", pg->data, pg->dataSize );
  726.     }
  727.  
  728.     for ( pg = mediumFirstUsedPage; pg; pg = pg->next ) {
  729.         idLib::common->Printf( "%p  bytes %-8d  (completely used by medium heap)\n", pg->data, pg->dataSize );
  730.     }
  731.  
  732.     for ( pg = mediumFirstFreePage; pg; pg = pg->next ) {
  733.         idLib::common->Printf( "%p  bytes %-8d  (partially used by medium heap)\n", pg->data, pg->dataSize );
  734.     }
  735.     
  736.     for ( pg = largeFirstUsedPage; pg; pg = pg->next ) {
  737.         idLib::common->Printf( "%p  bytes %-8d  (fully used by large heap)\n", pg->data, pg->dataSize );
  738.     }
  739.  
  740.     idLib::common->Printf( "pages allocated : %d\n", pagesAllocated );
  741. }
  742.  
  743. /*
  744. ================
  745. idHeap::FreePageReal
  746.  
  747.   frees page to be used by the OS
  748.   p    = page to free
  749. ================
  750. */
  751. void idHeap::FreePageReal( idHeap::page_s *p ) {
  752.     assert( p );
  753. // RAVEN BEGIN
  754. // jnewquist: send all allocations through one place on the Xenon
  755.     ::local_free( p );
  756. // RAVEN END
  757. }
  758.  
  759. /*
  760. ================
  761. idHeap::ReleaseSwappedPages
  762.  
  763.   releases the swap page to OS
  764. ================
  765. */
  766. void idHeap::ReleaseSwappedPages () {
  767.     if ( swapPage ) {
  768.         FreePageReal( swapPage );
  769.     }
  770.     swapPage = NULL;
  771. }
  772.  
  773. /*
  774. ================
  775. idHeap::AllocatePage
  776.  
  777.   allocates memory from the OS
  778.   bytes    = page size in bytes
  779.   returns pointer to page
  780. ================
  781. */
  782. idHeap::page_s* idHeap::AllocatePage( dword bytes ) {
  783.     idHeap::page_s*    p;
  784.  
  785.     pageRequests++;
  786.  
  787.     if ( swapPage && swapPage->dataSize == bytes ) {            // if we've got a swap page somewhere
  788.         p            = swapPage;
  789.         swapPage    = NULL;
  790.     }
  791.     else {
  792.         dword size;
  793.  
  794.         size = bytes + sizeof(idHeap::page_s);
  795.  
  796. // RAVEN BEGIN
  797. // jnewquist: send all allocations through one place on the Xenon
  798.         p = (idHeap::page_s *) ::local_malloc( size + ALIGN - 1 );
  799. // RAVEN END
  800.         if ( !p ) {
  801.             if ( defragBlock ) {
  802.                 idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", size + ALIGN - 1 );
  803. // RAVEN BEGIN
  804. // jnewquist: send all allocations through one place on the Xenon
  805.                 local_free( defragBlock );
  806. // RAVEN END
  807.                 defragBlock = NULL;
  808. // RAVEN BEGIN
  809. // jnewquist: send all allocations through one place on the Xenon
  810.                 p = (idHeap::page_s *) ::local_malloc( size + ALIGN - 1 );            
  811. // RAVEN END
  812.                 AllocDefragBlock();
  813.             }
  814.             if ( !p ) {
  815.                 common->FatalError( "malloc failure for %i", bytes );
  816.             }
  817.         }
  818.  
  819.         p->data        = (void *) ALIGN_SIZE( (int)((byte *)(p)) + sizeof( idHeap::page_s ) );
  820.         p->dataSize    = size - sizeof(idHeap::page_s);
  821.         p->firstFree = NULL;
  822.         p->largestFree = 0;
  823.         OSAllocs++;
  824.     }
  825.  
  826.     p->prev = NULL;
  827.     p->next = NULL;
  828.  
  829.     pagesAllocated++;
  830.     
  831.     return p;
  832. }
  833.  
  834. /*
  835. ================
  836. idHeap::FreePage
  837.  
  838.   frees a page back to the operating system
  839.   p    = pointer to page
  840. ================
  841. */
  842. void idHeap::FreePage( idHeap::page_s *p ) {
  843.     assert( p );
  844.  
  845.     if ( p->dataSize == pageSize && !swapPage ) {            // add to swap list?
  846.         swapPage = p;
  847.     }
  848.     else {
  849.         FreePageReal( p );
  850.     }
  851.  
  852.     pagesAllocated--;
  853. }
  854.  
  855. //===============================================================
  856. //
  857. //    small heap code
  858. //
  859. //===============================================================
  860.  
  861. /*
  862. ================
  863. idHeap::SmallAllocate
  864.  
  865.   allocate memory (1-255 bytes) from the small heap manager
  866.   bytes = number of bytes to allocate
  867.   returns pointer to allocated memory
  868. ================
  869. */
  870. //RAVEN BEGIN
  871. //amccarthy:  Added allocation tag
  872. void *idHeap::SmallAllocate( dword bytes, byte tag ) {
  873. //RAVEN END
  874.     // we need the at least sizeof( dword ) bytes for the free list
  875.     if ( bytes < sizeof( dword ) ) {
  876.         bytes = sizeof( dword );
  877.     }
  878.  
  879.     // increase the number of bytes if necessary to make sure the next small allocation is aligned
  880.     bytes = SMALL_ALIGN( bytes );
  881.  
  882.     byte *smallBlock = (byte *)(smallFirstFree[bytes / ALIGN]);
  883.     if ( smallBlock ) {
  884.         dword *link = (dword *)(smallBlock + SMALL_HEADER_SIZE);
  885. //RAVEN BEGIN
  886. //amccarthy:  Added allocation tag
  887. #ifdef _DEBUG
  888.         OutstandingXMallocSize += smallBlock[0];
  889.         assert( tag > 0 && tag < MA_MAX);
  890.         OutstandingXMallocTagSize[tag] += smallBlock[0];
  891.         if (OutstandingXMallocTagSize[tag] > PeakXMallocTagSize[tag])
  892.         {
  893.             PeakXMallocTagSize[tag] = OutstandingXMallocTagSize[tag];
  894.         }
  895.         CurrNumAllocations[tag]++;
  896.         smallBlock[1] = tag;
  897.         smallBlock[2] = SMALL_ALLOC;        // allocation identifier
  898.  
  899. #else
  900.         
  901.         smallBlock[1] = SMALL_ALLOC;        // allocation identifier
  902. #endif
  903.         
  904. //RAVEN END
  905.         smallFirstFree[bytes / ALIGN] = (void *)(*link);
  906.         return (void *)(link);
  907.     }
  908.  
  909.     dword bytesLeft = (long)(pageSize) - smallCurPageOffset;
  910.     // if we need to allocate a new page
  911.     if ( bytes >= bytesLeft ) {
  912.  
  913.         smallCurPage->next    = smallFirstUsedPage;
  914.         smallFirstUsedPage    = smallCurPage;
  915.         smallCurPage        = AllocatePage( pageSize );
  916.         if ( !smallCurPage ) {
  917.             return NULL;
  918.         }
  919.         // make sure the first allocation is aligned
  920.         smallCurPageOffset    = SMALL_ALIGN( 0 );
  921.     }
  922.  
  923.     smallBlock            = ((byte *)smallCurPage->data) + smallCurPageOffset;
  924.     smallBlock[0]        = (byte)(bytes / ALIGN);        // write # of bytes/ALIGN
  925. //RAVEN BEGIN
  926. //amccarthy:  Added allocation tag
  927. #ifdef _DEBUG
  928.         OutstandingXMallocSize += smallBlock[0];
  929.         assert( tag > 0 && tag < MA_MAX);
  930.         OutstandingXMallocTagSize[tag] += smallBlock[0];
  931.         if (OutstandingXMallocTagSize[tag] > PeakXMallocTagSize[tag])
  932.         {
  933.             PeakXMallocTagSize[tag] = OutstandingXMallocTagSize[tag];
  934.         }
  935.         CurrNumAllocations[tag]++;
  936.         smallBlock[1] = tag;
  937.         smallBlock[2] = SMALL_ALLOC;        // allocation identifier
  938.  
  939. #else
  940.         
  941.         smallBlock[1] = SMALL_ALLOC;        // allocation identifier
  942. #endif
  943. //RAVEN END
  944.     smallCurPageOffset  += bytes + SMALL_HEADER_SIZE;    // increase the offset on the current page
  945.     return ( smallBlock + SMALL_HEADER_SIZE );            // skip the first two bytes
  946. }
  947.  
  948. /*
  949. ================
  950. idHeap::SmallFree
  951.  
  952.   frees a block of memory allocated by SmallAllocate() call
  953.   data = pointer to block of memory
  954. ================
  955. */
  956. void idHeap::SmallFree( void *ptr ) {
  957.     ((byte *)(ptr))[-1] = INVALID_ALLOC;
  958.  
  959. //RAVEN BEGIN
  960. //amccarthy:  allocation tracking
  961. #ifdef _DEBUG
  962.     byte tag = ((byte *)(ptr))[-2];
  963.     byte size = ((byte *)(ptr))[-3];
  964.     OutstandingXMallocSize -= size;
  965.     assert( tag > 0 && tag < MA_MAX);
  966.     OutstandingXMallocTagSize[tag] -= size;
  967.     CurrNumAllocations[tag]--;
  968. #endif
  969. //RAVEN END
  970.  
  971.     byte *d = ( (byte *)ptr ) - SMALL_HEADER_SIZE;
  972.     dword *dt = (dword *)ptr;
  973.     // index into the table with free small memory blocks
  974.     dword ix = *d;
  975.  
  976.     // check if the index is correct
  977.     if ( ix > (256 / ALIGN) ) {
  978.         idLib::common->FatalError( "SmallFree: invalid memory block" );
  979.     }
  980.  
  981.     *dt = (dword)smallFirstFree[ix];    // write next index
  982.     smallFirstFree[ix] = (void *)d;        // link
  983. }
  984.  
  985. //===============================================================
  986. //
  987. //    medium heap code
  988. //
  989. //    Medium-heap allocated pages not returned to OS until heap destructor
  990. //    called (re-used instead on subsequent medium-size malloc requests).
  991. //
  992. //===============================================================
  993.  
  994. /*
  995. ================
  996. idHeap::MediumAllocateFromPage
  997.  
  998.   performs allocation using the medium heap manager from a given page
  999.   p                = page
  1000.   sizeNeeded    = # of bytes needed
  1001.   returns pointer to allocated memory
  1002. ================
  1003. */
  1004. //RAVEN BEGIN
  1005. //amccarthy:  Added allocation tag
  1006. void *idHeap::MediumAllocateFromPage( idHeap::page_s *p, dword sizeNeeded, byte tag ) {
  1007. //RAVEN END
  1008.  
  1009.     mediumHeapEntry_s    *best,*nw = NULL;
  1010.     byte                *ret;
  1011.  
  1012.     best = (mediumHeapEntry_s *)(p->firstFree);            // first block is largest
  1013.  
  1014.     assert( best );
  1015.     assert( best->size == p->largestFree );
  1016.     assert( best->size >= sizeNeeded );
  1017.  
  1018.     // if we can allocate another block from this page after allocating sizeNeeded bytes
  1019.     if ( best->size >= (dword)( sizeNeeded + MEDIUM_SMALLEST_SIZE ) ) {
  1020.         nw = (mediumHeapEntry_s *)((byte *)best + best->size - sizeNeeded);
  1021.         nw->page        = p;
  1022.         nw->prev        = best;
  1023.         nw->next        = best->next;
  1024.         nw->prevFree    = NULL;
  1025.         nw->nextFree    = NULL;
  1026.         nw->size        = sizeNeeded;
  1027.         nw->freeBlock    = 0;            // used block
  1028.         if ( best->next ) {
  1029.             best->next->prev = nw;
  1030.         }
  1031.         best->next    = nw;
  1032.         best->size    -= sizeNeeded;
  1033.         
  1034.         p->largestFree = best->size;
  1035.     }
  1036.     else {
  1037.         if ( best->prevFree ) {
  1038.             best->prevFree->nextFree = best->nextFree;
  1039.         }
  1040.         else {
  1041.             p->firstFree = (void *)best->nextFree;
  1042.         }
  1043.         if ( best->nextFree ) {
  1044.             best->nextFree->prevFree = best->prevFree;
  1045.         }
  1046.  
  1047.         best->prevFree  = NULL;
  1048.         best->nextFree  = NULL;
  1049.         best->freeBlock = 0;            // used block
  1050.         nw = best;
  1051.  
  1052.         p->largestFree = 0;
  1053.     }
  1054.  
  1055.     ret        = (byte *)(nw) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  1056. //RAVEN BEGIN
  1057. //amccarthy:  Added allocation tag
  1058. #ifdef _DEBUG
  1059.     OutstandingXMallocSize += nw->size;
  1060.     assert( tag > 0 && tag < MA_MAX);
  1061.     OutstandingXMallocTagSize[tag] += nw->size;
  1062.     if (OutstandingXMallocTagSize[tag] > PeakXMallocTagSize[tag])
  1063.     {
  1064.         PeakXMallocTagSize[tag] = OutstandingXMallocTagSize[tag];
  1065.     }
  1066.     CurrNumAllocations[tag]++;
  1067.     ret[-2] = tag;
  1068. #endif
  1069. //RAVEN END
  1070.     ret[-1] = MEDIUM_ALLOC;        // allocation identifier
  1071.  
  1072.     return (void *)(ret);
  1073. }
  1074.  
  1075. /*
  1076. ================
  1077. idHeap::MediumAllocate
  1078.  
  1079.   allocate memory (256-32768 bytes) from medium heap manager
  1080.   bytes    = number of bytes to allocate
  1081.   returns pointer to allocated memory
  1082. ================
  1083. */
  1084. //RAVEN BEGIN
  1085. //amccarthy:  Added allocation tag
  1086. void *idHeap::MediumAllocate( dword bytes, byte tag ) {
  1087. //RAVEN END
  1088.     idHeap::page_s        *p;
  1089.     void                *data;
  1090.  
  1091.     dword sizeNeeded = ALIGN_SIZE( bytes ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  1092.  
  1093.     // find first page with enough space
  1094.     for ( p = mediumFirstFreePage; p; p = p->next ) {
  1095.         if ( p->largestFree >= sizeNeeded ) {
  1096.             break;
  1097.         }
  1098.     }
  1099.  
  1100.     if ( !p ) {                                // need to allocate new page?
  1101.         p = AllocatePage( pageSize );
  1102.         if ( !p ) {
  1103.             return NULL;                    // malloc failure!
  1104.         }
  1105.         p->prev        = NULL;
  1106.         p->next        = mediumFirstFreePage;
  1107.         if (p->next) {
  1108.             p->next->prev = p;
  1109.         }
  1110.         else {
  1111.             mediumLastFreePage    = p;
  1112.         }
  1113.  
  1114.         mediumFirstFreePage        = p;
  1115.         
  1116.         p->largestFree    = pageSize;
  1117.         p->firstFree    = (void *)p->data;
  1118.  
  1119.         mediumHeapEntry_s *e;
  1120.         e                = (mediumHeapEntry_s *)(p->firstFree);
  1121.         e->page            = p;
  1122.         // make sure ((byte *)e + e->size) is aligned
  1123.         e->size            = pageSize & ~(ALIGN - 1);
  1124.         e->prev            = NULL;
  1125.         e->next            = NULL;
  1126.         e->prevFree        = NULL;
  1127.         e->nextFree        = NULL;
  1128.         e->freeBlock    = 1;
  1129.     }
  1130.  
  1131. //RAVEN BEGIN
  1132. //amccarthy:  Added allocation tag
  1133.     data = MediumAllocateFromPage( p, sizeNeeded, tag );        // allocate data from page
  1134. //RAVEN END
  1135.  
  1136.     // if the page can no longer serve memory, move it away from free list
  1137.     // (so that it won't slow down the later alloc queries)
  1138.     // this modification speeds up the pageWalk from O(N) to O(sqrt(N))
  1139.     // a call to free may swap this page back to the free list
  1140.  
  1141.     if ( p->largestFree < MEDIUM_SMALLEST_SIZE ) {
  1142.         if ( p == mediumLastFreePage ) {
  1143.             mediumLastFreePage = p->prev;
  1144.         }
  1145.  
  1146.         if ( p == mediumFirstFreePage ) {
  1147.             mediumFirstFreePage = p->next;
  1148.         }
  1149.  
  1150.         if ( p->prev ) {
  1151.             p->prev->next = p->next;
  1152.         }
  1153.         if ( p->next ) {
  1154.             p->next->prev = p->prev;
  1155.         }
  1156.  
  1157.         // link to "completely used" list
  1158.         p->prev = NULL;
  1159.         p->next = mediumFirstUsedPage;
  1160.         if ( p->next ) {
  1161.             p->next->prev = p;
  1162.         }
  1163.         mediumFirstUsedPage = p;
  1164.         return data;
  1165.     } 
  1166.  
  1167.     // re-order linked list (so that next malloc query starts from current
  1168.     // matching block) -- this speeds up both the page walks and block walks
  1169.  
  1170.     if ( p != mediumFirstFreePage ) {
  1171.         assert( mediumLastFreePage );
  1172.         assert( mediumFirstFreePage );
  1173.         assert( p->prev);
  1174.  
  1175.         mediumLastFreePage->next    = mediumFirstFreePage;
  1176.         mediumFirstFreePage->prev    = mediumLastFreePage;
  1177.         mediumLastFreePage            = p->prev;
  1178.         p->prev->next                = NULL;
  1179.         p->prev                        = NULL;
  1180.         mediumFirstFreePage            = p;
  1181.     }
  1182.  
  1183.     return data;
  1184. }
  1185.  
  1186. /*
  1187. ================
  1188. idHeap::MediumFree
  1189.  
  1190.   frees a block allocated by the medium heap manager
  1191.   ptr    = pointer to data block
  1192. ================
  1193. */
  1194. void idHeap::MediumFree( void *ptr ) {
  1195.     ((byte *)(ptr))[-1] = INVALID_ALLOC;
  1196.  
  1197.     mediumHeapEntry_s    *e = (mediumHeapEntry_s *)((byte *)ptr - ALIGN_SIZE( MEDIUM_HEADER_SIZE ));
  1198.     idHeap::page_s        *p = e->page;
  1199.     bool                isInFreeList;
  1200.  
  1201.     isInFreeList = p->largestFree >= MEDIUM_SMALLEST_SIZE;
  1202.  
  1203.     assert( e->size );
  1204.     assert( e->freeBlock == 0 );
  1205.  
  1206. //RAVEN BEGIN
  1207. //amccarthy:  allocation tracking
  1208. #ifdef _DEBUG
  1209.     byte tag = ((byte *)(ptr))[-2];
  1210.     dword size = e->size;
  1211.     OutstandingXMallocSize -= size;
  1212.     assert( tag > 0 && tag < MA_MAX);
  1213.     OutstandingXMallocTagSize[tag] -= size;
  1214.     CurrNumAllocations[tag]--;
  1215. #endif
  1216. //RAVEN END
  1217.  
  1218.     mediumHeapEntry_s *prev = e->prev;
  1219.  
  1220.     // if the previous block is free we can merge
  1221.     if ( prev && prev->freeBlock ) {
  1222.         prev->size += e->size;
  1223.         prev->next = e->next;
  1224.         if ( e->next ) {
  1225.             e->next->prev = prev;
  1226.         }
  1227.         e = prev;
  1228.     }
  1229.     else {
  1230.         e->prevFree        = NULL;                // link to beginning of free list
  1231.         e->nextFree        = (mediumHeapEntry_s *)p->firstFree;
  1232.         if ( e->nextFree ) {
  1233.             assert( !(e->nextFree->prevFree) );
  1234.             e->nextFree->prevFree = e;
  1235.         }
  1236.  
  1237.         p->firstFree    = e;
  1238.         p->largestFree    = e->size;
  1239.         e->freeBlock    = 1;                // mark block as free
  1240.     }
  1241.             
  1242.     mediumHeapEntry_s *next = e->next;
  1243.  
  1244.     // if the next block is free we can merge
  1245.     if ( next && next->freeBlock ) {
  1246.         e->size += next->size;
  1247.         e->next = next->next;
  1248.         
  1249.         if ( next->next ) {
  1250.             next->next->prev = e;
  1251.         }
  1252.         
  1253.         if ( next->prevFree ) {
  1254.             next->prevFree->nextFree = next->nextFree;
  1255.         }
  1256.         else {
  1257.             assert( next == p->firstFree );
  1258.             p->firstFree = next->nextFree;
  1259.         }
  1260.  
  1261.         if ( next->nextFree ) {
  1262.             next->nextFree->prevFree = next->prevFree;
  1263.         }
  1264.     }
  1265.  
  1266.     if ( p->firstFree ) {
  1267.         p->largestFree = ((mediumHeapEntry_s *)(p->firstFree))->size;
  1268.     }
  1269.     else {
  1270.         p->largestFree = 0;
  1271.     }
  1272.  
  1273.     // did e become the largest block of the page ?
  1274.  
  1275.     if ( e->size > p->largestFree ) {
  1276.         assert( e != p->firstFree );
  1277.         p->largestFree = e->size;
  1278.  
  1279.         if ( e->prevFree ) {
  1280.             e->prevFree->nextFree = e->nextFree;
  1281.         }
  1282.         if ( e->nextFree ) {
  1283.             e->nextFree->prevFree = e->prevFree;
  1284.         }
  1285.         
  1286.         e->nextFree = (mediumHeapEntry_s *)p->firstFree;
  1287.         e->prevFree = NULL;
  1288.         if ( e->nextFree ) {
  1289.             e->nextFree->prevFree = e;
  1290.         }
  1291.         p->firstFree = e;
  1292.     }
  1293.  
  1294.     // if page wasn't in free list (because it was near-full), move it back there
  1295.     if ( !isInFreeList ) {
  1296.  
  1297.         // remove from "completely used" list
  1298.         if ( p->prev ) {
  1299.             p->prev->next = p->next;
  1300.         }
  1301.         if ( p->next ) {
  1302.             p->next->prev = p->prev;
  1303.         }
  1304.         if ( p == mediumFirstUsedPage ) {
  1305.             mediumFirstUsedPage = p->next;
  1306.         }
  1307.  
  1308.         p->next = NULL;
  1309.         p->prev = mediumLastFreePage;
  1310.  
  1311.         if ( mediumLastFreePage ) {
  1312.             mediumLastFreePage->next = p;
  1313.         }
  1314.         mediumLastFreePage = p;
  1315.         if ( !mediumFirstFreePage ) {
  1316.             mediumFirstFreePage = p;
  1317.         }
  1318.     } 
  1319. }
  1320.  
  1321. //===============================================================
  1322. //
  1323. //    large heap code
  1324. //
  1325. //===============================================================
  1326.  
  1327. /*
  1328. ================
  1329. idHeap::LargeAllocate
  1330.  
  1331.   allocates a block of memory from the operating system
  1332.   bytes    = number of bytes to allocate
  1333.   returns pointer to allocated memory
  1334. ================
  1335. */
  1336. //RAVEN BEGIN
  1337. //amccarthy:  Added allocation tag
  1338. void *idHeap::LargeAllocate( dword bytes, byte tag ) {
  1339. //RAVEN END
  1340.     idHeap::page_s *p = AllocatePage( bytes + ALIGN_SIZE( LARGE_HEADER_SIZE ) );
  1341.  
  1342.     assert( p );
  1343.  
  1344.     if ( !p ) {
  1345.         return NULL;
  1346.     }
  1347.  
  1348.     byte *    d    = (byte*)(p->data) + ALIGN_SIZE( LARGE_HEADER_SIZE );
  1349.     dword *    dw    = (dword*)(d - ALIGN_SIZE( LARGE_HEADER_SIZE ));
  1350.     dw[0]        = (dword)p;                // write pointer back to page table
  1351. //RAVEN BEGIN
  1352. //amccarthy:  Added allocation tag
  1353. #ifdef _DEBUG
  1354.     OutstandingXMallocSize += p->dataSize;
  1355.     assert( tag > 0 && tag < MA_MAX);
  1356.     OutstandingXMallocTagSize[tag] += p->dataSize;
  1357.     if (OutstandingXMallocTagSize[tag] > PeakXMallocTagSize[tag])
  1358.     {
  1359.         PeakXMallocTagSize[tag] = OutstandingXMallocTagSize[tag];
  1360.     }
  1361.     CurrNumAllocations[tag]++;
  1362.     d[-2]        = tag;
  1363. #endif
  1364. //RAVEN END
  1365.     d[-1]        = LARGE_ALLOC;            // allocation identifier
  1366.  
  1367.     // link to 'large used page list'
  1368.     p->prev = NULL;
  1369.     p->next = largeFirstUsedPage;
  1370.     if ( p->next ) {
  1371.         p->next->prev = p;
  1372.     }
  1373.     largeFirstUsedPage = p;
  1374.  
  1375.     return (void *)(d);
  1376. }
  1377.  
  1378. /*
  1379. ================
  1380. idHeap::LargeFree
  1381.  
  1382.   frees a block of memory allocated by the 'large memory allocator'
  1383.   p    = pointer to allocated memory
  1384. ================
  1385. */
  1386. void idHeap::LargeFree( void *ptr) {
  1387.     idHeap::page_s*    pg;
  1388.  
  1389.     ((byte *)(ptr))[-1] = INVALID_ALLOC;
  1390.  
  1391.     // get page pointer
  1392.     pg = (idHeap::page_s *)(*((dword *)(((byte *)ptr) - ALIGN_SIZE( LARGE_HEADER_SIZE ))));
  1393.  
  1394.     //RAVEN BEGIN
  1395. //amccarthy:  allocation tracking
  1396. #ifdef _DEBUG
  1397.     byte tag = ((byte *)(ptr))[-2];
  1398.     dword size = pg->dataSize;
  1399.     OutstandingXMallocSize -= size;
  1400.     assert( tag > 0 && tag < MA_MAX);
  1401.     OutstandingXMallocTagSize[tag] -= size;
  1402.     CurrNumAllocations[tag]--;
  1403. #endif
  1404. //RAVEN END
  1405.  
  1406.     // unlink from doubly linked list
  1407.     if ( pg->prev ) {
  1408.         pg->prev->next = pg->next;
  1409.     }
  1410.     if ( pg->next ) {
  1411.         pg->next->prev = pg->prev;
  1412.     }
  1413.     if ( pg == largeFirstUsedPage ) {
  1414.         largeFirstUsedPage = pg->next;
  1415.     }
  1416.     pg->next = pg->prev = NULL;
  1417.  
  1418.     FreePage(pg);
  1419. }
  1420.  
  1421. //===============================================================
  1422. //
  1423. //    memory allocation all in one place
  1424. //
  1425. //===============================================================
  1426.  
  1427. #undef new
  1428.  
  1429. static idHeap *            mem_heap = NULL;
  1430. static memoryStats_t    mem_total_allocs = { 0, 0x0fffffff, -1, 0 };
  1431. static memoryStats_t    mem_frame_allocs;
  1432. static memoryStats_t    mem_frame_frees;
  1433.  
  1434. /*
  1435. ==================
  1436. Mem_ClearFrameStats
  1437. ==================
  1438. */
  1439. void Mem_ClearFrameStats( void ) {
  1440.     mem_frame_allocs.num = mem_frame_frees.num = 0;
  1441.     mem_frame_allocs.minSize = mem_frame_frees.minSize = 0x0fffffff;
  1442.     mem_frame_allocs.maxSize = mem_frame_frees.maxSize = -1;
  1443.     mem_frame_allocs.totalSize = mem_frame_frees.totalSize = 0;
  1444. }
  1445.  
  1446. /*
  1447. ==================
  1448. Mem_GetFrameStats
  1449. ==================
  1450. */
  1451. void Mem_GetFrameStats( memoryStats_t &allocs, memoryStats_t &frees ) {
  1452.     allocs = mem_frame_allocs;
  1453.     frees = mem_frame_frees;
  1454. }
  1455.  
  1456. /*
  1457. ==================
  1458. Mem_GetStats
  1459. ==================
  1460. */
  1461. void Mem_GetStats( memoryStats_t &stats ) {
  1462.     stats = mem_total_allocs;
  1463. }
  1464.  
  1465. /*
  1466. ==================
  1467. Mem_UpdateStats
  1468. ==================
  1469. */
  1470. void Mem_UpdateStats( memoryStats_t &stats, int size ) {
  1471.     stats.num++;
  1472.     if ( size < stats.minSize ) {
  1473.         stats.minSize = size;
  1474.     }
  1475.     if ( size > stats.maxSize ) {
  1476.         stats.maxSize = size;
  1477.     }
  1478.     stats.totalSize += size;
  1479. }
  1480.  
  1481. /*
  1482. ==================
  1483. Mem_UpdateAllocStats
  1484. ==================
  1485. */
  1486. void Mem_UpdateAllocStats( int size ) {
  1487.     Mem_UpdateStats( mem_frame_allocs, size );
  1488.     Mem_UpdateStats( mem_total_allocs, size );
  1489. }
  1490.  
  1491. /*
  1492. ==================
  1493. Mem_UpdateFreeStats
  1494. ==================
  1495. */
  1496. void Mem_UpdateFreeStats( int size ) {
  1497.     Mem_UpdateStats( mem_frame_frees, size );
  1498.     mem_total_allocs.num--;
  1499.     mem_total_allocs.totalSize -= size;
  1500. }
  1501.  
  1502.  
  1503. #ifndef ID_DEBUG_MEMORY
  1504.  
  1505. /*
  1506. ==================
  1507. Mem_Alloc
  1508. ==================
  1509. */
  1510. // RAVEN BEGIN
  1511. // amccarthy: Added allocation tag
  1512. void *Mem_Alloc( const int size, byte tag ) {
  1513.     if ( !size ) {
  1514.         return NULL;
  1515.     }
  1516.     if ( !mem_heap ) {
  1517. #ifdef CRASH_ON_STATIC_ALLOCATION
  1518.         *((int*)0x0) = 1;
  1519. #endif
  1520. // jnewquist: send all allocations through one place on the Xenon
  1521.         return local_malloc( size );
  1522.     }
  1523. // amccarthy: Added allocation tag
  1524.     void *mem = mem_heap->Allocate( size, tag );
  1525.     Mem_UpdateAllocStats( mem_heap->Msize( mem ) );
  1526.     return mem;
  1527. }
  1528. // RAVEN END
  1529.  
  1530. /*
  1531. ==================
  1532. Mem_Free
  1533. ==================
  1534. */
  1535. void Mem_Free( void *ptr ) {
  1536.     if ( !ptr ) {
  1537.         return;
  1538.     }
  1539.     if ( !mem_heap ) {
  1540. #ifdef CRASH_ON_STATIC_ALLOCATION
  1541.         *((int*)0x0) = 1;
  1542. #endif
  1543. // RAVEN BEGIN
  1544. // jnewquist: send all allocations through one place on the Xenon
  1545.         local_free( ptr );
  1546. // RAVEN END
  1547.         return;
  1548.     }
  1549.     Mem_UpdateFreeStats( mem_heap->Msize( ptr ) );
  1550.      mem_heap->Free( ptr );
  1551. }
  1552.  
  1553. /*
  1554. ==================
  1555. Mem_Alloc16
  1556. ==================
  1557. */
  1558. // RAVEN BEGIN
  1559. // amccarthy: Added allocation tag
  1560. void *Mem_Alloc16( const int size, byte tag ) {
  1561.     if ( !size ) {
  1562.         return NULL;
  1563.     }
  1564.     if ( !mem_heap ) {
  1565. #ifdef CRASH_ON_STATIC_ALLOCATION
  1566.         *((int*)0x0) = 1;
  1567. #endif
  1568. // jnewquist: send all allocations through one place on the Xenon
  1569.         return local_malloc( size );
  1570.     }
  1571.  
  1572. // amccarthy: Added allocation tag
  1573.     void *mem = mem_heap->Allocate16( size, tag );
  1574.     // make sure the memory is 16 byte aligned
  1575.     assert( ( ((int)mem) & 15) == 0 );
  1576.     return mem;
  1577. }
  1578. // RAVEN END
  1579.  
  1580. /*
  1581. ==================
  1582. Mem_Free16
  1583. ==================
  1584. */
  1585. void Mem_Free16( void *ptr ) {
  1586.     if ( !ptr ) {
  1587.         return;
  1588.     }
  1589.     if ( !mem_heap ) {
  1590. #ifdef CRASH_ON_STATIC_ALLOCATION
  1591.         *((int*)0x0) = 1;
  1592. #endif
  1593. // RAVEN BEGIN
  1594. // jnewquist: send all allocations through one place on the Xenon
  1595.         local_free( ptr );
  1596. // RAVEN END
  1597.         return;
  1598.     }
  1599.     // make sure the memory is 16 byte aligned
  1600.     assert( ( ((int)ptr) & 15) == 0 );
  1601.      mem_heap->Free16( ptr );
  1602. }
  1603.  
  1604. /*
  1605. ==================
  1606. Mem_ClearedAlloc
  1607. ==================
  1608. */
  1609. // RAVEN BEGIN
  1610. // amccarthy: Added allocation tag
  1611. void *Mem_ClearedAlloc( const int size, byte tag ) {
  1612.     void *mem = Mem_Alloc( size, tag );
  1613. // RAVEN END
  1614.     SIMDProcessor->Memset( mem, 0, size );
  1615.     return mem;
  1616. }
  1617.  
  1618. /*
  1619. ==================
  1620. Mem_ClearedAlloc
  1621. ==================
  1622. */
  1623. void Mem_AllocDefragBlock( void ) {
  1624.     mem_heap->AllocDefragBlock();
  1625. }
  1626.  
  1627. /*
  1628. ==================
  1629. Mem_CopyString
  1630. ==================
  1631. */
  1632. char *Mem_CopyString( const char *in ) {
  1633.     char    *out;
  1634.  
  1635. // RAVEN BEGIN
  1636. // amccarthy: Added allocation tag
  1637.     out = (char *)Mem_Alloc( strlen(in) + 1, MA_STRING );
  1638. // RAVEN END
  1639.     strcpy( out, in );
  1640.     return out;
  1641. }
  1642.  
  1643. /*
  1644. ==================
  1645. Mem_Dump_f
  1646. ==================
  1647. */
  1648. void Mem_Dump_f( const idCmdArgs &args ) {
  1649. }
  1650.  
  1651. /*
  1652. ==================
  1653. Mem_DumpCompressed_f
  1654. ==================
  1655. */
  1656. void Mem_DumpCompressed_f( const idCmdArgs &args ) {
  1657. }
  1658.  
  1659. /*
  1660. ==================
  1661. Mem_Init
  1662. ==================
  1663. */
  1664. void Mem_Init( void ) {
  1665. // RAVEN BEGIN
  1666. // jnewquist: Tag scope and callees to track allocations using "new".
  1667.     MEM_SCOPED_TAG(tag,MA_DEFAULT);
  1668. // RAVEN END
  1669.     mem_heap = new idHeap;
  1670.     Mem_ClearFrameStats();
  1671. }
  1672.  
  1673. /*
  1674. ==================
  1675. Mem_Shutdown
  1676. ==================
  1677. */
  1678. void Mem_Shutdown( void ) {
  1679.     idHeap *m = mem_heap;
  1680.     mem_heap = NULL;
  1681.     delete m;
  1682. }
  1683.  
  1684. /*
  1685. ==================
  1686. Mem_EnableLeakTest
  1687. ==================
  1688. */
  1689. void Mem_EnableLeakTest( const char *name ) {
  1690. }
  1691.  
  1692.  
  1693. #else /* !ID_DEBUG_MEMORY */
  1694.  
  1695. #undef        Mem_Alloc
  1696. #undef        Mem_ClearedAlloc
  1697. #undef        Com_ClearedReAlloc
  1698. #undef        Mem_Free
  1699. #undef        Mem_CopyString
  1700. #undef        Mem_Alloc16
  1701. #undef        Mem_Free16
  1702.  
  1703. #define MAX_CALLSTACK_DEPTH        6
  1704.  
  1705. // RAVEN BEGIN
  1706. // jnewquist: Add a signature to avoid misinterpreting an early allocation
  1707. // size of this struct must be a multiple of 16 bytes
  1708. typedef struct debugMemory_s {
  1709.     unsigned short            signature;
  1710.     unsigned short            lineNumber;
  1711.     const char *            fileName;
  1712.     int                        frameNumber;
  1713.     int                        size;
  1714.     address_t                callStack[MAX_CALLSTACK_DEPTH];
  1715.     struct debugMemory_s *    prev;
  1716.     struct debugMemory_s *    next;
  1717. } debugMemory_t;
  1718. // RAVEN END
  1719.  
  1720. static debugMemory_t *    mem_debugMemory = NULL;
  1721. static char                mem_leakName[256] = "";
  1722.  
  1723. /*
  1724. ==================
  1725. Mem_CleanupFileName
  1726. ==================
  1727. */
  1728. const char *Mem_CleanupFileName( const char *fileName ) {
  1729.     int i1, i2;
  1730.     idStr newFileName;
  1731.     static char newFileNames[4][MAX_STRING_CHARS];
  1732.     static int index;
  1733.  
  1734.     newFileName = fileName;
  1735.     newFileName.BackSlashesToSlashes();
  1736.     i1 = newFileName.Find( "neo", false );
  1737.     if ( i1 >= 0 ) {
  1738.         i1 = newFileName.Find( "/", false, i1 );
  1739.         newFileName = newFileName.Right( newFileName.Length() - ( i1 + 1 ) );
  1740.     }
  1741.     while( 1 ) {
  1742.         i1 = newFileName.Find( "/../" );
  1743.         if ( i1 <= 0 ) {
  1744.             break;
  1745.         }
  1746.         i2 = i1 - 1;
  1747.         while( i2 > 1 && newFileName[i2-1] != '/' ) {
  1748.             i2--;
  1749.         }
  1750.         newFileName = newFileName.Left( i2 - 1 ) + newFileName.Right( newFileName.Length() - ( i1 + 4 ) );
  1751.     }
  1752.     index = ( index + 1 ) & 3;
  1753.     strncpy( newFileNames[index], newFileName.c_str(), sizeof( newFileNames[index] ) );
  1754.     return newFileNames[index];
  1755. }
  1756.  
  1757. /*
  1758. ==================
  1759. Mem_Dump
  1760. ==================
  1761. */
  1762. void Mem_Dump( const char *fileName ) {
  1763.     int i, numBlocks, totalSize;
  1764.     char dump[32], *ptr;
  1765.     debugMemory_t *b;
  1766.     idStr module, funcName;
  1767.     FILE *f;
  1768.  
  1769.     f = fopen( fileName, "wb" );
  1770.     if ( !f ) {
  1771.         return;
  1772.     }
  1773.  
  1774.     totalSize = 0;
  1775.     for ( numBlocks = 0, b = mem_debugMemory; b; b = b->next, numBlocks++ ) {
  1776.         ptr = ((char *) b) + sizeof(debugMemory_t);
  1777.         totalSize += b->size;
  1778.         for ( i = 0; i < (sizeof(dump)-1) && i < b->size; i++) {
  1779.             if ( ptr[i] >= 32 && ptr[i] < 127 ) {
  1780.                 dump[i] = ptr[i];
  1781.             } else {
  1782.                 dump[i] = '_';
  1783.             }
  1784.         }
  1785.         dump[i] = '\0';
  1786.         if ( ( b->size >> 10 ) != 0 ) {
  1787.             fprintf( f, "size: %6d KB: %s, line: %d [%s], call stack: %s\r\n", ( b->size >> 10 ), Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
  1788.         }
  1789.         else {
  1790.             fprintf( f, "size: %7d B: %s, line: %d [%s], call stack: %s\r\n", b->size, Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
  1791.         }
  1792.     }
  1793.  
  1794.     idLib::sys->ShutdownSymbols();
  1795.  
  1796.     fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
  1797.     fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
  1798.  
  1799.     fclose( f );
  1800. }
  1801.  
  1802.  
  1803.  
  1804. /*
  1805. ==================
  1806. Mem_Dump_f
  1807. ==================
  1808. */
  1809. void Mem_Dump_f( const idCmdArgs &args ) {
  1810.     const char *fileName;
  1811.  
  1812.     if ( args.Argc() >= 2 ) {
  1813.         fileName = args.Argv( 1 );
  1814.     }
  1815.     else {
  1816.         fileName = "memorydump.txt";
  1817.     }
  1818.     Mem_Dump( fileName );
  1819. }
  1820.  
  1821. /*
  1822. ==================
  1823. Mem_DumpCompressed
  1824. ==================
  1825. */
  1826.  
  1827. typedef struct allocInfo_s {
  1828.     const char *            fileName;
  1829.     int                        lineNumber;
  1830.     int                        size;
  1831.     int                        numAllocs;
  1832.     address_t                callStack[MAX_CALLSTACK_DEPTH];
  1833.     struct allocInfo_s *    next;
  1834. } allocInfo_t;
  1835.  
  1836. typedef enum {
  1837.     MEMSORT_SIZE,
  1838.     MEMSORT_LOCATION,
  1839.     MEMSORT_NUMALLOCS,
  1840.     MEMSORT_CALLSTACK
  1841. } memorySortType_t;
  1842.  
  1843. void Mem_DumpCompressed( const char *fileName, memorySortType_t memSort, int sortCallStack, int numFrames ) {
  1844.     int numBlocks, totalSize, r, j;
  1845.     debugMemory_t *b;
  1846.     allocInfo_t *a, *nexta, *allocInfo = NULL, *sortedAllocInfo = NULL, *prevSorted, *nextSorted;
  1847.     idStr module, funcName;
  1848.  
  1849.     // build list with memory allocations
  1850.     totalSize = 0;
  1851.     numBlocks = 0;
  1852. // RAVEN BEGIN
  1853.     nextSorted = NULL;
  1854. // RAVEN END
  1855.  
  1856.     for ( b = mem_debugMemory; b; b = b->next ) {
  1857.  
  1858.         if ( numFrames && b->frameNumber < idLib::frameNumber - numFrames ) {
  1859.             continue;
  1860.         }
  1861.  
  1862.         numBlocks++;
  1863.         totalSize += b->size;
  1864.  
  1865.         // search for an allocation from the same source location
  1866.         for ( a = allocInfo; a; a = a->next ) {
  1867.             if ( a->lineNumber != b->lineNumber ) {
  1868.                 continue;
  1869.             }
  1870. // RAVEN BEGIN
  1871. // dluetscher: removed the call stack info for better consolidation of info and speed of dump
  1872. #ifndef _XENON
  1873.             for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
  1874.                 if ( a->callStack[j] != b->callStack[j] ) {
  1875.                     break;
  1876.                 }
  1877.             }
  1878.             if ( j < MAX_CALLSTACK_DEPTH ) {
  1879.                 continue;
  1880.             }
  1881. #endif
  1882. // RAVEN END
  1883.             if ( idStr::Cmp( a->fileName, b->fileName ) != 0 ) {
  1884.                 continue;
  1885.             }
  1886.             a->numAllocs++;
  1887.             a->size += b->size;
  1888.             break;
  1889.         }
  1890.  
  1891.         // if this is an allocation from a new source location
  1892.         if ( !a ) {
  1893. // RAVEN BEGIN
  1894. // jnewquist: send all allocations through one place on the Xenon
  1895.             a = (allocInfo_t *) ::local_malloc( sizeof( allocInfo_t ) );
  1896. // RAVEN END
  1897.             a->fileName = b->fileName;
  1898.             a->lineNumber = b->lineNumber;
  1899.             a->size = b->size;
  1900.             a->numAllocs = 1;
  1901.             for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
  1902.                 a->callStack[j] = b->callStack[j];
  1903.             }
  1904.             a->next = allocInfo;
  1905.             allocInfo = a;
  1906.         }
  1907.     }
  1908.  
  1909.     // sort list
  1910.     for ( a = allocInfo; a; a = nexta ) {
  1911.         nexta = a->next;
  1912.  
  1913.         prevSorted = NULL;
  1914.         switch( memSort ) {
  1915.             // sort on size
  1916.             case MEMSORT_SIZE: {
  1917.                 for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1918.                     if ( a->size > nextSorted->size ) {
  1919.                         break;
  1920.                     }
  1921.                     prevSorted = nextSorted;
  1922.                 }
  1923.                 break;
  1924.             }
  1925.             // sort on file name and line number
  1926.             case MEMSORT_LOCATION: {
  1927.                 for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1928.                     r = idStr::Cmp( Mem_CleanupFileName( a->fileName ), Mem_CleanupFileName( nextSorted->fileName ) );
  1929.                     if ( r < 0 || ( r == 0 && a->lineNumber < nextSorted->lineNumber ) ) {
  1930.                         break;
  1931.                     }
  1932.                     prevSorted = nextSorted;
  1933.                 }
  1934.                 break;
  1935.             }
  1936.             // sort on the number of allocations
  1937.             case MEMSORT_NUMALLOCS: {
  1938.                 for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1939.                     if ( a->numAllocs > nextSorted->numAllocs ) {
  1940.                         break;
  1941.                     }
  1942.                     prevSorted = nextSorted;
  1943.                 }
  1944.                 break;
  1945.             }
  1946.             // sort on call stack
  1947.             case MEMSORT_CALLSTACK: {
  1948.                 for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1949.                     if ( a->callStack[sortCallStack] < nextSorted->callStack[sortCallStack] ) {
  1950.                         break;
  1951.                     }
  1952.                     prevSorted = nextSorted;
  1953.                 }
  1954.                 break;
  1955.             }
  1956.         }
  1957.         if ( !prevSorted ) {
  1958.             a->next = sortedAllocInfo;
  1959.             sortedAllocInfo = a;
  1960.         }
  1961.         else {
  1962.             prevSorted->next = a;
  1963.             a->next = nextSorted;
  1964.         }
  1965.     }
  1966.  
  1967. // RAVEN BEGIN
  1968. // dluetscher: changed xenon version to output anything above 1K to the console 
  1969. #ifdef _XENON
  1970.     // write list to debug output and console
  1971.     for ( a = sortedAllocInfo; a; a = nexta ) {
  1972.         nexta = a->next;
  1973.         if ( (a->size >> 10) > 0 ) {
  1974.  
  1975.             idLib::common->Printf("size: %6d KB, allocs: %5d: %s, line: %d, call stack: %s\r\n",
  1976.                         (a->size >> 10), a->numAllocs, Mem_CleanupFileName(a->fileName),
  1977.                                 a->lineNumber, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
  1978.  
  1979.         }
  1980.         ::local_free( a );
  1981.     }
  1982.  
  1983.     idLib::sys->ShutdownSymbols();
  1984.  
  1985.     idLib::common->Printf("%8d total memory blocks allocated\r\n", numBlocks );
  1986.     idLib::common->Printf("%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
  1987. #else
  1988. // RAVEN END
  1989.     FILE *f;
  1990.  
  1991.     f = fopen( fileName, "wb" );
  1992.     if ( !f ) {
  1993.         return;
  1994.     }
  1995.  
  1996.     // write list to file
  1997.     for ( a = sortedAllocInfo; a; a = nexta ) {
  1998.         nexta = a->next;
  1999.         fprintf( f, "size: %6d KB, allocs: %5d: %s, line: %d, call stack: %s\r\n",
  2000.                     (a->size >> 10), a->numAllocs, Mem_CleanupFileName(a->fileName),
  2001.                             a->lineNumber, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
  2002. // RAVEN BEGIN
  2003. // jnewquist: send all allocations through one place on the Xenon
  2004.         ::local_free( a );
  2005. // RAVEN END
  2006.     }
  2007.  
  2008.     idLib::sys->ShutdownSymbols();
  2009.  
  2010.     fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
  2011.     fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
  2012.  
  2013.     fclose( f );
  2014. #endif
  2015. }
  2016.  
  2017. /*
  2018. ==================
  2019. Mem_DumpCompressed_f
  2020. ==================
  2021. */
  2022. void Mem_DumpCompressed_f( const idCmdArgs &args ) {
  2023.     int argNum;
  2024.     const char *arg, *fileName;
  2025.     memorySortType_t memSort = MEMSORT_LOCATION;
  2026.     int sortCallStack = 0, numFrames = 0;
  2027.  
  2028.     // get cmd-line options
  2029.     argNum = 1;
  2030.     arg = args.Argv( argNum );
  2031.     while( arg[0] == '-' ) {
  2032.         arg = args.Argv( ++argNum );
  2033.         if ( idStr::Icmp( arg, "s" ) == 0 ) {
  2034.             memSort = MEMSORT_SIZE;
  2035.         } else if ( idStr::Icmp( arg, "l" ) == 0 ) {
  2036.             memSort = MEMSORT_LOCATION;
  2037.         } else if ( idStr::Icmp( arg, "a" ) == 0 ) {
  2038.             memSort = MEMSORT_NUMALLOCS;
  2039.         } else if ( idStr::Icmp( arg, "cs1" ) == 0 ) {
  2040.             memSort = MEMSORT_CALLSTACK;
  2041.             sortCallStack = 2;
  2042.         } else if ( idStr::Icmp( arg, "cs2" ) == 0 ) {
  2043.             memSort = MEMSORT_CALLSTACK;
  2044.             sortCallStack = 1;
  2045.         } else if ( idStr::Icmp( arg, "cs3" ) == 0 ) {
  2046.             memSort = MEMSORT_CALLSTACK;
  2047.             sortCallStack = 0;
  2048.         } else if ( arg[0] == 'f' ) {
  2049.             numFrames = atoi( arg + 1 );
  2050.         } else {
  2051.             idLib::common->Printf( "memoryDumpCompressed [options] [filename]\n"
  2052.                         "options:\n"
  2053.                         "  -s     sort on size\n"
  2054.                         "  -l     sort on location\n"
  2055.                         "  -a     sort on the number of allocations\n"
  2056.                         "  -cs1   sort on first function on call stack\n"
  2057.                         "  -cs2   sort on second function on call stack\n"
  2058.                         "  -cs3   sort on third function on call stack\n"
  2059.                         "  -f<X>  only report allocations the last X frames\n"
  2060.                         "By default the memory allocations are sorted on location.\n"
  2061.                         "By default a 'memorydump.txt' is written if no file name is specified.\n" );
  2062.             return;
  2063.         }
  2064.         arg = args.Argv( ++argNum );
  2065.     }
  2066.     if ( argNum >= args.Argc() ) {
  2067.         fileName = "memorydump.txt";
  2068.     } else {
  2069.         fileName = arg;
  2070.     }
  2071.     Mem_DumpCompressed( fileName, memSort, sortCallStack, numFrames );
  2072. }
  2073.  
  2074. /*
  2075. ==================
  2076. Mem_AllocDebugMemory
  2077. ==================
  2078. */
  2079. // RAVEN BEGIN
  2080. void *Mem_AllocDebugMemory( const int size, const char *fileName, const int lineNumber, const bool align16, byte tag ) {
  2081. // RAVEN END
  2082.     void *p;
  2083.     debugMemory_t *m;
  2084.  
  2085.     if ( !size ) {
  2086.         return NULL;
  2087.     }
  2088.  
  2089.     if ( !mem_heap ) {
  2090. #ifdef CRASH_ON_STATIC_ALLOCATION
  2091.         *((int*)0x0) = 1;
  2092. #endif
  2093.         // NOTE: set a breakpoint here to find memory allocations before mem_heap is initialized
  2094. // RAVEN BEGIN
  2095. // jnewquist: send all allocations through one place on the Xenon
  2096.         return local_malloc( size );
  2097. // RAVEN END
  2098.     }
  2099.  
  2100.     if ( align16 ) {
  2101. // RAVEN BEGIN
  2102.         p = mem_heap->Allocate16( size + sizeof( debugMemory_t ), tag );
  2103.     }
  2104.     else {
  2105.         p = mem_heap->Allocate( size + sizeof( debugMemory_t ), tag );
  2106. // RAVEN END
  2107.     }
  2108.  
  2109.     Mem_UpdateAllocStats( size );
  2110.  
  2111.     m = (debugMemory_t *) p;
  2112. // RAVEN BEGIN
  2113. // jnewquist: Add a signature to avoid misinterpreting an early allocation
  2114.     m->signature = 0xf00d;
  2115. // RAVEN END
  2116.     m->fileName = fileName;
  2117.     m->lineNumber = lineNumber;
  2118.     m->frameNumber = idLib::frameNumber;
  2119.     m->size = size;
  2120.     m->next = mem_debugMemory;
  2121.     m->prev = NULL;
  2122.     if ( mem_debugMemory ) {
  2123.         mem_debugMemory->prev = m;
  2124.     }
  2125.     mem_debugMemory = m;
  2126.     idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
  2127.  
  2128.     return ( ( (byte *) p ) + sizeof( debugMemory_t ) );
  2129. }
  2130.  
  2131. /*
  2132. ==================
  2133. Mem_FreeDebugMemory
  2134. ==================
  2135. */
  2136. void Mem_FreeDebugMemory( void *p, const char *fileName, const int lineNumber, const bool align16 ) {
  2137.     debugMemory_t *m;
  2138.  
  2139.     if ( !p ) {
  2140.         return;
  2141.     }
  2142.  
  2143. // RAVEN BEGIN
  2144. // jnewquist: Add a signature to avoid misinterpreting an early allocation
  2145.     m = (debugMemory_t *) ( ( (byte *) p ) - sizeof( debugMemory_t ) );
  2146.  
  2147.     if ( !mem_heap || m->signature != 0xf00d ) {
  2148. #ifdef CRASH_ON_STATIC_ALLOCATION
  2149.         *((int*)0x0) = 1;
  2150. #endif
  2151.         // NOTE: set a breakpoint here to find memory being freed before mem_heap is initialized
  2152. // jnewquist: send all allocations through one place on the Xenon
  2153.         local_free( p );
  2154.         return;
  2155.     }
  2156.  
  2157. // RAVEN END
  2158.     if ( m->size < 0 ) {
  2159.         idLib::common->FatalError( "memory freed twice, first from %s, now from %s", idLib::sys->GetCallStackStr( m->callStack, MAX_CALLSTACK_DEPTH ), idLib::sys->GetCallStackCurStr( MAX_CALLSTACK_DEPTH ) );
  2160.     }
  2161.  
  2162.     Mem_UpdateFreeStats( m->size );
  2163.  
  2164.     if ( m->next ) {
  2165.         m->next->prev = m->prev;
  2166.     }
  2167.     if ( m->prev ) {
  2168.         m->prev->next = m->next;
  2169.     }
  2170.     else {
  2171.         mem_debugMemory = m->next;
  2172.     }
  2173.  
  2174.     m->fileName = fileName;
  2175.     m->lineNumber = lineNumber;
  2176.     m->frameNumber = idLib::frameNumber;
  2177.     m->size = -m->size;
  2178.     idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
  2179.  
  2180.     if ( align16 ) {
  2181.          mem_heap->Free16( m );
  2182.     }
  2183.     else {
  2184.          mem_heap->Free( m );
  2185.     }
  2186. }
  2187.  
  2188. /*
  2189. ==================
  2190. Mem_Alloc
  2191. ==================
  2192. */
  2193. void *Mem_Alloc( const int size, const char *fileName, const int lineNumber, byte tag ) {
  2194.     if ( !size ) {
  2195.         return NULL;
  2196.     }
  2197.     return Mem_AllocDebugMemory( size, fileName, lineNumber, false, tag );
  2198. }
  2199.  
  2200. /*
  2201. ==================
  2202. Mem_Free
  2203. ==================
  2204. */
  2205. void Mem_Free( void *ptr, const char *fileName, const int lineNumber ) {
  2206.     if ( !ptr ) {
  2207.         return;
  2208.     }
  2209.     Mem_FreeDebugMemory( ptr, fileName, lineNumber, false );
  2210. }
  2211.  
  2212. /*
  2213. ==================
  2214. Mem_Alloc16
  2215. ==================
  2216. */
  2217. void *Mem_Alloc16( const int size, const char *fileName, const int lineNumber, byte tag ) {
  2218.     if ( !size ) {
  2219.         return NULL;
  2220.     }
  2221.     void *mem = Mem_AllocDebugMemory( size, fileName, lineNumber, true, tag );
  2222.     // make sure the memory is 16 byte aligned
  2223.     assert( ( ((int)mem) & 15) == 0 );
  2224.     return mem;
  2225. }
  2226.  
  2227. /*
  2228. ==================
  2229. Mem_Free16
  2230. ==================
  2231. */
  2232. void Mem_Free16( void *ptr, const char *fileName, const int lineNumber ) {
  2233.     if ( !ptr ) {
  2234.         return;
  2235.     }
  2236.     // make sure the memory is 16 byte aligned
  2237.     assert( ( ((int)ptr) & 15) == 0 );
  2238.     Mem_FreeDebugMemory( ptr, fileName, lineNumber, true );
  2239. }
  2240.  
  2241. /*
  2242. ==================
  2243. Mem_ClearedAlloc
  2244. ==================
  2245. */
  2246. void *Mem_ClearedAlloc( const int size, const char *fileName, const int lineNumber, byte tag ) {
  2247.     void *mem = Mem_Alloc( size, fileName, lineNumber, tag );
  2248.     SIMDProcessor->Memset( mem, 0, size );
  2249.     return mem;
  2250. }
  2251.  
  2252. /*
  2253. ==================
  2254. Mem_CopyString
  2255. ==================
  2256. */
  2257. char *Mem_CopyString( const char *in, const char *fileName, const int lineNumber ) {
  2258.     char    *out;
  2259.     
  2260.     out = (char *)Mem_Alloc( strlen(in) + 1, fileName, lineNumber );
  2261.     strcpy( out, in );
  2262.     return out;
  2263. }
  2264.  
  2265. /*
  2266. ==================
  2267. Mem_Init
  2268. ==================
  2269. */
  2270. void Mem_Init( void ) {
  2271.     mem_heap = new idHeap;
  2272. }
  2273.  
  2274. /*
  2275. ==================
  2276. Mem_Shutdown
  2277. ==================
  2278. */
  2279. void Mem_Shutdown( void ) {
  2280.  
  2281.     if ( mem_leakName[0] != '\0' ) {
  2282.         Mem_DumpCompressed( va( "%s_leak_size.txt", mem_leakName ), MEMSORT_SIZE, 0, 0 );
  2283.         Mem_DumpCompressed( va( "%s_leak_location.txt", mem_leakName ), MEMSORT_LOCATION, 0, 0 );
  2284.         Mem_DumpCompressed( va( "%s_leak_cs1.txt", mem_leakName ), MEMSORT_CALLSTACK, 2, 0 );
  2285.     }
  2286.  
  2287.     idHeap *m = mem_heap;
  2288.     mem_heap = NULL;
  2289.     delete m;
  2290. }
  2291.  
  2292. /*
  2293. ==================
  2294. Mem_EnableLeakTest
  2295. ==================
  2296. */
  2297. void Mem_EnableLeakTest( const char *name ) {
  2298.     idStr::Copynz( mem_leakName, name, sizeof( mem_leakName ) );
  2299. }
  2300.  
  2301. // RAVEN BEGIN
  2302. // jnewquist: Add Mem_Size to query memory allocation size
  2303. /*
  2304. ==================
  2305. Mem_Size
  2306. ==================
  2307. */
  2308. int    Mem_Size( void *ptr ) {
  2309.     return mem_heap->Msize(ptr);
  2310. }
  2311. // RAVEN END
  2312.  
  2313. #endif /* !ID_DEBUG_MEMORY */
  2314.  
  2315. #endif    // #ifndef _RV_MEM_SYS_SUPPORT
  2316.