// 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 . #include "stdmisc.h" #include "nel/misc/heap_memory.h" #include "nel/misc/debug.h" using namespace std; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif namespace NLMISC { // *************************************************************************** CHeapMemory::CHeapMemory() { reset(); // For allocate to work even if the heap is not initialized. _Alignment= 4; } // *************************************************************************** CHeapMemory::~CHeapMemory() { reset(); } // *************************************************************************** void CHeapMemory::reset() { _EmptySpaces.clear(); _EmptySpaceMap.clear(); _AllocatedSpaceMap.clear(); _HeapPtr= NULL; _HeapSize= 0; _HeapSizeUsed= 0; } // *************************************************************************** void CHeapMemory::initHeap(void *heap, uint size, uint align) { // setup alignement. if(align!=4 && align!=8 && align!=16 && align!=32) { nlstop; align= 4; } _Alignment= align; // Manage alignement. size= (size) & (~(_Alignment-1)); // clear container. reset(); if(heap==0 || size==0) return; _HeapPtr= (uint8*)heap; _HeapSize= size; _HeapSizeUsed= 0; // Add the only one empty space. CEmptySpace space; space.Ptr= _HeapPtr; space.Size= _HeapSize; addEmptySpace(space); } // *************************************************************************** void CHeapMemory::removeEmptySpace(CEmptySpace &space) { // remove the iterator on the spaceMap. _EmptySpaceMap.erase( space.SizeIt ); // remove from the list of EmptySpaces. NB: must fo it after all, because "space" may be deleted. _EmptySpaces.erase( space.Ptr ); } // *************************************************************************** void CHeapMemory::addEmptySpace(CEmptySpace &space) { // insert and get the iterator on the spaceMap. space.SizeIt= _EmptySpaceMap.insert( make_pair(space.Size, space.Ptr)); // insert into the list of EmptySpaces. _EmptySpaces.insert( make_pair(space.Ptr, space) ); } // *************************************************************************** void *CHeapMemory::allocate(uint size) { if(size==0) return NULL; // Manage alignement. size= (size + (_Alignment-1)) & (~(_Alignment-1)); // retrieve the best block. //========================= CEmptySpace bestSpace; // NB: do a copy, because of removeEmptySpace() which delete the space. // Find the smaller space which is >= than size. ItEmptySpaceSizeMap it; it= _EmptySpaceMap.lower_bound(size); // if not found, alloc fails. if(it == _EmptySpaceMap.end()) return NULL; else { // NB: this space must exist in the "array". bestSpace= _EmptySpaces[it->second]; } // remove this empty space from list. //========================= removeEmptySpace(bestSpace); // if any, add the space unused to the list. //========================= if(bestSpace.Size > size) { CEmptySpace space; space.Ptr= bestSpace.Ptr + size; space.Size= bestSpace.Size - size; addEmptySpace(space); } // return / insert the allocated space. //========================= _AllocatedSpaceMap.insert(make_pair(bestSpace.Ptr, size)); _HeapSizeUsed+= size; // return the ptr of start of this empty space. return bestSpace.Ptr; } // *************************************************************************** void CHeapMemory::free(void *ptr) { if(ptr==NULL) return; // Must find the array in allocated spaces. //========================== ItAllocatedSpaceMap itAlloc= _AllocatedSpaceMap.find((uint8*)ptr); if(itAlloc == _AllocatedSpaceMap.end()) { nlstop; return; } uint size= itAlloc->second; // free this space from allocated Spaces. _AllocatedSpaceMap.erase(itAlloc); _HeapSizeUsed-= size; // Must find previous or/and next empty space, if any. //========================== ItEmptySpacePtrMap itPrevious, itNext; // find the empty space which is immediately >= than ptr. itNext= _EmptySpaces.lower_bound((uint8*)ptr); // NB: it may be end(), if it is the last block (very rare). // some check. next empty space ptr must be after ptr. if(itNext!=_EmptySpaces.end()) { nlassert(itNext->second.Ptr >= (uint8*)ptr + size); } // if itNext is not the first empty space, there is an empty space before us. if( itNext!= _EmptySpaces.begin() ) { // NB: work even if itNext==end(). itPrevious= itNext; itPrevious--; // some check. previous empty space ptr must be before ptr. nlassert(itPrevious!=_EmptySpaces.end()); nlassert(itPrevious->second.Ptr + itPrevious->second.Size <= (uint8*)ptr ); } else itPrevious= _EmptySpaces.end(); // if next exist. if(itNext!=_EmptySpaces.end()) { // If Previous is not just after allocated ptr, it means that there is some allocated blocks beetween, // so it is not a valid empty space to concat. if(itNext->second.Ptr != (uint8*)ptr + size) itNext= _EmptySpaces.end(); } // if previous exist. if(itPrevious!=_EmptySpaces.end()) { // If Previous is not just before allocated ptr, it means that there is some allocated blocks beetween, // so it is not a valid empty space to concat. if(itPrevious->second.Ptr + itPrevious->second.Size != (uint8*)ptr ) itPrevious=_EmptySpaces.end(); } // According to configuration, build the new empty space, mreging previous and next, and remove old ones. //========================== CEmptySpace newSpace; // if no previous empty space, then newSpace start at ptr. if(itPrevious == _EmptySpaces.end()) { // Start with old allocated block. newSpace.Ptr= (uint8*)ptr; newSpace.Size= size; } // else, start at previous Ptr. else { // Start with previous block. size is previous size + allocated block size. newSpace.Ptr= itPrevious->second.Ptr; newSpace.Size= itPrevious->second.Size + size; } // if next empty space, must inc size. if(itNext != _EmptySpaces.end()) { newSpace.Size+= itNext->second.Size; } // remove old empty space, and add new one. //========================== // remove old empty spaces. if(itPrevious != _EmptySpaces.end()) removeEmptySpace(itPrevious->second); if(itNext != _EmptySpaces.end()) removeEmptySpace(itNext->second); // Add the new concatenated empty space. addEmptySpace(newSpace); } } // NLMISC