/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /////////////////////////////////////////////////////////////////////////////// // FastAllocator.h // // Implements simple and fast memory allocators. Includes sample code for // STL and overriding of specific or global new/delete. // // Allocators are very fast and prevent memory fragmentation. They use up // a little more space on average than generic new/malloc free/delete. // /////////////////////////////////////////////////////////////////////////////// #ifndef FASTALLOCATOR_H #define FASTALLOCATOR_H #if defined(_MSC_VER) #pragma once #endif //#define MEMORY_OVERWRITE_TEST /////////////////////////////////////////////////////////////////////////////// // Include files // #include "always.h" #include "wwdebug.h" #include "mutex.h" #include #include //size_t & ptrdiff_t definition #include /////////////////////////////////////////////////////////////////////////////// // Forward Declarations // class FastFixedAllocator; //Allocates and deletes items of a fixed size. class FastAllocatorGeneral; //Allocates and deletes items of any size. Can use as a fast replacement for global new/delete. /////////////////////////////////////////////////////////////////////////////// // StackAllocator // // Introduction: // This templated class allows you to allocate a dynamically sized temporary // block of memory local to a function in a way that causes it to use some // stack space instead of using the (very slow) malloc function. You don't // want to call malloc/new to allocate temporary blocks of memory when speed // is important. This class is only practical if you are allocating arrays of // objects (especially smaller objects). If you have a class whose size is // 40K, you aren't going to be able to put even one of them on the stack. // If you don't need an array but instead just need a single object, then you // can simply create your object on the stack and be done with it. However, // there are some times when you need a temporary array of objects (or just // an array of chars or pointers) but don't know the size beforehand. That's // where this class is useful. // // Parameters: // The class takes three templated parameters: T, nStackCount, bConstruct. // T -- The type of object being allocated. Examples include char, // cRZString, int*, cRZRect*. // nStackCount -- How many items to allocate on the stack before switching // to a heap allocation. Note that if you want to write // portable code, you need to use no more than about 2K // of stack space. So make sure nStackCount*sizeof(T) < 2048. // bConstruct -- This is normally 1, but setting it to 0 is a hint to the // compiler that you are constructing a type that has no // constructor. Testing showed that the VC++ compiler wasn't // completely able to optimize on its own. In particular, it // was smart enough to remove the while loop, but not smart // enough to remore the pTArrayEnd assignment. // // Benchmarks: // Testing one one machine showed that allocating and freeing a block of // 2048 bytes via malloc/free took 8700 and 8400 clock ticks respectively. // Using this stack allocator to do the same thing tooko 450 and 375 clock // ticks respectively. // // Usage and examples: // Since we are using a local allocator and not overloading global or class // operator new, you have to directory call StackAllocator::New instead // of operator new. So instead of saying: // SomeStruct* pStructArray = new SomeStruct[13]; // you say: // SomeStruct* pStructArray = stackAllocator.New(13); // // void Example(int nSize){ // StackAllocator stackAllocator; //Create an instance // char* pArray = stackAllocator.New(nSize); //Allocate memory, it will auto-freed. // memset(pArray, 0, nSize*sizeof(char)); //Do something with the memory. // } // // void Example(int nSize){ // StackAllocator stackAllocator; //Create an instance. We use the 'construct' hint feature here. // int** pArray = stackAllocator.New(nSize); //Allocate memory. // memset(pArray, 0, nSize*sizeof(int*)); //Do something with the memory. // stackAllocator.Delete(pArray); //In this example, we explicity free the memory. // } // // void Example(int nSize){ // StackAllocator stackAllocator; //Create an instance // cRZRect* pRectArray = stackAllocator.New(nSize); //Allocate memory, it will auto-freed. // pRectArray[0].SetRect(0,0,1,1); //Do something with the memory. // } // // void Example(int nSize){ // StackAllocator stackAllocator; //Create an instance // char* pArray = stackAllocator.New(nSize); //Allocate memory. // char* pArray2 = stackAllocator.New(nSize); //Allocate some additional memory. // memset(pArray, 0, nSize*sizeof(char)); //Do something with the memory. // stackAllocator.Delete(pArray); //Delete the memory // pArray = stackAllocator.New(nSize*2); //Allocate some new memory. We'll let the allocator free it. // memset(pArray, 0, nSize*2*sizeof(char)); //Do something with the new memory. // stackAllocator.Delete(pArray2); //Delete the additional memory. // } // template class StackAllocator{ public: StackAllocator() : mnAllocCount(-1), mpTHeap(NULL){} ~StackAllocator(){ if(mnAllocCount != -1){ //If there is anything to do... if(mpTHeap) delete mpTHeap; else{ if(bConstruct){ //Since this constant, the comparison gets optimized away. T* pTArray = (T*)mTArray; const T* const pTArrayEnd = pTArray + mnAllocCount; while(pTArray < pTArrayEnd){ pTArray->~T(); //Call the destructor on the object directly. ++pTArray; } } } } } T* New(unsigned nCount){ if(mnAllocCount == -1){ mnAllocCount = nCount; if(nCount < nStackCount){ //If the request is small enough to come from the stack... //We call the constructors of all the objects here. if(bConstruct){ //Since this constant, the comparison gets optimized away. T* pTArray = (T*)mTArray; const T* const pTArrayEnd = pTArray + nCount; while(pTArray < pTArrayEnd){ new(pTArray)T; //Use the placement operator new. This simply calls the constructor ++pTArray; //of T with 'this' set to the input address. Note that we don't put } //a '()' after the T this is because () causes trivial types like int } //and class* to be assigned zero/NULL. We don't want that. return (T*)mTArray; } //Else the request is too big. So let's use (the slower) operator new. return (mpTHeap = new T[nCount]); //The compiler will call the constructors here. } //Else we are being used. Let's be nice and allocate something anyway. return new T[nCount]; } void Delete(T* pT){ if(pT == (T*)mTArray){ //If the allocation came from our stack... if(bConstruct){ //Since this constant, the comparison gets optimized away. T* pTArray = (T*)mTArray; const T* const pTArrayEnd = pTArray + mnAllocCount; while(pTArray < pTArrayEnd){ pTArray->~T(); //Call the destructor on the object directly. ++pTArray; } } mnAllocCount = -1; } else if(pT == mpTHeap){ //If the allocation came from our heap... delete[] mpTHeap; //The compiler will call the destructors here. mpTHeap = NULL; //We clear these out so that we can possibly mnAllocCount = -1; // use the allocator again. } else //Else the allocation came from the external heap. delete[] pT; } protected: int mnAllocCount; //Count of objects allocated. -1 means that nothing is allocated. We don't use zero because zero is a legal allocation count in C++. T* mpTHeap; //This is normally NULL, but gets used of the allocation request is too high. char mTArray[nStackCount*sizeof(T)]; //This is our stack memory. }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // class FastFixedAllocator // class FastFixedAllocator { public: FastFixedAllocator(unsigned int n=0); ~FastFixedAllocator(); void Init(unsigned int n); //Useful for setting allocation size *after* construction, //but before first use. void* Alloc(); void Free(void* pAlloc); unsigned Get_Heap_Size() const { return TotalHeapSize; } unsigned Get_Allocated_Size() const { return TotalAllocatedSize; } unsigned Get_Allocation_Count() const { return TotalAllocationCount; } protected: struct Link { Link* next; }; struct Chunk { enum { size = 8*1024-16 }; Chunk* next; char mem[size]; }; Chunk* chunks; unsigned int esize; unsigned TotalHeapSize; unsigned TotalAllocatedSize; unsigned TotalAllocationCount; Link* head; void grow(); }; // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- WWINLINE void* FastFixedAllocator::Alloc() { TotalAllocationCount++; TotalAllocatedSize+=esize; if (head==0) { grow(); } Link* p = head; head = p->next; return p; } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- WWINLINE void FastFixedAllocator::Free(void* pAlloc) { TotalAllocationCount--; TotalAllocatedSize-=esize; Link* p = static_cast(pAlloc); p->next = head; head = p; } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- WWINLINE FastFixedAllocator::FastFixedAllocator(unsigned int n) : esize(1), TotalHeapSize(0), TotalAllocatedSize(0), TotalAllocationCount(0) { head = 0; chunks = 0; Init(n); } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- WWINLINE FastFixedAllocator::~FastFixedAllocator() { Chunk* n = chunks; while(n){ Chunk* p = n; n = n->next; delete p; } } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- WWINLINE void FastFixedAllocator::Init(unsigned int n) { esize = (nnext = chunks; chunks = n; TotalHeapSize+=sizeof(Chunk); const int nelem = Chunk::size/esize; char* start = n->mem; char* last = &start[(nelem-1)*esize]; for(char* p = start; p(p)->next = reinterpret_cast(p+esize); reinterpret_cast(last)->next = 0; head = reinterpret_cast(start); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // class FastAllocatorGeneral // // This class works by putting sizes into fixed size buckets. Each fixed size // bucket is a FastFixedAllocator. // class FastAllocatorGeneral { enum { MAX_ALLOC_SIZE=2048, ALLOC_STEP=16 }; public: FastAllocatorGeneral(); void* Alloc(unsigned int n); void Free(void* pAlloc); void* Realloc(void* pAlloc, unsigned int n); unsigned Get_Total_Heap_Size(); unsigned Get_Total_Allocated_Size(); unsigned Get_Total_Allocation_Count(); unsigned Get_Total_Actual_Memory_Usage() { return ActualMemoryUsage; } static FastAllocatorGeneral* Get_Allocator(); protected: FastFixedAllocator allocators[MAX_ALLOC_SIZE/ALLOC_STEP]; FastCriticalSectionClass CriticalSections[MAX_ALLOC_SIZE/ALLOC_STEP]; bool MemoryLeakLogEnabled; unsigned AllocatedWithMalloc; unsigned AllocatedWithMallocCount; unsigned ActualMemoryUsage; }; /////////////////////////////////////////////////////////////////////////////// WWINLINE unsigned FastAllocatorGeneral::Get_Total_Heap_Size() { int size=AllocatedWithMalloc; for (int i=0;i struct FastSTLAllocator{ typedef size_t size_type; //basically, "unsigned int" typedef ptrdiff_t difference_type; //basically, "int" typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; T* address(T& t) const { return (&t); } //These two are slightly strange but const T* address(const T& t) const { return (&t); } //required functions. Just do it. static T* allocate(size_t n, const void* =NULL) { return (T*)generalAllocator.Alloc(n*sizeof(T)); } static void construct(T* ptr, const T& value) { new(ptr) T(value); } static void deallocate(void* ptr, size_t /*n*/) { generalAllocator.Free(ptr); } static void destroy(T* ptr) { ptr->~T(); } static size_t max_size() { return (size_t)-1; } //This _Charalloc is required by VC++5 since it VC++5 predates //the language standardization. Allocator behaviour is one of the //last things to have been hammered out. Important note: If you //decide to write your own fast allocator, containers will allocate //random objects through this function but delete them through //the above delallocate() function. So your version of deallocate //should *not* assume that it will only be given T objects to delete. char* _Charalloc(size_t n){ return (char*)::generalAllocator.Alloc(n*sizeof(char)); } }; #else //This is a C++ language standard allocator. Most C++ compilers after 1999 //other than Microsoft C++ compile this fine. Otherwise. you might be able //to use the same allocator as VC++ uses above. template class FastSTLAllocator{ public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; template struct rebind { typedef FastSTLAllocator other; }; FastSTLAllocator() {} FastSTLAllocator(const FastSTLAllocator&) {} template FastSTLAllocator(const FastSTLAllocator&) {} ~FastSTLAllocator() {} pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } T* allocate(size_type n, const void* = NULL) { return n != 0 ? static_cast(generalAllocator.Alloc(n*sizeof(T))) : NULL; } void deallocate(pointer p, size_type n) { generalAllocator.Free(p); } size_type max_size() const { return size_t(-1) / sizeof(T); } void construct(pointer p, const T& val) { new(p) T(val); } void destroy(pointer p) { p->~T(); } }; #endif template WWINLINE bool operator==(const FastSTLAllocator&, const FastSTLAllocator&) { return true; } template WWINLINE bool operator!=(const FastSTLAllocator&, const FastSTLAllocator&) { return false; } /////////////////////////////////////////////////////////////////////////////// /* /////////////////////////////////////////////////////////////////////////////// // Example usage of fast allocators in STL. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include //Uncomment this if you have hash containers available. #include using namespace std; /////////////////////////////////////////////////////////////////////////////// // Here's how you declare a custom allocator for every regular STL container class: // typedef vector > IntArray; typedef list > IntList; typedef deque > IntDequeue; typedef queue > > IntQueue; typedef priority_queue > > IntPriorityQueue; typedef stack > > IntStack; typedef map, FastSTLAllocator > IntMap; typedef multimap, FastSTLAllocator > IntMultiMap; typedef set, FastSTLAllocator > IntSet; typedef multiset, FastSTLAllocator > IntMultiSet; //If you have the hashing containers available, here's how you do it: typedef hash_map, equal_to, FastSTLAllocator > IntHashMap; typedef hash_multimap, equal_to, FastSTLAllocator > IntHashMultiMap; typedef hash_set, equal_to, FastSTLAllocator > IntHashSet; typedef hash_multiset, equal_to, FastSTLAllocator > IntHashMultiSet; typedef basic_string, FastSTLAllocator > CharString; //bitset and valarray don't allow custom allocators. /////////////////////////////////////////////////////////////////////////////// void main(){ IntArray intArray; intArray.push_back(3); intArray.pop_back(); IntList intList; intList.push_back(3); intList.pop_back(); IntDequeue intDequeue; intDequeue.push_back(3); intDequeue.pop_back(); IntQueue intQueue; intQueue.push(3); intQueue.pop(); IntPriorityQueue intPriorityQueue; intPriorityQueue.push(3); intPriorityQueue.pop(); IntStack intStack; intStack.push(3); intStack.pop(); IntMap intMap; intMap.insert(pair(3,3)); intMap.erase(3); IntMultiMap intMultiMap; intMultiMap.insert(pair(3,3)); intMultiMap.erase(3); IntSet intSet; intSet.insert(3); intSet.erase(3); IntMultiSet intMultiSet; intMultiSet.insert(3); intMultiSet.erase(3); IntHashMap intHashMap; intHashMap.insert(3); intHashMap.erase(3); IntHashMultiMap intHashMultiMap; intHashMultiMap.insert(3); intHashMultiMap.erase(3); IntHashSet intHashSet; intHashSet.insert(3); intHashSet.erase(3); IntHashMultiSet intHashMultiSet; intHashMultiSet.insert(3); intHashMultiSet.erase(3); CharString charString; charString.append(1, '3'); charString.erase(charString.length()-1); //Shutdown printf("\nDone. Press the 'any' key\n"); getchar(); } */ #endif //sentry