Torque3D Documentation / _generateds / platformMemory.cpp

platformMemory.cpp

Engine/source/platform/platformMemory.cpp

More...

Classes:

class

Red/Black Tree Node - used to store queues of free blocks.

Namespaces:

namespace

Public Enumerations

enum
MemConstants {
  Allocated = BIT(0)
  Array = BIT(1)
  DebugFlag = BIT(2)
  Reallocated = BIT(3)
  GlobalFlag = BIT(4)
  StaticFlag = BIT(5)
  AllocatedGuard = 0xCEDEFEDE
  FreeGuard = 0x5555FFFF
  MaxAllocationAmount = 0xFFFFFFFF
  TreeNodeAllocCount = 2048
}
enum
RedBlackTokens {
  Red = 0
  Black = 1
}

Public Functions

dFree(void * in_pFree)
void *
dMalloc_r(dsize_t in_size, const char * fileName, const dsize_t line)
void *
dRealloc_r(void * in_pResize, dsize_t in_size, const char * fileName, const dsize_t line)
operator new(dsize_t size, const char * fileName, const U32 line)
operator new[](dsize_t size, const char * fileName, const U32 line)

Detailed Description

Public Enumerations

MemConstants

Enumerator

Allocated = BIT(0)
Array = BIT(1)
DebugFlag = BIT(2)
Reallocated = BIT(3)
GlobalFlag = BIT(4)

This flag is set if the memory has been allocated, then 'realloc' is called.

StaticFlag = BIT(5)
AllocatedGuard = 0xCEDEFEDE
FreeGuard = 0x5555FFFF
MaxAllocationAmount = 0xFFFFFFFF
TreeNodeAllocCount = 2048
RedBlackTokens

Enumerator

Red = 0
Black = 1

Public Variables

U32 gImageAlloc 
U32 gNewNewTotal 
U32 MinPageSize 

Public Functions

AFTER_MODULE_INIT(Sim )

dFree(void * in_pFree)

dMalloc_r(dsize_t in_size, const char * fileName, const dsize_t line)

dRealloc_r(void * in_pResize, dsize_t in_size, const char * fileName, const dsize_t line)

flagToBit(Memory::EFlag flag)

operator delete(void * mem)

operator delete[](void * mem)

operator new(dsize_t size)

operator new(dsize_t size, const char * fileName, const U32 line)

operator new[](dsize_t size)

operator new[](dsize_t size, const char * fileName, const U32 line)

