// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef NL_BLOCK_MEMORY_H #define NL_BLOCK_MEMORY_H #include "types_nl.h" #include #include #include "debug.h" #include "mutex.h" namespace NLMISC { // *************************************************************************** /// See CBlockMemory::Purge extern bool NL3D_BlockMemoryAssertOnPurge; // =true. // *************************************************************************** /** * Block memory allocation * * This memory manager is a fast memory allocator, doing same thing as new/delete. It works by blocks. Blocks are always * allocated, never deleted. Allocation/free are in O(1). * * Elements with sizeof(T) class CBlockMemory { public: /// Constructor CBlockMemory(uint blockSize= 16) { nlassert(blockSize); _BlockSize= blockSize; _EltSize= std::max((uint)sizeof(T), (uint)sizeof(void*)); _NextFreeElt= NULL; _NAllocatedElts= 0; } // just copy setup from other blockMemory, don't copy data! CBlockMemory(const CBlockMemory &other) { _BlockSize= other._BlockSize; // if other block is rebinded, don't copy its rebinded size. _EltSize= (uint)std::max(sizeof(T), sizeof(void*)); // No elts allocated _NextFreeElt= NULL; _NAllocatedElts= 0; } /** purge() */ ~CBlockMemory() { purge(); } /// allocate an element. ctor is called. T* allocate() { _Mutex.enter(); // if not enough memory, aloc a block. if(!_NextFreeElt) { _Blocks.push_front(CBlock()); buildBlock(*_Blocks.begin()); // new free elt points to the beginning of this block. _NextFreeElt= (*_Blocks.begin()).Data; #ifdef NL_DEBUG // if debug, must decal for begin check. _NextFreeElt= (uint32*)_NextFreeElt + 1; #endif } // choose next free elt. nlassert(_NextFreeElt); T* ret= (T*)_NextFreeElt; // update _NextFreeElt, so it points to the next free element. _NextFreeElt= *(void**)_NextFreeElt; // construct the allocated element. if( __ctor_dtor__ ) new (ret) T; // some simple Check. #ifdef NL_DEBUG uint32 *checkStart= (uint32*)(void*)ret-1; uint32 *checkEnd = (uint32*)((uint8*)(void*)ret+_EltSize); nlassert( *checkStart == CheckDeletedIdent); nlassert( *checkEnd == CheckDeletedIdent); // if ok, mark this element as allocated. *checkStart= CheckAllocatedIdent; *checkEnd = CheckAllocatedIdent; #endif _NAllocatedElts++; _Mutex.leave(); return ret; } /// delete an element allocated with this manager. dtor is called. NULL is tested. void freeBlock(T* ptr) { if(!ptr) return; _Mutex.enter(); // some simple Check. nlassert(_NAllocatedElts>0); #ifdef NL_DEBUG uint32 *checkStart= (uint32*)(void*)ptr-1; uint32 *checkEnd = (uint32*)((uint8*)(void*)ptr+_EltSize); nlassert( *checkStart == CheckAllocatedIdent); nlassert( *checkEnd == CheckAllocatedIdent); // if ok, mark this element as deleted. *checkStart = *checkEnd = uint32(CheckDeletedIdent); #endif // destruct the element. if( __ctor_dtor__ ) ptr->~T(); // just append this freed element to the list. *(void**)ptr= _NextFreeElt; _NextFreeElt= (void*) ptr; _NAllocatedElts--; _Mutex.leave(); } /** delete all blocks, freeing all memory. It is an error to purge() or delete a CBlockMemory, while elements * still remains!! You must free your elements with free(). * NB: you can disable this assert if you set NL3D_BlockMemoryAssertOnPurge to false * (good to quit a program quickly without uninitialize). */ void purge () { _Mutex.enter(); if(NL3D_BlockMemoryAssertOnPurge) { nlassert(_NAllocatedElts==0); } while(_Blocks.begin()!=_Blocks.end()) { releaseBlock(*_Blocks.begin()); _Blocks.erase(_Blocks.begin()); } _NextFreeElt= NULL; _NAllocatedElts= 0; _Mutex.leave(); } // ******************** public: // This is to be used with CSTLBlockAllocator only!!! It changes the size of an element!! void __stl_alloc_changeEltSize(uint eltSize) { _Mutex.enter(); // must not be used with object ctor/dtor behavior. nlassert(__ctor_dtor__ == false); // format size. eltSize= std::max((uint)eltSize, (uint)sizeof(void*)); // if not the same size as before if(_EltSize!= eltSize) { // verify that rebind is made before any allocation!! nlassert(_Blocks.empty()); // change the size. _EltSize= eltSize; } _Mutex.leave(); }; // This is to be used with CSTLBlockAllocator only!!! uint __stl_alloc_getEltSize() const { return _EltSize; } private: /// size of a block. uint _BlockSize; /// size of an element in the block. uint _EltSize; /// number of elements allocated. sint _NAllocatedElts; /// next free element. void *_NextFreeElt; /// Must be ThreadSafe (eg: important for 3D PointLight and list of transform) CFastMutex _Mutex; /// a block. struct CBlock { /// The data allocated. void *Data; }; /// list of blocks. std::list _Blocks; /// For debug only, check ident. enum TCheckIdent { CheckAllocatedIdent= 0x01234567, CheckDeletedIdent= 0x89ABCDEF }; private: void buildBlock(CBlock &block) { uint i; uint32 nodeSize= _EltSize; #ifdef NL_DEBUG // must allocate more size for mem checks in debug. nodeSize+= 2*sizeof(uint32); #endif // allocate. block.Data = (void*)new uint8 [_BlockSize * nodeSize]; // by default, all elements are not allocated, build the list of free elements. void *ptr= block.Data; #ifdef NL_DEBUG // if debug, must decal for begin check. ptr= (uint32*)ptr + 1; #endif for(i=0; i<_BlockSize-1; i++) { // next elt. void *next= (uint8*)ptr + nodeSize; // points to the next element in this array. *(void**)ptr= next; // next. ptr= next; } // last element points to NULL. *(void**)ptr= NULL; // If debug, must init all check values to CheckDeletedIdent. #ifdef NL_DEBUG ptr= block.Data; // must decal for begin check. ptr= (uint32*)ptr + 1; // fill all nodes. for(i=0; i<_BlockSize; i++) { uint32 *checkStart= (uint32*)ptr-1; uint32 *checkEnd = (uint32*)((uint8*)ptr+_EltSize); // mark this element as deleted. *checkStart = *checkEnd = uint32(CheckDeletedIdent); // next elt. ptr= (uint8*)ptr + nodeSize; } #endif } void releaseBlock(CBlock &block) { delete [] ((uint8*)block.Data); } }; } // NLMISC #endif // NL_BLOCK_MEMORY_H /* End of block_memory.h */