Imported Upstream version 2.81
[platform/upstream/libbullet.git] / Extras / CDTestFramework / Opcode / Ice / IceAllocator.cpp
1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 /**
3  *      Contains an allocator base class.
4  *      \file           IceAllocator.cpp
5  *      \author         Pierre Terdiman
6  *      \date           December, 19, 2003
7  */
8 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
10 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 // Precompiled Header
12 #include "StdAfx.h"
13 #include <malloc.h>
14
15 using namespace Opcode;
16
17 //#define ZERO_OVERHEAD_RELEASE
18 #define NEW_CODE
19 // For some reason dmalloc seems a lot slower than the system malloc?
20 //#define USE_DMALLOC
21
22 #ifdef USE_DMALLOC
23         #include "dmalloc.h"
24         #define LOCAL_MALLOC    dlmalloc
25         #define LOCAL_FREE              dlfree
26 #else
27         #define LOCAL_MALLOC    ::malloc
28         #define LOCAL_FREE              ::free
29 #endif
30
31
32 // WARNING: this makes allocations a lot slower. Only use when tracking memory leaks.
33 //#define ALLOC_STRINGS
34
35 // ### doesn't seem that useful
36 //#define       FAST_BUFFER_SIZE        256*1024
37
38 #define DEBUG_IDENTIFIER        0xBeefBabe
39 #define DEBUG_DEALLOCATED       0xDeadDead
40
41 #ifdef ALLOC_STRINGS
42 static const char* AllocString(const char* str)
43 {
44         if(!str)        return null;
45         char* mem = (char*)LOCAL_MALLOC(strlen(str)+1);
46         strcpy(mem, str);
47         return mem;
48 }
49
50 static void FreeString(const char* str)
51 {
52         if(str) LOCAL_FREE((void*)str);
53 }
54
55 #endif
56
57         class DefaultAllocator : public Allocator
58         {
59                 public:
60                                                                                 DefaultAllocator();
61                 virtual                                                 ~DefaultAllocator();
62
63                                                         void            reset();
64
65                 override(Allocator)     void*           malloc(size_t size, MemoryType type);
66                 override(Allocator)     void*           mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type);
67                 override(Allocator)     void*           realloc(void* memory, size_t size);
68                 override(Allocator)     void*           shrink(void* memory, size_t size);
69                 override(Allocator)     void            free(void* memory);
70
71                                                         void            DumpCurrentMemoryState() const;
72
73                                                         void**          mMemBlockList;
74                                                         udword          mMemBlockListSize;
75 #ifdef NEW_CODE
76                                                         udword          mFirstFree;
77 #else
78                                                         udword          mMemBlockFirstFree;
79 #endif
80                                                         udword          mMemBlockUsed;
81
82                                                         sdword          mNbAllocatedBytes;
83                                                         sdword          mHighWaterMark;
84                                                         sdword          mTotalNbAllocs;
85                                                         sdword          mNbAllocs;
86                                                         sdword          mNbReallocs;
87 #ifdef FAST_BUFFER_SIZE
88                                                         udword          mNbFastBytes;
89                                                         udword          mFastBufferOffset;
90                                                         ubyte*          mFastBuffer;
91 #endif
92         };
93
94 #define MEMBLOCKSTART           64
95
96         struct DebugBlock
97         {
98                 udword                  mCheckValue;
99 #ifdef FAST_BUFFER_SIZE
100                 MemoryType              mType;
101 #endif
102                 udword                  mSize;
103                 const char*             mFilename;
104                 udword                  mLine;
105                 udword                  mSlotIndex;
106                 const char*             mClassName;
107         };
108
109 #ifndef FAST_BUFFER_SIZE
110         ICE_COMPILE_TIME_ASSERT(sizeof(DebugBlock)==24);        // Prevents surprises.....
111 #endif
112
113 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
114
115 DefaultAllocator::DefaultAllocator() : mNbAllocatedBytes(0), mHighWaterMark(0), mTotalNbAllocs(0), mNbAllocs(0), mNbReallocs(0)
116 {
117         mMemBlockList = null;
118
119 #ifdef _DEBUG
120         // Initialize the Memory blocks list (DEBUG mode only)
121         mMemBlockList = (void**)LOCAL_MALLOC(MEMBLOCKSTART*sizeof(void*));
122         ZeroMemory(mMemBlockList, MEMBLOCKSTART*sizeof(void*));
123         mMemBlockListSize       = MEMBLOCKSTART;
124 #ifdef NEW_CODE
125         mFirstFree                      = INVALID_ID;
126 #else
127         mMemBlockFirstFree      = 0;
128 #endif
129         mMemBlockUsed           = 0;
130 #endif
131
132
133 #ifdef FAST_BUFFER_SIZE
134         mNbFastBytes            = 0;
135         mFastBufferOffset       = 0;
136         mFastBuffer                     = (ubyte*)LOCAL_MALLOC(FAST_BUFFER_SIZE);
137 #endif
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
141
142 DefaultAllocator::~DefaultAllocator()
143 {
144 #ifdef FAST_BUFFER_SIZE
145         mNbFastBytes = 0;
146         mFastBufferOffset = 0;
147         if(mFastBuffer) LOCAL_FREE(mFastBuffer);
148         mFastBuffer = null;
149 #endif
150
151 #ifdef _DEBUG
152
153         // Ok, it is a bad idea to use _F() here, because it internally uses the allocator (for the log string). So let's use good old C style here.
154         char Buffer[4096];
155
156         if(mNbAllocatedBytes)
157         {
158                 sprintf(Buffer, "Memory leak detected: %d bytes non released\n", mNbAllocatedBytes);
159 //              IceTrace(Buffer);
160 //              IceTrace(_F("Memory leak detected: %d bytes non released\n", mNbAllocatedBytes));
161         }
162         if(mNbAllocs)
163         {
164                 sprintf(Buffer, "Remaining allocs: %d\n", mNbAllocs);
165 //              IceTrace(Buffer);
166 //              IceTrace(_F("Remaining allocs: %d\n", mNbAllocs));
167         }
168 //      IceTrace(_F("Nb alloc: %d\n", mTotalNbAllocs));
169         sprintf(Buffer, "Total nb alloc: %d\n", mTotalNbAllocs);
170 //      IceTrace(Buffer);
171
172 //      IceTrace(_F("Nb realloc: %d\n", mNbReallocs));
173         sprintf(Buffer, "Nb realloc: %d\n", mNbReallocs);
174 //      IceTrace(Buffer);
175
176 //      IceTrace(_F("High water mark: %d Kb\n", mHighWaterMark/1024));
177         sprintf(Buffer, "High water mark: %d Kb\n", mHighWaterMark/1024);
178 //      IceTrace(Buffer);
179
180         // Scanning for memory leaks
181         if(mMemBlockList && mNbAllocs)
182         {
183                 udword NbLeaks = 0;
184 //              IceTrace("\n\n  ICE Message Memory leaks detected :\n\n");
185
186 #ifdef NEW_CODE
187                 for(udword i=0; i<mMemBlockUsed; i++)
188                 {
189                         if(udword(mMemBlockList[i])&1)
190                                 continue;
191
192                         const DebugBlock* DB = (const DebugBlock*)mMemBlockList[i];
193 //                      IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", cur+6, cur[1], (const char*)cur[5], (const char*)cur[2], cur[3]));
194 //                      IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine));
195                         sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine);
196 //                      IceTrace(Buffer);
197
198                         NbLeaks++;
199 //                      Free(cur+4);
200                 }
201 #else
202                 for(udword i=0, j=0; i<mMemBlockUsed; i++, j++)
203                 {
204                         // Skip empty slots
205                         while(!mMemBlockList[j]) j++;
206
207                         const DebugBlock* DB = (const DebugBlock*)mMemBlockList[j];
208 //                      IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", cur+6, cur[1], (const char*)cur[5], (const char*)cur[2], cur[3]));
209 //                      IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine));
210                         sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine);
211                         IceTrace(Buffer);
212
213                         NbLeaks++;
214 //                      Free(cur+4);
215                 }
216 #endif
217 //              IceTrace(_F("\n  Dump complete (%d leaks)\n\n", NbLeaks));
218                 sprintf(Buffer, "\n  Dump complete (%d leaks)\n\n", NbLeaks);
219 //              IceTrace(Buffer);
220         }
221         // Free the Memory Block list
222         if(mMemBlockList)       LOCAL_FREE(mMemBlockList);
223         mMemBlockList = null;
224 #endif
225 }
226
227 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
228
229 void DefaultAllocator::reset()
230 {
231         mNbAllocatedBytes       = 0;
232         mHighWaterMark          = 0;
233         mNbAllocs                       = 0;
234 }
235
236 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
237
238 void* DefaultAllocator::malloc(udword size, MemoryType type)
239 {
240 //      return ::malloc(size);
241
242 #ifdef _DEBUG
243         return mallocDebug(size, null, 0, "Undefined", type);
244 #endif
245
246         if(!size)
247         {
248 #ifdef _DEBUG
249 //              IceTrace("Warning: trying to allocate 0 bytes\n");
250 #endif
251                 return null;
252         }
253
254         mTotalNbAllocs++;
255         mNbAllocs++;
256
257         mNbAllocatedBytes+=size;
258         if(mNbAllocatedBytes>mHighWaterMark)    mHighWaterMark = mNbAllocatedBytes;
259
260 #ifdef ZERO_OVERHEAD_RELEASE
261         return LOCAL_MALLOC(size);
262 #else
263         void* ptr = (void*)LOCAL_MALLOC(size+8);
264         udword* blockStart = (udword*)ptr;
265         blockStart[0] = DEBUG_IDENTIFIER;
266         blockStart[1] = size;
267         return ((udword*)ptr)+2;
268 #endif
269 }
270
271 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
272
273 void* DefaultAllocator::mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type)
274 {
275 #ifdef _DEBUG
276         if(!size)
277         {
278 //              IceTrace("Warning: trying to allocate 0 bytes\n");
279                 return null;
280         }
281
282         // Catch improper use of alloc macro...
283         if(0 && class_name)
284         {
285                 const char* c = class_name;
286                 while(*c)
287                 {
288                         if(*c==']' || *c=='[')
289                         {
290                                 int stop=0;
291                         }
292                         c++;
293                 }
294         }
295
296         // Make sure size is even
297         if(size&1)      size++;
298
299 #ifdef FAST_BUFFER_SIZE
300         // Allocate one debug block in front of each real allocation
301         void* ptr = null;
302         if(type==MEMORY_TEMP)
303         {
304                 udword NeededSize = size + sizeof(DebugBlock);
305                 if(mFastBufferOffset + NeededSize <= FAST_BUFFER_SIZE)
306                 {
307                         ptr = mFastBuffer + mFastBufferOffset;
308                         mFastBufferOffset += NeededSize;
309                         mNbFastBytes += NeededSize;
310                 }
311         }
312
313         if(!ptr)
314         {
315                 ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock));
316                 type = MEMORY_PERSISTENT;
317         }
318 #else
319         // Allocate one debug block in front of each real allocation
320         void* ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock));
321 #endif
322         ASSERT(IS_ALIGNED_2(udword(ptr)));
323
324         // Fill debug block
325         DebugBlock* DB = (DebugBlock*)ptr;
326         DB->mCheckValue = DEBUG_IDENTIFIER;
327 #ifdef FAST_BUFFER_SIZE
328         DB->mType               = type;
329 #endif
330         DB->mSize               = size;
331         DB->mLine               = line;
332         DB->mSlotIndex  = INVALID_ID;
333 #ifdef ALLOC_STRINGS
334         DB->mFilename   = AllocString(filename);
335         DB->mClassName  = AllocString(class_name);
336 #else
337         DB->mFilename   = filename;
338         DB->mClassName  = class_name;
339 #endif
340
341         // Update global stats
342         mTotalNbAllocs++;
343         mNbAllocs++;
344         mNbAllocatedBytes += size;
345         if(mNbAllocatedBytes>mHighWaterMark)
346                 mHighWaterMark = mNbAllocatedBytes;
347
348         // Insert the allocated block in the debug memory block list
349         if(mMemBlockList)
350         {
351 #ifdef NEW_CODE
352                 if(mFirstFree!=INVALID_ID)
353                 {
354                         // Recycle old location
355
356                         udword NextFree = udword(mMemBlockList[mFirstFree]);
357                         if(NextFree!=INVALID_ID)        NextFree>>=1;
358
359                         mMemBlockList[mFirstFree] = ptr;
360                         DB->mSlotIndex = mFirstFree;
361
362                         mFirstFree = NextFree;
363                 }
364                 else
365                 {
366                         if(mMemBlockUsed==mMemBlockListSize)
367                         {
368                                 // Allocate a bigger block
369                                 void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*));
370                                 // Copy already used part
371                                 CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*));
372                                 // Initialize remaining part
373                                 void* Next = tps + mMemBlockListSize;
374                                 ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*));
375
376                                 // Free previous memory, setup new pointer
377                                 LOCAL_FREE(mMemBlockList);
378                                 mMemBlockList = tps;
379                                 // Setup new size
380                                 mMemBlockListSize += MEMBLOCKSTART;
381                         }
382
383                         mMemBlockList[mMemBlockUsed] = ptr;
384                         DB->mSlotIndex = mMemBlockUsed++;
385                 }
386 #else
387                 // Store allocated pointer in first free slot
388                 mMemBlockList[mMemBlockFirstFree] = ptr;
389                 DB->mSlotIndex = mMemBlockFirstFree;
390
391                 // Count number of used slots
392                 mMemBlockUsed++;
393
394                 // Resize if needed
395                 if(mMemBlockUsed==mMemBlockListSize)
396                 {
397                         // Allocate a bigger block
398                         void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*));
399                         // Copy already used part
400                         CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*));
401                         // Initialize remaining part
402                         void* Next = tps + mMemBlockListSize;
403                         ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*));
404
405                         // Free previous memory, setup new pointer
406                         LOCAL_FREE(mMemBlockList);
407                         mMemBlockList = tps;
408                         // -1 because we'll do a ++ just afterwards
409                         mMemBlockFirstFree = mMemBlockListSize-1;
410                         // Setup new size
411                         mMemBlockListSize += MEMBLOCKSTART;
412                 }
413
414                 // Look for first free ### recode this ugly thing
415                 while(mMemBlockList[++mMemBlockFirstFree] && (mMemBlockFirstFree<mMemBlockListSize));
416                 if(mMemBlockFirstFree==mMemBlockListSize)
417                 {
418                         mMemBlockFirstFree = (udword)-1;
419                         while(mMemBlockList[++mMemBlockFirstFree] && (mMemBlockFirstFree<mMemBlockListSize));
420                 }
421 #endif
422         }
423
424         return ((ubyte*)ptr) + sizeof(DebugBlock);
425 #else
426         Log("Error: mallocDebug has been called in release!\n");
427         ASSERT(0);//Don't use debug malloc for release mode code!
428         return 0;
429 #endif
430 }
431
432 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
433
434 void* DefaultAllocator::shrink(void* memory, udword size)
435 {
436         return null;    // #ifdef ZERO_OVERHEAD_RELEASE
437
438         if(!memory)     return null;
439 #ifdef _DEBUG
440         // Debug codepath
441
442         ubyte* SystemPointer = ((ubyte*)memory) - sizeof(DebugBlock);
443
444         DebugBlock* DB = (DebugBlock*)SystemPointer;
445         if(DB->mCheckValue!=DEBUG_IDENTIFIER)
446         {
447                 // Not a valid memory block
448                 return null;
449         }
450         if(size>DB->mSize)
451         {
452                 // New size should be smaller!
453                 return null;
454         }
455
456         //  Try to shrink the block
457         void* Reduced = _expand(SystemPointer, size + sizeof(DebugBlock));
458         if(!Reduced)    return null;
459
460         if(Reduced!=SystemPointer)
461         {
462                 // Should not be possible?!
463         }
464
465         // Update stats
466         mNbAllocatedBytes -= DB->mSize;
467         mNbAllocatedBytes += size;
468         // Setup new size
469         DB->mSize = size;
470
471         return memory;  // The pointer should not have changed!
472 #else
473         // Release codepath
474         udword* SystemPointer = ((udword*)memory)-2;
475         if(SystemPointer[0]!=DEBUG_IDENTIFIER)
476         {
477                 // Not a valid memory block
478                 return null;
479         }
480         if(size>SystemPointer[1])
481         {
482                 // New size should be smaller!
483                 return null;
484         }
485
486         //  Try to shrink the block
487         void* Reduced = _expand(SystemPointer, size+8);
488         if(!Reduced)    return null;
489
490         if(Reduced!=SystemPointer)
491         {
492                 // Should not be possible?!
493         }
494
495         // Update stats
496         mNbAllocatedBytes -= SystemPointer[1];
497         mNbAllocatedBytes += size;
498         // Setup new size
499         SystemPointer[1] = size;
500
501         return memory;  // The pointer should not have changed!
502 #endif
503 }
504
505 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
506
507 void* DefaultAllocator::realloc(void* memory, udword size)
508 {
509 //      return ::realloc(memory, size);
510
511         ASSERT(0);
512         return null;
513 }
514
515 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
516
517 void DefaultAllocator::free(void* memory)
518 {
519         if(!memory)
520         {
521 #ifdef _DEBUG
522 //              IceTrace("Warning: trying to free null pointer\n");
523 #endif
524                 return;
525         }
526
527 #ifdef _DEBUG
528         DebugBlock* DB = ((DebugBlock*)memory)-1;
529
530 //      DebugBlock TmpDB = *DB; // Keep a local copy to have readable data when ::free() fails!
531
532         // Check we allocated it
533         if(DB->mCheckValue!=DEBUG_IDENTIFIER)
534         {
535 //              IceTrace("Error: free unknown memory!!\n");
536                 // ### should we really continue??
537                 return;
538         }
539
540         // Update global stats
541         mNbAllocatedBytes -= DB->mSize;
542         mNbAllocs--;
543
544 #ifdef NEW_CODE
545         // Remove the block from the Memory block list
546         if(mMemBlockList)
547         {
548                 udword FreeSlot = DB->mSlotIndex;
549                 ASSERT(mMemBlockList[FreeSlot]==DB);
550
551                 udword NextFree = mFirstFree;
552                 if(NextFree!=INVALID_ID)
553                 {
554                         NextFree<<=1;
555                         NextFree|=1;
556                 }
557
558                 mMemBlockList[FreeSlot] = (void*)NextFree;
559                 mFirstFree = FreeSlot;
560         }
561 #else
562         udword MemBlockFirstFree = DB->mSlotIndex;      // The slot we are in
563         udword Line = DB->mLine;
564         const char* File = DB->mFilename;
565
566         // Remove the block from the Memory block list
567         if(mMemBlockList)
568         {
569                 ASSERT(mMemBlockList[MemBlockFirstFree]==DB);
570                 mMemBlockList[MemBlockFirstFree] = null;
571                 mMemBlockUsed--;
572         }
573 #endif
574
575 #ifdef ALLOC_STRINGS
576         FreeString(DB->mClassName);
577         FreeString(DB->mFilename);
578 #endif
579
580 #ifdef FAST_BUFFER_SIZE
581         if(DB->mType==MEMORY_TEMP)
582         {
583                 mNbFastBytes -= DB->mSize + sizeof(DebugBlock);
584                 if(mNbFastBytes==0)
585                 {
586                         mFastBufferOffset = 0;
587                 }
588                 return;
589         }
590 #endif
591
592         // ### should be useless since we'll release the memory just afterwards
593         DB->mCheckValue = DEBUG_DEALLOCATED;
594         DB->mSize               = 0;
595         DB->mClassName  = null;
596         DB->mFilename   = null;
597         DB->mSlotIndex  = INVALID_ID;
598         DB->mLine               = INVALID_ID;
599
600         LOCAL_FREE(DB);
601 #else
602         // Release codepath
603         #ifdef ZERO_OVERHEAD_RELEASE
604
605 //      mNbAllocatedBytes -= ptr[1];    // ### use _msize() ?
606         mNbAllocs--;
607         LOCAL_FREE(memory);
608
609         #else
610
611         udword* ptr = ((udword*)memory)-2;
612         if(ptr[0]!=DEBUG_IDENTIFIER)
613         {
614         #ifdef _DEBUG
615                 IceTrace("Error: free unknown memory!!\n");
616         #endif
617         }
618         mNbAllocatedBytes -= ptr[1];
619         if(mNbAllocatedBytes<0)
620         {
621         #ifdef _DEBUG
622                 IceTrace(_F("Oops (%d)\n", ptr[1]));
623         #endif
624         }
625         mNbAllocs--;
626         ptr[0]=DEBUG_DEALLOCATED;
627         ptr[1]=0;
628         LOCAL_FREE(ptr);
629
630         #endif
631 #endif
632 }
633
634 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
635
636 inline_ bool ValidAddress(const void* addy)
637 {
638 #ifdef NEW_CODE
639         return (addy && !(udword(addy)&1));
640 #else
641         return addy!=null;
642 #endif
643 }
644
645 void DefaultAllocator::DumpCurrentMemoryState() const
646 {
647 #ifdef _DEBUG
648         // Scanning for memory leaks
649         if(mMemBlockList && mMemBlockUsed)
650         {
651 //              IceTrace("\n\n----ALLOCATOR MEMORY DUMP:\n\n");
652
653                 // We can't just use the "const char*" stored in the debug blocks because they're not guaranteed to
654                 // be unique for similar strings. For example if a Container is allocated from two different DLLs,
655                 // the "Container" character string will be duplicated (one per DLL). So we need to group similar
656                 // strings together using the actual characters, not just the string address. We also have to do this
657                 // without allocating any new memory, since it would add new entries to the memory debug structure.
658                 //
659                 // The good news is that we don't care about speed too much in this function, since it's not supposed
660                 // to be called all the time.
661
662                 struct Local
663                 {
664                         struct TmpStruct
665                         {
666                                 const char*     mName;
667                                 udword          mSize;
668                         };
669                         static int SortCB(const void* elem1, const void* elem2)
670                         {
671                                 const TmpStruct* s1 = (const TmpStruct*)elem1;
672                                 const TmpStruct* s2 = (const TmpStruct*)elem2;
673                                 return strcmp(s1->mName, s2->mName);
674                         }
675                 };
676
677                 Local::TmpStruct* SortedStrings = (Local::TmpStruct*)LOCAL_MALLOC(sizeof(Local::TmpStruct)*mMemBlockListSize);
678                 udword NbStrings = 0;
679                 udword TotalSize = 0;
680                 for(udword i=0;i<mMemBlockListSize;i++)
681                 {
682                         if(ValidAddress(mMemBlockList[i]))
683                         {
684                                 const DebugBlock* DB = (const DebugBlock*)mMemBlockList[i];
685                                 if(DB->mClassName)
686                                 {
687                                         SortedStrings[NbStrings].mName = DB->mClassName;        // Memory by class
688 //                                      SortedStrings[NbStrings].mName = DB->mFilename;         // Memory by file
689                                         SortedStrings[NbStrings].mSize = DB->mSize;
690                                         TotalSize += DB->mSize;
691                                         NbStrings++;
692                                 }
693                         }
694                 }
695                 qsort(SortedStrings, NbStrings, sizeof(Local::TmpStruct), Local::SortCB);
696
697                 // Strings are now sorted. They might still be duplicated, i.e. we may have two strings for the same
698                 // class. So now we parse the list and collect used memory for all classes. Then we sort this again,
699                 // to report results in order of increasing memory.
700
701                 udword NbClasses=0;
702                 udword* Classes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings);
703                 udword* Sizes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings);
704
705                 udword CurrentSize = SortedStrings[0].mSize;
706                 const char* CurrentClass = SortedStrings[0].mName;
707                 for(udword i=1;i<=NbStrings;i++)        // One more time on purpose
708                 {
709                         const char* Current = null;
710                         if(i!=NbStrings)
711                         {
712                                 Current = SortedStrings[i].mName;
713                         }
714
715                         if(Current && strcmp(Current, CurrentClass)==0)
716                         {
717                                 // Same class
718                                 CurrentSize += SortedStrings[i].mSize;
719                         }
720                         else
721                         {
722                                 // New class
723
724                                 // Store previous class
725                                 if(CurrentClass)
726                                 {
727                                         Classes[NbClasses] = (udword)CurrentClass;      // We can store this pointer now because it's unique in our new array
728                                         Sizes[NbClasses++] = CurrentSize;
729                                 }
730
731                                 // Next one
732                                 if(Current)
733                                 {
734                                         CurrentClass = Current;
735                                         CurrentSize = SortedStrings[i].mSize;
736                                 }
737                         }
738                 }
739
740                 udword* Ranks0 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses);
741                 udword* Ranks1 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses);
742
743                 StackRadixSort(RS, Ranks0, Ranks1);
744                 const udword* Sorted = RS.Sort(Sizes, NbClasses).GetRanks();
745                 for(udword i=0;i<NbClasses;i++)
746                 {
747                         udword Index = Sorted[i];
748                         char Buffer[4096];
749                         sprintf(Buffer, "%s : %d\n", (const char*)Classes[Index], Sizes[Index]);
750 //                      IceTrace(Buffer);
751                 }
752                 char Buffer[4096];
753                 sprintf(Buffer, "Total size: %d\n", TotalSize);
754 //              IceTrace(Buffer);
755
756                 LOCAL_FREE(Ranks1);
757                 LOCAL_FREE(Ranks0);
758
759                 LOCAL_FREE(Sizes);
760                 LOCAL_FREE(Classes);
761
762                 LOCAL_FREE(SortedStrings);
763         }
764 #endif
765 }
766
767 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
768
769 // ### probably a bad idea to have a static var. Are we sure the "const char*" for class names/etc
770 // are still valid when this gets deleted?
771
772 static Allocator* gAllocator = null;
773 static DefaultAllocator gDefault;
774 //static DefaultAllocator* gDefault = null;
775
776 Allocator* IceCore::GetAllocator()
777 {
778         if(!gAllocator) gAllocator = &gDefault;
779 //      if(!gAllocator) gAllocator = gDefault;
780         return gAllocator;
781 }
782
783 bool IceCore::SetAllocator(Allocator& allocator)
784 {
785         // ### make sure nothing has been allocated from the default one
786         gAllocator = &allocator;
787         return true;
788 }
789
790 void IceCore::DumpMemory()
791 {
792         gDefault.DumpCurrentMemoryState();
793 //      if(gDefault)    gDefault->DumpCurrentMemoryState();
794 }
795
796 void InitDefaultAllocator()
797 {
798 //      gDefault = ::new DefaultAllocator;
799 }
800
801 void ReleaseDefaultAllocator()
802 {
803 //      if(gDefault)    ::delete gDefault;
804 //      gDefault = null;
805 }