setMinimumAllocUnit(U32 allocUnit)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platformMemory.h"
  25#include "console/dynamicTypes.h"
  26#include "console/engineAPI.h"
  27#include "core/stream/fileStream.h"
  28#include "core/strings/stringFunctions.h"
  29#include "console/console.h"
  30#include "platform/profiler.h"
  31#include "platform/threads/mutex.h"
  32#include "core/module.h"
  33
  34// If profile paths are enabled, disable profiling of the
  35// memory manager as that would cause a cyclic dependency
  36// through the string table's allocation stuff used by the
  37// profiler (talk about a long sentence...)
  38
  39#ifdef TORQUE_ENABLE_PROFILE_PATH
  40#  undef PROFILE_START
  41#  undef PROFILE_END
  42#  undef PROFILE_SCOPE
  43#  define PROFILE_START( x )
  44#  define PROFILE_END()
  45#  define PROFILE_SCOPE( x )
  46#endif
  47
  48#ifdef TORQUE_MULTITHREAD
  49void * gMemMutex = NULL;
  50#endif
  51   
  52//-------------------------------------- Make sure we don't have the define set
  53#ifdef new
  54#undef new
  55#endif
  56
  57enum MemConstants : U32
  58{
  59   Allocated            = BIT(0),
  60   Array                = BIT(1),
  61   DebugFlag            = BIT(2),
  62   Reallocated          = BIT(3),      /// This flag is set if the memory has been allocated, then 'realloc' is called
  63   GlobalFlag           = BIT(4),
  64   StaticFlag           = BIT(5),
  65   AllocatedGuard       = 0xCEDEFEDE,
  66   FreeGuard            = 0x5555FFFF,
  67   MaxAllocationAmount  = 0xFFFFFFFF,
  68   TreeNodeAllocCount   = 2048,
  69};
  70
  71inline U32 flagToBit( Memory::EFlag flag )
  72{
  73   using namespace Memory;
  74
  75   U32 bit = 0;
  76   switch( flag )
  77   {
  78   case FLAG_Debug:  bit = DebugFlag; break;
  79   case FLAG_Global: bit = GlobalFlag; break;
  80   case FLAG_Static: bit = StaticFlag; break;
  81   }
  82   return bit;
  83}
  84
  85enum RedBlackTokens {
  86   Red = 0,
  87   Black = 1
  88};
  89
  90static U32 MinPageSize = 8 * 1024 * 1024;
  91
  92#if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD)
  93#define LOG_PAGE_ALLOCS
  94#endif
  95
  96U32 gNewNewTotal = 0;
  97U32 gImageAlloc = 0;
  98
  99//---------------------------------------------------------------------------
 100
 101namespace Memory
 102{
 103
 104ConsoleFunctionGroupBegin( Memory, "Memory manager utility functions.");
 105
 106struct FreeHeader;
 107
 108/// Red/Black Tree Node - used to store queues of free blocks
 109struct TreeNode
 110{
 111   U32 size;
 112   TreeNode *parent;
 113   TreeNode *left;
 114   TreeNode *right;
 115   U32 color;
 116   FreeHeader *queueHead;
 117   FreeHeader *queueTail;
 118   U32 unused;
 119};
 120
 121struct Header
 122{
 123   // doubly linked list of allocated and free blocks -
 124   // contiguous in memory.
 125#ifdef TORQUE_DEBUG_GUARD
 126   U32 preguard[4];
 127#endif
 128   Header *next;
 129   Header *prev;
 130   dsize_t size;
 131   U32 flags;
 132#ifdef TORQUE_DEBUG_GUARD
 133   #ifdef TORQUE_ENABLE_PROFILE_PATH
 134   U32 unused[5];
 135   #else
 136   U32 unused[4];
 137   #endif
 138   U32 postguard[4];
 139#endif
 140};
 141
 142struct AllocatedHeader
 143{
 144#ifdef TORQUE_DEBUG_GUARD
 145   U32 preguard[4];
 146#endif
 147   Header *next;
 148   Header *prev;
 149   dsize_t size;
 150   U32 flags;
 151
 152#ifdef TORQUE_DEBUG_GUARD
 153   // an allocated header will only have this stuff if TORQUE_DEBUG_GUARD
 154   U32 line;
 155   U32 allocNum;
 156   const char *fileName;
 157   #ifdef TORQUE_ENABLE_PROFILE_PATH
 158      const char * profilePath;
 159   #endif
 160   U32 realSize;
 161   U32 postguard[4];
 162#endif
 163
 164   void* getUserPtr()
 165   {
 166      return ( this + 1 );
 167   }
 168};
 169
 170struct FreeHeader
 171{
 172#ifdef TORQUE_DEBUG_GUARD
 173   U32 preguard[4];
 174#endif
 175   Header *next;
 176   Header *prev;
 177   dsize_t size;
 178   U32 flags;
 179
 180// since a free header has at least one cache line (16 bytes)
 181// we can tag some more stuff on:
 182
 183   FreeHeader *nextQueue;  // of the same size
 184   FreeHeader *prevQueue;  // doubly linked
 185   TreeNode *treeNode; // which tree node we're coming off of.
 186   U32 guard;
 187#ifdef TORQUE_DEBUG_GUARD
 188   #ifdef TORQUE_ENABLE_PROFILE_PATH
 189      U32 unused;
 190   #endif
 191   U32 postguard[4];
 192#endif
 193};
 194
 195struct PageRecord
 196{
 197   dsize_t allocSize;
 198   PageRecord *prevPage;
 199   Header *headerList;  // if headerList is NULL, this is a treeNode page
 200   void *basePtr;
 201   U32 unused[4];  // even out the record to 32 bytes...
 202                   // so if we're on a 32-byte cache-line comp, the tree nodes
 203                   // will cache better
 204};
 205
 206PageRecord *gPageList = NULL;
 207TreeNode nil;
 208TreeNode *NIL = &nil;
 209TreeNode *gFreeTreeRoot = &nil;
 210TreeNode *gTreeFreeList = NULL;
 211
 212U32  gInsertCount = 0;
 213U32  gRemoveCount = 0;
 214U32  gBreakAlloc = 0xFFFFFFFF;
 215U32  gCurrAlloc = 0;
 216char gLogFilename[256] = "memlog.txt";
 217bool gEnableLogging = false;
 218bool gNeverLogLeaks = 0;
 219bool gAlwaysLogLeaks = 0;
 220U32 gBytesAllocated = 0;
 221U32 gBlocksAllocated = 0;
 222U32 gPageBytesAllocated = 0;
 223
 224struct HeapIterator
 225{
 226   PageRecord*    mCurrentPage;
 227   Header*        mCurrentHeader;
 228   bool           mAllocatedOnly;
 229
 230   HeapIterator( bool allocatedOnly = true )
 231      : mCurrentPage( gPageList ),
 232        mCurrentHeader( NULL ),
 233        mAllocatedOnly( allocatedOnly )
 234   {
 235      if( mCurrentPage )
 236      {
 237         mCurrentHeader = mCurrentPage->headerList;
 238         while( !mCurrentHeader && mCurrentPage )
 239         {
 240            mCurrentPage = mCurrentPage->prevPage;
 241            mCurrentHeader = mCurrentPage->headerList;
 242         }
 243
 244         if( mCurrentHeader && mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) )
 245            ++ ( *this ); // Advance to first allocated record.
 246      }
 247   }
 248
 249   bool isValid() const
 250   {
 251      return ( mCurrentHeader != NULL );
 252   }
 253   HeapIterator& operator++()
 254   {
 255      do
 256      {
 257         if( !mCurrentHeader )
 258         {
 259            if( mCurrentPage )
 260               mCurrentPage = mCurrentPage->prevPage;
 261
 262            if( !mCurrentPage )
 263               break;
 264            mCurrentHeader = mCurrentPage->headerList;
 265         }
 266         else
 267            mCurrentHeader = mCurrentHeader->next;
 268      }
 269      while( !mCurrentHeader || ( mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) );
 270
 271      return *this;
 272   }
 273   operator Header*() const
 274   {
 275      return mCurrentHeader;
 276   }
 277   Header* operator*() const
 278   {
 279      return mCurrentHeader;
 280   }
 281   Header* operator->() const
 282   {
 283      return mCurrentHeader;
 284   }
 285};
 286
 287#ifdef TORQUE_DEBUG_GUARD
 288
 289static bool checkGuard(Header *header, bool alloc)
 290{
 291   U32 guardVal = alloc ? AllocatedGuard : FreeGuard;
 292   for(U32 i = 0; i < 4; i++)
 293      if(header->preguard[i] != guardVal || header->postguard[i] != guardVal)
 294      {
 295         Platform::debugBreak();
 296         return false;
 297      }
 298
 299   return true;
 300}
 301
 302#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 303static void setGuard(Header *header, bool alloc)
 304{
 305   U32 guardVal = alloc ? AllocatedGuard : FreeGuard;
 306   for(U32 i = 0; i < 4; i++)
 307      header->preguard[i] = header->postguard[i] = guardVal;
 308}
 309#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 310
 311#endif // TORQUE_DEBUG_GUARD
 312
 313static void memoryError()
 314{
 315   // free all the pages
 316   PageRecord *walk = gPageList;
 317   while(walk) {
 318      PageRecord *prev = walk->prevPage;
 319      dRealFree(walk);
 320      walk = prev;
 321   }
 322   AssertFatal(false, "Error allocating memory! Shutting down.");
 323   Platform::AlertOK("Torque Memory Error", "Error allocating memory. Shutting down.\n");
 324   Platform::forceShutdown(-1);
 325}
 326
 327PageRecord *allocPage(dsize_t pageSize)
 328{
 329   pageSize += sizeof(PageRecord);
 330   void* base = dRealMalloc(pageSize);
 331   if (base == NULL)
 332      memoryError();
 333
 334   PageRecord *rec = (PageRecord *) base;
 335   rec->basePtr = (void *) (rec + 1);
 336   rec->allocSize = pageSize;
 337   rec->prevPage = gPageList;
 338   gPageList = rec;
 339   rec->headerList = NULL;
 340   return rec;
 341}
 342
 343TreeNode *allocTreeNode()
 344{
 345   if(!gTreeFreeList)
 346   {
 347      PageRecord *newPage = allocPage(TreeNodeAllocCount * sizeof(TreeNode));
 348      TreeNode *walk = (TreeNode *) newPage->basePtr;
 349      U32 i;
 350      gTreeFreeList = walk;
 351      for(i = 0; i < TreeNodeAllocCount - 1; i++, walk++)
 352         walk->parent = walk + 1;
 353      walk->parent = NULL;
 354   }
 355   TreeNode *ret = gTreeFreeList;
 356   gTreeFreeList = ret->parent;
 357   return ret;
 358}
 359
 360void freeTreeNode(TreeNode *tn)
 361{
 362   tn->parent = gTreeFreeList;
 363   gTreeFreeList = tn;
 364}
 365
 366
 367static U32 validateTreeRecurse(TreeNode *tree)
 368{
 369   if(tree == NIL)
 370      return 1;
 371   // check my left tree
 372   S32 lcount, rcount, nc = 0;
 373
 374   if(tree->color == Red)
 375   {
 376      if(tree->left->color == Red || tree->right->color == Red)
 377         Platform::debugBreak();
 378   }
 379   else
 380      nc = 1;
 381
 382   FreeHeader *walk = tree->queueHead;
 383   if(!walk)
 384      Platform::debugBreak();
 385
 386   FreeHeader *prev = NULL;
 387   while(walk)
 388   {
 389      if(walk->prevQueue != prev)
 390         Platform::debugBreak();
 391      if(walk->treeNode != tree)
 392         Platform::debugBreak();
 393      if(walk->size != tree->size)
 394         Platform::debugBreak();
 395      if(!walk->nextQueue && walk != tree->queueTail)
 396         Platform::debugBreak();
 397      prev = walk;
 398      walk = walk->nextQueue;
 399   }
 400
 401   lcount = validateTreeRecurse(tree->left);
 402   rcount = validateTreeRecurse(tree->right);
 403   if(lcount != rcount)
 404      Platform::debugBreak();
 405   return lcount + nc;
 406}
 407
 408static void validateParentageRecurse(TreeNode *tree)
 409{
 410   if(tree->left != NIL)
 411   {
 412      if(tree->left->parent != tree)
 413         Platform::debugBreak();
 414
 415      if(tree->left->size > tree->size)
 416         Platform::debugBreak();
 417      validateParentageRecurse(tree->left);
 418   }
 419   if(tree->right != NIL)
 420   {
 421      if(tree->right->parent != tree)
 422         Platform::debugBreak();
 423
 424      if(tree->right->size < tree->size)
 425         Platform::debugBreak();
 426      validateParentageRecurse(tree->right);
 427   }
 428}
 429
 430static void validateTree()
 431{
 432   if(gFreeTreeRoot == NIL)
 433      return;
 434   validateParentageRecurse(gFreeTreeRoot);
 435   validateTreeRecurse(gFreeTreeRoot);
 436}
 437
 438void validate()
 439{
 440#ifdef TORQUE_MULTITHREAD
 441   if(!gMemMutex)
 442      gMemMutex = Mutex::createMutex();
 443
 444   Mutex::lockMutex(gMemMutex);
 445#endif
 446
 447
 448   // first validate the free tree:
 449   validateTree();
 450   // now validate all blocks:
 451   for(PageRecord *list = gPageList; list; list = list->prevPage)
 452   {
 453      Header *prev = NULL;
 454      for(Header *walk = list->headerList; walk; walk = walk->next)
 455      {
 456#ifdef TORQUE_DEBUG_GUARD
 457         checkGuard(walk, walk->flags & Allocated);
 458#endif
 459         if(walk->prev != prev)
 460            Platform::debugBreak();
 461         prev = walk;
 462         if(walk->next && ((const char *)(walk->next) != (const char *)(walk) + sizeof(Header) + walk->size))
 463            Platform::debugBreak();
 464      }
 465   }
 466
 467#ifdef TORQUE_MULTITHREAD
 468   Mutex::unlockMutex(gMemMutex);
 469#endif
 470}
 471
 472#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 473static void rotateLeft(TreeNode *hdr)
 474{
 475   TreeNode *temp = hdr->right;
 476   hdr->right = temp->left;
 477   if(temp->left != NIL)
 478      temp->left->parent = hdr;
 479   temp->parent = hdr->parent;
 480   if(temp->parent == NIL)
 481      gFreeTreeRoot = temp;
 482   else if(hdr == hdr->parent->left)
 483      hdr->parent->left = temp;
 484   else
 485      hdr->parent->right = temp;
 486   temp->left = hdr;
 487   hdr->parent = temp;
 488}
 489#endif
 490
 491#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 492static void rotateRight(TreeNode *hdr)
 493{
 494   TreeNode *temp = hdr->left;
 495   hdr->left = temp->right;
 496   if(temp->right != NIL)
 497      temp->right->parent = hdr;
 498   temp->parent = hdr->parent;
 499   if(temp->parent == NIL)
 500      gFreeTreeRoot = temp;
 501   else if(hdr == hdr->parent->left)
 502      hdr->parent->left = temp;
 503   else
 504      hdr->parent->right = temp;
 505   temp->right = hdr;
 506   hdr->parent = temp;
 507}
 508#endif
 509
 510#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 511static void treeInsert(FreeHeader *fhdr)
 512{
 513#ifdef TORQUE_DEBUG_GUARD
 514   checkGuard((Header *) fhdr, true); // check to see that it's got allocated guards
 515   setGuard((Header *) fhdr, false);
 516#endif
 517   //gInsertCount++;
 518
 519   TreeNode *newParent = NIL;
 520   TreeNode *walk = gFreeTreeRoot;
 521   while(walk != NIL)
 522   {
 523      newParent = walk;
 524      if(fhdr->size < walk->size)
 525         walk = walk->left;
 526      else if(fhdr->size > walk->size)
 527         walk = walk->right;
 528      else // tag it on the end of the queue...
 529      {
 530         // insert it on this header...
 531         walk->queueTail->nextQueue = fhdr;
 532         fhdr->prevQueue = walk->queueTail;
 533         walk->queueTail = fhdr;
 534         fhdr->nextQueue = NULL;
 535         fhdr->treeNode = walk;
 536         return;
 537      }
 538   }
 539   TreeNode *hdr = allocTreeNode();
 540   hdr->size = fhdr->size;
 541   hdr->queueHead = hdr->queueTail = fhdr;
 542   fhdr->nextQueue = fhdr->prevQueue = NULL;
 543   fhdr->treeNode = hdr;
 544
 545   hdr->left = NIL;
 546   hdr->right = NIL;
 547
 548   hdr->parent = newParent;
 549
 550   if(newParent == NIL)
 551      gFreeTreeRoot = hdr;
 552   else if(hdr->size < newParent->size)
 553      newParent->left = hdr;
 554   else
 555      newParent->right = hdr;
 556
 557   // do red/black rotations
 558   hdr->color = Red;
 559   while(hdr != gFreeTreeRoot && (hdr->parent->color == Red))
 560   {
 561      TreeNode *parent = hdr->parent;
 562      TreeNode *pparent = hdr->parent->parent;
 563
 564      if(parent == pparent->left)
 565      {
 566         TreeNode *temp = pparent->right;
 567         if(temp->color == Red)
 568         {
 569            parent->color = Black;
 570            temp->color = Black;
 571            pparent->color = Red;
 572            hdr = pparent;
 573         }
 574         else
 575         {
 576            if(hdr == parent->right)
 577            {
 578               hdr = parent;
 579               rotateLeft(hdr);
 580               parent = hdr->parent;
 581               pparent = hdr->parent->parent;
 582            }
 583            parent->color = Black;
 584            pparent->color = Red;
 585            rotateRight(pparent);
 586         }
 587      }
 588      else
 589      {
 590         TreeNode *temp = pparent->left;
 591         if(temp->color == Red)
 592         {
 593            parent->color = Black;
 594            temp->color = Black;
 595            pparent->color = Red;
 596            hdr = pparent;
 597         }
 598         else
 599         {
 600            if(hdr == parent->left)
 601            {
 602               hdr = parent;
 603               rotateRight(hdr);
 604               parent = hdr->parent;
 605               pparent = hdr->parent->parent;
 606            }
 607            parent->color = Black;
 608            pparent->color = Red;
 609            rotateLeft(pparent);
 610         }
 611      }
 612   }
 613   gFreeTreeRoot->color = Black;
 614   //validateTree();
 615}
 616#endif
 617
 618#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 619static void treeRemove(FreeHeader *hdr)
 620{
 621#ifdef TORQUE_DEBUG_GUARD
 622   checkGuard((Header *) hdr, false);
 623   setGuard((Header *) hdr, true);
 624#endif
 625   //validateTree();
 626   //gRemoveCount++;
 627
 628   FreeHeader *prev = hdr->prevQueue;
 629   FreeHeader *next = hdr->nextQueue;
 630
 631   if(prev)
 632      prev->nextQueue = next;
 633   else
 634      hdr->treeNode->queueHead = next;
 635
 636   if(next)
 637      next->prevQueue = prev;
 638   else
 639      hdr->treeNode->queueTail = prev;
 640
 641   if(prev || next)
 642      return;
 643
 644   TreeNode *z = hdr->treeNode;
 645
 646   nil.color = Black;
 647
 648   TreeNode *y, *x;
 649   if(z->left == NIL || z->right == NIL)
 650      y = z;
 651   else
 652   {
 653      y = z->right;
 654      while(y->left != NIL)
 655         y = y->left;
 656   }
 657   if(y->left != NIL)
 658      x = y->left;
 659   else
 660      x = y->right;
 661
 662   x->parent = y->parent;
 663   if(y->parent == NIL)
 664      gFreeTreeRoot = x;
 665   else if(y == y->parent->left)
 666      y->parent->left = x;
 667   else
 668      y->parent->right = x;
 669
 670   U32 yColor = y->color;
 671   if(y != z)
 672   {
 673      // copy y's important fields into z (since we're going to free y)
 674      if(z->parent->left == z)
 675         z->parent->left = y;
 676      else if(z->parent->right == z)
 677         z->parent->right = y;
 678      y->left = z->left;
 679      y->right = z->right;
 680      if(y->left != NIL)
 681         y->left->parent = y;
 682      if(y->right != NIL)
 683         y->right->parent = y;
 684      y->parent = z->parent;
 685      if(z->parent == NIL)
 686         gFreeTreeRoot = y;
 687      y->color = z->color;
 688      if(x->parent == z)
 689         x->parent = y;
 690      //validateTree();
 691   }
 692   freeTreeNode(z);
 693
 694   if(yColor == Black)
 695   {
 696      while(x != gFreeTreeRoot && x->color == Black)
 697      {
 698         TreeNode *w;
 699         if(x == x->parent->left)
 700         {
 701            w = x->parent->right;
 702            if(w->color == Red)
 703            {
 704               w->color = Black;
 705               x->parent->color = Red;
 706               rotateLeft(x->parent);
 707               w = x->parent->right;
 708            }
 709            if(w->left->color == Black && w->right->color == Black)
 710            {
 711               w->color = Red;
 712               x = x->parent;
 713            }
 714            else
 715            {
 716               if(w->right->color == Black)
 717               {
 718                  w->left->color = Black;
 719                  rotateRight(w);
 720                  w = x->parent->right;
 721               }
 722               w->color = x->parent->color;
 723               x->parent->color = Black;
 724               w->right->color = Black;
 725               rotateLeft(x->parent);
 726               x = gFreeTreeRoot;
 727            }
 728         }
 729         else
 730         {
 731            w = x->parent->left;
 732            if(w->color == Red)
 733            {
 734               w->color = Black;
 735               x->parent->color = Red;
 736               rotateRight(x->parent);
 737               w = x->parent->left;
 738            }
 739            if(w->left->color == Black && w->right->color == Black)
 740            {
 741               w->color = Red;
 742               x = x->parent;
 743            }
 744            else
 745            {
 746               if(w->left->color == Black)
 747               {
 748                  w->right->color = Black;
 749                  rotateLeft(w);
 750                  w = x->parent->left;
 751               }
 752               w->color = x->parent->color;
 753               x->parent->color = Black;
 754               w->left->color = Black;
 755               rotateRight(x->parent);
 756               x = gFreeTreeRoot;
 757            }
 758         }
 759      }
 760      x->color = Black;
 761   }
 762   //validateTree();
 763}
 764#endif
 765
 766#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
 767static FreeHeader *treeFindSmallestGreaterThan(dsize_t size)
 768{
 769   TreeNode *bestMatch = NIL;
 770   TreeNode *walk = gFreeTreeRoot;
 771   while(walk != NIL)
 772   {
 773      if(size == walk->size)
 774         return walk->queueHead;
 775      else if(size > walk->size)
 776         walk = walk->right;
 777      else // size < walk->size
 778      {
 779         bestMatch = walk;
 780         walk = walk->left;
 781      }
 782   }
 783   //validateTree();
 784   if(bestMatch != NIL)
 785      return bestMatch->queueHead;
 786
 787   return NULL;
 788}
 789#endif
 790
 791/// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards
 792/// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled).
 793///
 794/// @note This function does not allow interior pointers!
 795
 796void checkPtr( void* ptr )
 797{
 798   for( HeapIterator iter; iter.isValid(); ++ iter )
 799   {
 800      AllocatedHeader* header = ( AllocatedHeader* ) *iter;
 801      if( header->getUserPtr() == ptr )
 802      {
 803#ifdef TORQUE_DEBUG_GUARD
 804         char buffer[ 1024 ];
 805         if( !checkGuard( *iter, true ) )
 806         {
 807            dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer but has its guards corrupted", ptr );
 808            Platform::outputDebugString( buffer );
 809            return;
 810         }
 811         //dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr );
 812         //Platform::outputDebugString( buffer );
 813#endif
 814         return;
 815      }
 816   }
 817
 818   char buffer[ 1024 ];
 819   dSprintf( buffer, sizeof( buffer ), "0x%x is not a valid heap pointer", ptr );
 820   Platform::outputDebugString( buffer );
 821
 822   Platform::debugBreak();
 823}
 824
 825/// Dump info on all memory blocks that are still allocated.
 826/// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP.
 827
 828void ensureAllFreed()
 829{
 830#ifndef TORQUE_DISABLE_MEMORY_MANAGER
 831
 832   U32 numLeaks      = 0;
 833   U32 bytesLeaked   = 0;
 834
 835   for( HeapIterator iter; iter.isValid(); ++ iter )
 836   {
 837      AllocatedHeader* header = ( AllocatedHeader* ) *iter;
 838      if( !( header->flags & GlobalFlag ) )
 839      {
 840         // Note: can't spill profile paths here since they by
 841         //  now are all invalid (they're on the now freed string table)
 842
 843#ifdef TORQUE_DEBUG_GUARD
 844         Platform::outputDebugString( "MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i)",
 845            header->getUserPtr(),
 846            header->allocNum,
 847            ( header->flags & StaticFlag ? "(static)" : "" ),
 848            header->fileName, header->line, header->realSize, header->size );
 849         numLeaks ++;
 850         bytesLeaked += header->size;
 851#endif
 852      }
 853   }
 854
 855   if( numLeaks )
 856      Platform::outputDebugString( "NUM LEAKS: %i (%i bytes)", numLeaks, bytesLeaked );
 857#endif
 858}
 859
 860struct MemDumpLog
 861{
 862   U32 size;
 863   U32 count;
 864   U32 depthTotal;
 865   U32 maxDepth;
 866   U32 minDepth;
 867};
 868
 869void logDumpTraverse(MemDumpLog *sizes, TreeNode *header, U32 depth)
 870{
 871   if(header == NIL)
 872      return;
 873   MemDumpLog *mySize = sizes;
 874   while(mySize->size < header->size)
 875      mySize++;
 876
 877   U32 cnt = 0;
 878   for(FreeHeader *walk = header->queueHead; walk; walk = walk->nextQueue)
 879      cnt++;
 880   mySize->count += cnt;
 881   mySize->depthTotal += depth * cnt;
 882   mySize->maxDepth = depth > mySize->maxDepth ? depth : mySize->maxDepth;
 883   mySize->minDepth = depth < mySize->minDepth ? depth : mySize->minDepth;
 884   logDumpTraverse(sizes, header->left, depth + 1);
 885   logDumpTraverse(sizes, header->right, depth + 1);
 886}
 887
 888#ifdef TORQUE_DEBUG
 889DefineEngineFunction( validateMemory, void, ( ),,
 890   "@brief Used to validate memory space for the game.\n\n"
 891   "@ingroup Debugging" )
 892{
 893   validate();
 894}
 895#endif
 896
 897DefineEngineFunction( freeMemoryDump, void, ( ),,
 898   "@brief Dumps some useful statistics regarding free memory.\n\n"
 899   "Dumps an analysis of \'free chunks\' of memory. "
 900   "Does not print how much memory is free.\n\n"
 901   "@ingroup Debugging" )
 902{
 903   U32 startSize = 16;
 904   MemDumpLog memSizes[20];
 905   U32 i;
 906   for(i = 0; i < 20; i++)
 907   {
 908      memSizes[i].size = startSize << i;
 909      memSizes[i].count = 0;
 910      memSizes[i].depthTotal = 0;
 911      memSizes[i].maxDepth = 0;
 912      memSizes[i].minDepth = 1000;
 913   }
 914   memSizes[19].size = MaxAllocationAmount;
 915   logDumpTraverse(memSizes, gFreeTreeRoot, 1);
 916   MemDumpLog fullMem;
 917   fullMem.count = 0;
 918   fullMem.depthTotal = 0;
 919   fullMem.maxDepth = 0;
 920   fullMem.minDepth = 1000;
 921
 922   for(i = 0; i < 20; i++)
 923   {
 924      if(memSizes[i].count)
 925         Con::printf("Size: %d - Free blocks: %d  Max Depth: %d  Min Depth: %d  Average Depth: %g",
 926               memSizes[i].size, memSizes[i].count, memSizes[i].maxDepth, memSizes[i].minDepth,
 927               F32(memSizes[i].depthTotal) / F32(memSizes[i].count));
 928
 929      fullMem.count += memSizes[i].count;
 930      fullMem.depthTotal += memSizes[i].depthTotal;
 931      fullMem.maxDepth = memSizes[i].maxDepth > fullMem.maxDepth ? memSizes[i].maxDepth : fullMem.maxDepth;
 932      fullMem.minDepth = memSizes[i].minDepth < fullMem.minDepth ? memSizes[i].minDepth : fullMem.minDepth;
 933   }
 934   Con::printf("Total free blocks: %d  Max Depth: %d  Min Depth: %d  Average Depth: %g",
 935         fullMem.count, fullMem.maxDepth, fullMem.minDepth, F32(fullMem.depthTotal) / F32(fullMem.count));
 936}
 937
 938#ifdef TORQUE_DEBUG_GUARD
 939
 940void flagCurrentAllocs( EFlag flag )
 941{
 942#ifdef TORQUE_ENABLE_PROFILE_PATH
 943   if (gProfiler && !gProfiler->isEnabled())
 944   {
 945      gProfiler->enable(true);
 946      // warm it up
 947      //gProfiler->dumpToConsole();
 948   }
 949#endif
 950
 951   U32 bit = flagToBit( flag );
 952   for( HeapIterator iter; iter.isValid(); ++ iter )
 953      iter->flags |= bit;
 954}
 955
 956DefineEngineFunction(flagCurrentAllocs, void, (),,
 957   "@brief Flags all current memory allocations.\n\n"
 958   "Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). "
 959   "Helpful in detecting memory leaks and analyzing memory usage.\n\n"
 960   "@ingroup Debugging" )
 961{
 962   flagCurrentAllocs();
 963}
 964
 965void dumpUnflaggedAllocs(const char *file, EFlag flag)
 966{
 967   countUnflaggedAllocs(file, NULL, flag);
 968}
 969
 970S32 countUnflaggedAllocs(const char * filename, S32 *outUnflaggedRealloc, EFlag flag)
 971{
 972   S32 unflaggedAllocCount = 0;
 973   S32 unflaggedReAllocCount = 0;
 974
 975   FileStream fws;
 976   bool useFile = filename && *filename;
 977   if (useFile)
 978      useFile = fws.open(filename, Torque::FS::File::Write);
 979   char buffer[1024];
 980
 981   U32 bit = flagToBit( flag );
 982
 983   PageRecord* walk;
 984   for (walk = gPageList; walk; walk = walk->prevPage)
 985   {
 986      for(Header *probe = walk->headerList; probe; probe = probe->next)
 987      {
 988         if (probe->flags & Allocated)
 989         {
 990            AllocatedHeader* pah = (AllocatedHeader*)probe;
 991            if (!(pah->flags & bit))
 992            {
 993               // If you want to extract further information from an unflagged
 994               // memory allocation, do the following:
 995               // U8 *foo = (U8 *)pah;
 996               // foo += sizeof(Header);
 997               // FooObject *obj = (FooObject *)foo;
 998               dSprintf(buffer, 1023, "%s%s\t%d\t%d\t%d\r\n",
 999                  pah->flags & Reallocated ? "[R] " : "",
1000                  pah->fileName != NULL ? pah->fileName : "Undetermined",
1001                  pah->line, pah->realSize, pah->allocNum);
1002
1003               if( pah->flags & Reallocated )
1004                  unflaggedReAllocCount++;
1005               else
1006                  unflaggedAllocCount++;
1007
1008               if (useFile)
1009               {
1010                  fws.write(dStrlen(buffer), buffer);
1011                  fws.write(2, "\r\n");
1012               }
1013               else
1014               {
1015                  if( pah->flags & Reallocated )
1016                     Con::warnf(buffer);
1017                  else
1018                     Con::errorf(buffer);
1019               }
1020
1021#ifdef TORQUE_ENABLE_PROFILE_PATH
1022               static char line[4096];
1023               dSprintf(line, sizeof(line), "   %s\r\nreal size=%d",
1024                  pah->profilePath ? pah->profilePath : "unknown",
1025                  pah->realSize);
1026
1027               if (useFile)
1028               {
1029                  fws.write(dStrlen(line), line);
1030                  fws.write(2, "\r\n");
1031               }
1032               else
1033               {
1034                  if( pah->flags & Reallocated )
1035                     Con::warnf(line);
1036                  else
1037                     Con::errorf(line);
1038               }
1039#endif
1040
1041            }
1042         }
1043      }
1044   }
1045
1046   if (useFile)
1047      fws.close();
1048
1049   if( outUnflaggedRealloc != NULL )
1050      *outUnflaggedRealloc = unflaggedReAllocCount;
1051
1052   return unflaggedAllocCount;
1053}
1054
1055DefineEngineFunction(dumpUnflaggedAllocs, void, ( const char* fileName ), ( "" ), 
1056   "@brief Dumps all unflagged memory allocations.\n\n"
1057   "Dumps all memory allocations that were made after a call to flagCurrentAllocs(). "
1058   "Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage.\n\n"
1059   "@param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). "
1060   "If left blank, data will be dumped to the console.\n\n"
1061   "@tsexample\n"
1062   "dumpMemSnapshot(); // dumps info to console\n"
1063   "dumpMemSnapshot( \"C:/Torque/profilerlog1.txt\" ); // dumps info to file\n"
1064   "@endtsexample\n\n"
1065   "@note Available in debug builds only. "
1066   "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n"
1067   "@ingroup Debugging" )
1068{
1069   dumpUnflaggedAllocs(fileName);
1070}
1071
1072static void initLog()
1073{
1074   static const char* sInitString = " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) ---\r\n";
1075
1076   FileStream fws;
1077   fws.open(gLogFilename, Torque::FS::File::Write);
1078   fws.write(dStrlen(sInitString), sInitString);
1079   fws.close();
1080}
1081
1082#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1083static void logAlloc(const AllocatedHeader* hdr, S32 memSize)
1084{
1085   FileStream fws;
1086   fws.open(gLogFilename, Torque::FS::File::ReadWrite);
1087   fws.setPosition(fws.getStreamSize());
1088
1089   char buffer[1024];
1090   dSprintf(buffer, 1023, "alloc: %s %d %d %d\r\n",
1091            hdr->fileName != NULL ? hdr->fileName : "Undetermined",
1092            hdr->line, memSize, hdr->allocNum);
1093   fws.write(dStrlen(buffer), buffer);
1094   fws.close();
1095}
1096#endif
1097
1098#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1099static void logRealloc(const AllocatedHeader* hdr, S32 memSize)
1100{
1101   FileStream fws;
1102   fws.open(gLogFilename, Torque::FS::File::ReadWrite);
1103   fws.setPosition(fws.getStreamSize());
1104
1105   char buffer[1024];
1106   dSprintf(buffer, 1023, "realloc: %s %d %d %d\r\n",
1107            hdr->fileName != NULL ? hdr->fileName : "Undetermined",
1108            hdr->line, memSize, hdr->allocNum);
1109   fws.write(dStrlen(buffer), buffer);
1110   fws.close();
1111}
1112#endif
1113
1114#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1115static void logFree(const AllocatedHeader* hdr)
1116{
1117   FileStream fws;
1118   fws.open(gLogFilename, Torque::FS::File::ReadWrite);
1119   fws.setPosition(fws.getStreamSize());
1120
1121   char buffer[1024];
1122   dSprintf(buffer, 1023, "free:  %s %d %d\r\n",
1123            hdr->fileName != NULL ? hdr->fileName : "Undetermined",
1124            hdr->line, hdr->allocNum);
1125   fws.write(dStrlen(buffer), buffer);
1126   fws.close();
1127}
1128#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1129
1130#endif
1131
1132void enableLogging(const char* fileName)
1133{
1134   dStrcpy(gLogFilename, fileName, 256);
1135   if (!gEnableLogging)
1136   {
1137      gEnableLogging = true;
1138#ifdef TORQUE_DEBUG_GUARD
1139      initLog();
1140#endif
1141   }
1142}
1143
1144void disableLogging()
1145{
1146   gLogFilename[0] = '\0';
1147   gEnableLogging = false;
1148}
1149
1150// CodeReview - this is never called so commented out to save warning.
1151// Do we want to re-enable it?  Might be nice to get leak tracking on
1152// exit...or maybe that is just a problematical feature we shouldn't
1153// worry about.
1154//static void shutdown()
1155//{
1156//#ifdef TORQUE_MULTITHREAD
1157//   Mutex::destroyMutex(gMemMutex);
1158//   gMemMutex = NULL;
1159//#endif
1160//
1161//#ifdef TORQUE_DEBUG_GUARD
1162//
1163//   // write out leaks and such
1164//   const U32 maxNumLeaks = 1024;
1165//   U32 numLeaks = 0;
1166//
1167//   AllocatedHeader* pLeaks[maxNumLeaks];
1168//   for (PageRecord * walk = gPageList; walk; walk = walk->prevPage)
1169//      for(Header *probe = walk->headerList; probe; probe = probe->next)
1170//         if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL)
1171//            pLeaks[numLeaks++] = (AllocatedHeader *) probe;
1172//
1173//   if (numLeaks && !gNeverLogLeaks)
1174//   {
1175//      if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected.  Write to memoryLeaks.log?") == true) 
1176//      {
1177//         char buffer[1024];
1178//         FileStream logFile;
1179//         logFile.open("memoryLeaks.log", Torque::FS::File::Write);
1180//
1181//         for (U32 i = 0; i < numLeaks; i++)
1182//         {
1183//            dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum);
1184//            logFile.write(dStrlen(buffer), buffer);
1185//         }
1186//         logFile.close();
1187//      }
1188//   }
1189//#endif
1190//
1191//   // then free all the memory pages
1192//   for (PageRecord * walk = gPageList; walk; )
1193//   {
1194//      PageRecord *prev = walk->prevPage;
1195//      dRealFree(walk);
1196//      walk = prev;
1197//   }
1198//}
1199
1200#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1201static Header *allocMemPage(dsize_t pageSize)
1202{
1203   pageSize += sizeof(Header);
1204   if(pageSize < MinPageSize)
1205      pageSize = MinPageSize;
1206   PageRecord *base = allocPage(pageSize);
1207
1208   Header* rec = (Header*)base->basePtr;
1209   base->headerList = rec;
1210
1211   rec->size = pageSize - sizeof(Header);
1212   rec->next = NULL;
1213   rec->prev = NULL;
1214   rec->flags = 0;
1215#ifdef TORQUE_DEBUG_GUARD
1216   setGuard(rec, true);
1217#endif
1218
1219#ifdef LOG_PAGE_ALLOCS
1220   gPageBytesAllocated += pageSize;
1221   // total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string
1222   const U32 StrSize = 256;
1223   char strBytesAllocated[StrSize];
1224   if (gBytesAllocated > 0)
1225      dSprintf(strBytesAllocated, sizeof(strBytesAllocated), "%i", gBytesAllocated);
1226   else
1227      dStrncpy(strBytesAllocated,"unknown - enable TORQUE_DEBUG_GUARD", StrSize);
1228
1229#ifndef TORQUE_MULTITHREAD // May deadlock.
1230   // NOTE: This code may be called within Con::_printf, and if that is the case
1231   // this will infinitly recurse. This is the reason for the code in Con::_printf
1232   // that sets Con::active to false. -patw
1233   if (Con::isActive())
1234      Con::errorf("PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i)",strBytesAllocated,gPageBytesAllocated);
1235#endif
1236#endif
1237   return rec;
1238}
1239#endif
1240
1241#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1242static void checkUnusedAlloc(FreeHeader *header, U32 size)
1243{
1244   //validate();
1245   if(header->size >= size + sizeof(Header) + 16)
1246   {
1247      U8 *basePtr = (U8 *) header;
1248      basePtr += sizeof(Header);
1249      FreeHeader *newHeader = (FreeHeader *) (basePtr + size);
1250      newHeader->next = header->next;
1251      newHeader->prev = (Header *) header;
1252      header->next = (Header *) newHeader;
1253      if(newHeader->next)
1254         newHeader->next->prev = (Header *) newHeader;
1255      newHeader->flags = 0;
1256      newHeader->size = header->size - size - sizeof(Header);
1257      header->size = size;
1258#ifdef TORQUE_DEBUG_GUARD
1259      setGuard((Header *) newHeader, true);
1260#endif
1261      treeInsert(newHeader);
1262   }
1263}
1264#endif
1265
1266#if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1267static bool gReentrantGuard = false;
1268#endif
1269
1270#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1271static void* alloc(dsize_t size, bool array, const char* fileName, const U32 line)
1272{
1273   AssertFatal(size < MaxAllocationAmount, "Memory::alloc - tried to allocate > <a href="/coding/file/platformmemory_8cpp/#platformmemory_8cpp_1a3ffd6d180bfa999dfbafef07fab77f2ca51e0d57677c7f2ef51f5bd801811d0a8">MaxAllocationAmount</a>!");
1274
1275#ifdef TORQUE_MULTITHREAD
1276   if(!gMemMutex && !gReentrantGuard)
1277   {
1278      gReentrantGuard = true;
1279      gMemMutex = Mutex::createMutex();
1280      gReentrantGuard = false;
1281   }
1282
1283   if(!gReentrantGuard)
1284      Mutex::lockMutex(gMemMutex);
1285
1286#endif
1287
1288   AssertFatal(size < MaxAllocationAmount, "Size error.");
1289   //validate();
1290   if (size == 0)
1291   {
1292#ifdef TORQUE_MULTITHREAD
1293      if(!gReentrantGuard)
1294         Mutex::unlockMutex(gMemMutex);
1295#endif
1296      return NULL;
1297   }
1298
1299#ifndef TORQUE_ENABLE_PROFILE_PATH
1300   // Note: will cause crash if profile path is on
1301   PROFILE_START(MemoryAlloc);
1302#endif
1303
1304#ifdef TORQUE_DEBUG_GUARD
1305   // if we're guarding, round up to the nearest DWORD
1306   size = ((size + 3) & ~0x3);
1307#else
1308   // round up size to nearest 16 byte boundary (cache lines and all...)
1309   size = ((size + 15) & ~0xF);
1310#endif
1311
1312   FreeHeader *header = treeFindSmallestGreaterThan(size);
1313   if(header)
1314      treeRemove(header);
1315   else
1316      header = (FreeHeader *) allocMemPage(size);
1317
1318   // ok, see if there's enough room in the block to make another block
1319   // for this to happen it has to have enough room for a header
1320   // and 16 more bytes.
1321
1322   U8 *basePtr = (U8 *) header;
1323   basePtr += sizeof(Header);
1324
1325   checkUnusedAlloc(header, size);
1326
1327   AllocatedHeader *retHeader = (AllocatedHeader *) header;
1328   retHeader->flags = array ? (Allocated | Array) : Allocated;
1329
1330#ifdef TORQUE_DEBUG_GUARD
1331   retHeader->line = line;
1332   retHeader->fileName = fileName;
1333   retHeader->allocNum = gCurrAlloc;
1334   retHeader->realSize = size;
1335#ifdef TORQUE_ENABLE_PROFILE_PATH
1336   retHeader->profilePath = gProfiler ? gProfiler->getProfilePath() : "pre";
1337#endif
1338   gBytesAllocated += size;
1339   gBlocksAllocated ++;
1340   //static U32 skip = 0;
1341   //if ((++skip % 1000) == 0)
1342   //   Con::printf("new=%i",gBytesAllocated,gNewNewTotal,gImageAlloc);
1343   if (gEnableLogging)
1344      logAlloc(retHeader, size);
1345#endif
1346   if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF)
1347      Platform::debugBreak();
1348   gCurrAlloc++;
1349#ifndef TORQUE_ENABLE_PROFILE_PATH
1350   PROFILE_END();
1351#endif
1352   //validate();
1353
1354#ifdef TORQUE_DEBUG
1355   // fill the block with the fill value.  although this is done in free(), that won't fill
1356   // newly allocated MM memory (which hasn't been freed yet).  We use a different fill value
1357   // to diffentiate filled freed memory from filled new memory; this may aid debugging.
1358   #ifndef TORQUE_ENABLE_PROFILE_PATH
1359      PROFILE_START(stompMem1);
1360   #endif
1361   dMemset(basePtr, 0xCF, size);
1362   #ifndef TORQUE_ENABLE_PROFILE_PATH
1363      PROFILE_END();
1364   #endif
1365#endif
1366
1367   if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF)
1368      Platform::debugBreak();
1369
1370   gCurrAlloc++;
1371
1372#ifdef TORQUE_MULTITHREAD
1373   if(!gReentrantGuard)
1374      Mutex::unlockMutex(gMemMutex);
1375#endif
1376
1377   return basePtr;
1378}
1379#endif
1380
1381#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1382static void free(void* mem, bool array)
1383{
1384   // validate();
1385
1386   if (!mem)
1387      return;
1388
1389#ifdef TORQUE_MULTITHREAD
1390   if(!gMemMutex)
1391      gMemMutex = Mutex::createMutex();
1392
1393   if( mem != gMemMutex )
1394      Mutex::lockMutex(gMemMutex);
1395   else
1396      gMemMutex = NULL;
1397#endif
1398
1399   PROFILE_START(MemoryFree);
1400   AllocatedHeader *hdr = ((AllocatedHeader *)mem) - 1;
1401
1402   AssertFatal(hdr->flags & Allocated, avar("Not an allocated block!"));
1403   AssertFatal(((bool)((hdr->flags & Array)==Array))==array, avar("<a href="/coding/file/platformmemory_8cpp/#platformmemory_8cpp_1a3ffd6d180bfa999dfbafef07fab77f2caf8303e03242532eebd970f40ee509689">Array</a> alloc mismatch. "));
1404
1405   gBlocksAllocated --;
1406#ifdef TORQUE_DEBUG_GUARD
1407   gBytesAllocated -= hdr->realSize;
1408   if (gEnableLogging)
1409      logFree(hdr);
1410#endif
1411
1412   hdr->flags = 0;
1413
1414   // fill the block with the fill value
1415
1416#ifdef TORQUE_DEBUG
1417   #ifndef TORQUE_ENABLE_PROFILE_PATH
1418      PROFILE_START(stompMem2);
1419   #endif
1420   dMemset(mem, 0xCE, hdr->size);
1421   #ifndef TORQUE_ENABLE_PROFILE_PATH
1422      PROFILE_END();
1423   #endif
1424#endif
1425
1426   // see if we can merge hdr with the block after it.
1427
1428   Header* next = hdr->next;
1429   if (next && next->flags == 0)
1430   {
1431      treeRemove((FreeHeader *) next);
1432      hdr->size += next->size + sizeof(Header);
1433      hdr->next = next->next;
1434      if(next->next)
1435         next->next->prev = (Header *) hdr;
1436   }
1437
1438   // see if we can merge hdr with the block before it.
1439   Header* prev = hdr->prev;
1440
1441   if (prev && prev->flags == 0)
1442   {
1443      treeRemove((FreeHeader *) prev);
1444      prev->size += hdr->size + sizeof(Header);
1445      prev->next = hdr->next;
1446      if (hdr->next)
1447         hdr->next->prev = prev;
1448
1449      hdr = (AllocatedHeader *) prev;
1450   }
1451
1452   // throw this puppy into the tree!
1453   treeInsert((FreeHeader *) hdr);
1454   PROFILE_END();
1455
1456//   validate();
1457
1458#ifdef TORQUE_MULTITHREAD
1459   Mutex::unlockMutex(gMemMutex);
1460#endif
1461}
1462#endif
1463
1464#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1465static void* realloc(void* mem, dsize_t size, const char* fileName, const U32 line)
1466{
1467   //validate();
1468   if (!size) {
1469      free(mem, false);
1470      return NULL;
1471   }
1472   if(!mem)
1473      return alloc(size, false, fileName, line);
1474
1475#ifdef TORQUE_MULTITHREAD
1476   if(!gMemMutex)
1477      gMemMutex = Mutex::createMutex();
1478
1479   Mutex::lockMutex(gMemMutex);
1480#endif
1481
1482   AllocatedHeader* hdr = ((AllocatedHeader *)mem) - 1;
1483#ifdef TORQUE_DEBUG_GUARD
1484   checkGuard( ( Header* ) hdr, true );
1485#endif
1486
1487   AssertFatal((hdr->flags & Allocated) == Allocated, "Bad block flags.");
1488
1489   size = (size + 0xF) & ~0xF;
1490
1491   U32 oldSize = hdr->size;
1492   if(oldSize == size)
1493   {
1494#ifdef TORQUE_MULTITHREAD
1495      Mutex::unlockMutex(gMemMutex);
1496#endif
1497      return mem;
1498   }
1499   PROFILE_START(MemoryRealloc);
1500
1501   FreeHeader *next = (FreeHeader *) hdr->next;
1502
1503#ifdef TORQUE_DEBUG_GUARD
1504   // adjust header size and allocated bytes size
1505   hdr->realSize   += size - oldSize;
1506   gBytesAllocated += size - oldSize;
1507   if (gEnableLogging)
1508      logRealloc(hdr, size);
1509
1510   // Add reallocated flag, note header changes will not persist if the realloc 
1511   // decides tofree, and then perform a fresh allocation for the memory. The flag will 
1512   // be manually set again after this takes place, down at the bottom of this fxn.
1513   hdr->flags |= Reallocated;
1514
1515   // Note on Above ^
1516   // A more useful/robust implementation can be accomplished by storing an additional
1517   // AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure
1518   // itself to create a sort of reallocation history. This will be, essentially,
1519   // a allocation header stack for each allocation. Each time the memory is reallocated
1520   // it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain
1521   // it to the reallocation history chain, and the dump output changed to display
1522   // reallocation history. It is also important to clean up this chain during 'free'
1523   // using dRealFree (Since memory for the chain was allocated via dRealMalloc).
1524   //
1525   // See patw for details.
1526#endif
1527   if (next && !(next->flags & Allocated) && next->size + hdr->size + sizeof(Header) >= size)
1528   {
1529      // we can merge with the next dude.
1530      treeRemove(next);
1531      hdr->size += sizeof(Header) + next->size;
1532      hdr->next = next->next;
1533      if(next->next)
1534         next->next->prev = (Header *) hdr;
1535
1536      checkUnusedAlloc((FreeHeader *) hdr, size);
1537      //validate();
1538      PROFILE_END();
1539#ifdef TORQUE_MULTITHREAD
1540      Mutex::unlockMutex(gMemMutex);
1541#endif
1542      return mem;
1543   }
1544   else if(size < oldSize)
1545   {
1546      checkUnusedAlloc((FreeHeader *) hdr, size);
1547      PROFILE_END();
1548#ifdef TORQUE_MULTITHREAD
1549      Mutex::unlockMutex(gMemMutex);
1550#endif
1551      return mem;
1552   }
1553#ifdef TORQUE_DEBUG_GUARD
1554   // undo above adjustment because we're going though alloc instead
1555   hdr->realSize   -= size - oldSize;
1556   gBytesAllocated -= size - oldSize;
1557#endif
1558   void* ret = alloc(size, false, fileName, line);
1559   dMemcpy(ret, mem, oldSize);
1560   free(mem, false);
1561   PROFILE_END();
1562   
1563   // Re-enable the 'Reallocated' flag so that this allocation can be ignored by
1564   // a non-strict run of the flag/dumpunflagged. 
1565   hdr = ((AllocatedHeader *)ret) - 1;
1566   hdr->flags |= Reallocated;
1567
1568#ifdef TORQUE_MULTITHREAD
1569   Mutex::unlockMutex(gMemMutex);
1570#endif
1571   return ret;
1572}
1573#endif
1574
1575dsize_t getMemoryUsed()
1576{
1577   U32 size = 0;
1578
1579   PageRecord* walk;
1580   for (walk = gPageList; walk; walk = walk->prevPage) {
1581      for(Header *probe = walk->headerList; probe; probe = probe->next)
1582         if (probe->flags & Allocated) {
1583            size += probe->size;
1584         }
1585   }
1586
1587   return size;
1588}
1589
1590#ifdef TORQUE_DEBUG_GUARD
1591DefineEngineFunction( dumpAlloc, void, ( S32 allocNum ),,
1592            "@brief Dumps information about the given allocated memory block.\n\n"
1593            "@param allocNum <a href="/coding/namespace/namespacememory/">Memory</a> block <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> dump information about."
1594            "@note Available in debug builds only. "
1595            "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use this function.\n\n"
1596            "@ingroup Debugging")
1597{ 
1598   PageRecord* walk;
1599   for( walk = gPageList; walk; walk = walk->prevPage )
1600      for( Header* probe = walk->headerList; probe; probe = probe->next )
1601         if( probe->flags & Allocated )
1602         {
1603            AllocatedHeader* pah = ( AllocatedHeader* ) probe;
1604            if( pah->allocNum == allocNum )
1605            {
1606               Con::printf( "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>: %s\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1607                            "line: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1608                            "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a>: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1609                            "allocNum: %<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aa42a9a9cb6e2b93d7f825c395af871bf">i</a>\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1610                            "reallocated: %s",
1611                        pah->fileName != NULL ? pah->fileName : "Undetermined",
1612                        pah->line,
1613                        pah->realSize,
1614                        pah->allocNum,
1615                        pah->flags & Reallocated ? "yes" : "no"
1616               );
1617               
1618               // Dump the profile path, if we have one.
1619               
1620               #ifdef TORQUE_ENABLE_PROFILE_PATH
1621               if( pah->profilePath && pah->profilePath[ 0 ] )
1622                  Con::printf( "profilepath: %s", pah->profilePath );
1623               #endif
1624            }
1625         }
1626}
1627
1628DefineEngineFunction( dumpMemSnapshot, void, ( const char* fileName ),,
1629            "@brief Dumps <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> snapshot of current memory <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a>.\n\n"
1630            "The total memory used will also be output <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console.\n"
1631            "This function will attempt <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> if it does not already exist.\n"
1632            "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/namespace/namespacecubemapsaver/#namespacecubemapsaver_1a2e40c81f39564e68e4c85cba2fada0fc">save</a> profiling stats <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a>. Must use forward slashes (/)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1633            "@tsexample\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>"
1634            "dumpMemSnapshot( \"C:/Torque/ProfilerLogs/profilerlog1.txt\" );\n"
1635            "@endtsexample\n\n"
1636            "@note Available in debug builds only. "
1637            "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n"
1638            "@ingroup Debugging")
1639{
1640   FileStream fws;
1641   fws.open(fileName, Torque::FS::File::Write);
1642   char buffer[ 2048 ];
1643
1644   PageRecord* walk;
1645   for (walk = gPageList; walk; walk = walk->prevPage) {
1646      for(Header *probe = walk->headerList; probe; probe = probe->next)
1647         if (probe->flags & Allocated) {
1648            AllocatedHeader* pah = (AllocatedHeader*)probe;
1649            
1650            dSprintf( buffer, sizeof( buffer ), "%s%s\t%d\t%d\t%d\r\n",
1651                     pah->flags & Reallocated ? "[R] " : "",
1652                     pah->fileName != NULL ? pah->fileName : "Undetermined",
1653                     pah->line, pah->realSize, pah->allocNum);
1654            fws.write(dStrlen(buffer), buffer);
1655            
1656            // Dump the profile path, if we have one.
1657            
1658            #ifdef TORQUE_ENABLE_PROFILE_PATH
1659            if( pah->profilePath )
1660            {
1661               dSprintf( buffer, sizeof( buffer ), "%s\r\n\r\n", pah->profilePath );
1662               fws.write( dStrlen( buffer ), buffer );
1663            }
1664            #endif
1665         }
1666   }
1667
1668   Con::errorf("total memory used: %d",getMemoryUsed());
1669   fws.close();
1670}
1671#endif
1672
1673dsize_t getMemoryAllocated()
1674{
1675   return 0;
1676}
1677
1678void getMemoryInfo( void* ptr, Info& info )
1679{
1680   #ifndef TORQUE_DISABLE_MEMORY_MANAGER
1681   
1682   AllocatedHeader* header = ( ( AllocatedHeader* ) ptr ) - 1;
1683   
1684   info.mAllocSize      = header->size;
1685   #ifdef TORQUE_DEBUG_GUARD
1686   info.mAllocNumber    = header->allocNum;
1687   info.mLineNumber     = header->line;
1688   info.mFileName       = header->fileName;
1689   #endif
1690   info.mIsArray        = header->flags & Array;
1691   info.mIsGlobal       = header->flags & GlobalFlag;
1692   info.mIsStatic       = header->flags & StaticFlag;
1693   
1694   #endif
1695}
1696
1697void setBreakAlloc(U32 breakAlloc)
1698{
1699   gBreakAlloc = breakAlloc;
1700}
1701
1702ConsoleFunctionGroupEnd( Memory );
1703
1704} // namespace Memory
1705
1706void setMinimumAllocUnit(U32 allocUnit)
1707{
1708   AssertFatal(isPow2(allocUnit) && allocUnit > (2 << 20),
1709               "Error, allocunit must be a power of two, and greater than 2 megs");
1710
1711   MinPageSize = allocUnit;
1712}
1713
1714//---------------------------------------------------------------------------
1715
1716//---------------------------------------------------------------------------
1717
1718#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
1719
1720// Manage our own memory, add overloaded memory operators and functions
1721
1722void* FN_CDECL operator new(dsize_t size, const char* fileName, const U32 line)
1723{
1724   return Memory::alloc(size, false, fileName, line);
1725}
1726
1727void* FN_CDECL operator new[](dsize_t size, const char* fileName, const U32 line)
1728{
1729   return Memory::alloc(size, true, fileName, line);
1730}
1731
1732void* FN_CDECL operator new(dsize_t size)
1733{
1734   return Memory::alloc(size, false, NULL, 0);
1735}
1736
1737void* FN_CDECL operator new[](dsize_t size)
1738{
1739   return Memory::alloc(size, true, NULL, 0);
1740}
1741
1742void FN_CDECL operator delete(void* mem)
1743{
1744   Memory::free(mem, false);
1745}
1746
1747void FN_CDECL operator delete[](void* mem)
1748{
1749   Memory::free(mem, true);
1750}
1751
1752void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line)
1753{
1754   return Memory::alloc(in_size, false, fileName, line);
1755}
1756
1757void dFree(void* in_pFree)
1758{
1759   Memory::free(in_pFree, false);
1760}
1761
1762void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line)
1763{
1764   return Memory::realloc(in_pResize, in_size, fileName, line);
1765}
1766
1767AFTER_MODULE_INIT( Sim )
1768{
1769   Con::addVariable( "$Memory::numBlocksAllocated", TypeS32, &Memory::gBlocksAllocated,
1770      "Total number of memory blocks currently allocated.\n\n"
1771      "@ingroup Debugging" );
1772   Con::addVariable( "$Memory::numBytesAllocated", TypeS32, &Memory::gBytesAllocated,
1773      "Total number of bytes currently allocated.\n\n"
1774      "@ingroup Debugging" );
1775}
1776
1777#else
1778
1779// Don't manage our own memory
1780void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line)
1781{
1782   return malloc(in_size);
1783}
1784
1785void dFree(void* in_pFree)
1786{
1787   free(in_pFree);
1788}
1789
1790void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line)
1791{
1792   return realloc(in_pResize,in_size);
1793}
1794
1795#endif
1796