khanat-opennel-code/code/nel/src/misc/fixed_size_allocator.cpp
2014-06-13 19:26:22 +02:00

211 lines
6.8 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#include "stdmisc.h"
#include "nel/misc/fixed_size_allocator.h"
#include "nel/misc/debug.h"
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLMISC
{
// *****************************************************************************************************************
CFixedSizeAllocator::CFixedSizeAllocator(uint numBytesPerBlock, uint numBlockPerChunk)
{
_FreeSpace = NULL;
_NumChunks = 0;
nlassert(numBytesPerBlock > 1);
_NumBytesPerBlock = numBytesPerBlock;
const uint mask = NL_DEFAULT_MEMORY_ALIGNMENT - 1;
_NumBytesPerBlock = (_NumBytesPerBlock + mask) & ~mask;
nlassert(_NumBytesPerBlock >= numBytesPerBlock);
_NumBlockPerChunk = std::max(numBlockPerChunk, (uint) 3);
_NumAlloc = 0;
}
// *****************************************************************************************************************
CFixedSizeAllocator::~CFixedSizeAllocator()
{
if (_NumAlloc != 0)
{
#ifdef NL_DEBUG
nlwarning("%d blocks were not freed", (int) _NumAlloc);
#endif
return;
}
if (_NumChunks > 0)
{
nlassert(_NumChunks == 1);
// delete the left chunk. This should force all the left nodes to be removed from the empty list
delete _FreeSpace->Chunk;
}
}
// *****************************************************************************************************************
void *CFixedSizeAllocator::alloc()
{
if (!_FreeSpace)
{
CChunk *chunk = new CChunk; // link a new chunk to that object
chunk->init(this);
}
++ _NumAlloc;
return _FreeSpace->unlink();
}
#define aligned_offsetof(s, m) ((offsetof(s, m) + (NL_DEFAULT_MEMORY_ALIGNMENT - 1)) & ~(NL_DEFAULT_MEMORY_ALIGNMENT - 1))
// *****************************************************************************************************************
void CFixedSizeAllocator::free(void *block)
{
if (!block) return;
/// get the node from the object
CNode *node = (CNode *) ((uint8 *) block - aligned_offsetof(CNode, Next));
//
nlassert(node->Chunk != NULL);
nlassert(node->Chunk->Allocator == this);
//
--_NumAlloc;
node->link();
}
// *****************************************************************************************************************
uint CFixedSizeAllocator::CChunk::getBlockSizeWithOverhead() const
{
nlctassert((sizeof(CNode) % NL_DEFAULT_MEMORY_ALIGNMENT) == 0);
return std::max((uint)(sizeof(CNode) - aligned_offsetof(CNode, Next)),
(uint)(Allocator->getNumBytesPerBlock())) + aligned_offsetof(CNode, Next);
}
// *****************************************************************************************************************
CFixedSizeAllocator::CChunk::CChunk()
{
NumFreeObjs = 0;
Allocator = NULL;
}
// *****************************************************************************************************************
CFixedSizeAllocator::CChunk::~CChunk()
{
nlassert(Allocator != NULL);
for (uint k = 0; k < Allocator->getNumBlockPerChunk(); ++k)
{
getNode(k).unlink();
}
nlassert(NumFreeObjs == 0);
nlassert(Allocator->_NumChunks > 0);
-- (Allocator->_NumChunks);
aligned_free(Mem); //delete[] Mem;
}
// *****************************************************************************************************************
void CFixedSizeAllocator::CChunk::init(CFixedSizeAllocator *alloc)
{
nlassert(!Allocator);
nlassert(alloc != NULL);
Allocator = alloc;
//
Mem = (uint8 *)aligned_malloc(getBlockSizeWithOverhead() * alloc->getNumBlockPerChunk(), NL_DEFAULT_MEMORY_ALIGNMENT); // new uint8[getBlockSizeWithOverhead() * alloc->getNumBlockPerChunk()];
//
getNode(0).Chunk = this;
getNode(0).Next = &getNode(1);
getNode(0).Prev = &alloc->_FreeSpace;
//
NumFreeObjs = alloc->getNumBlockPerChunk();
/// init all the element as a linked list
for (uint k = 1; k < (NumFreeObjs - 1); ++k)
{
getNode(k).Chunk = this;
getNode(k).Next = &getNode(k + 1);
getNode(k).Prev = &getNode(k - 1).Next;
}
getNode(NumFreeObjs - 1).Chunk = this;
getNode(NumFreeObjs - 1).Next = alloc->_FreeSpace;
getNode(NumFreeObjs - 1).Prev = &(getNode(NumFreeObjs - 2).Next);
if (alloc->_FreeSpace) { alloc->_FreeSpace->Prev = &getNode(NumFreeObjs - 1).Next; }
alloc->_FreeSpace = &getNode(0);
++(alloc->_NumChunks);
}
// *****************************************************************************************************************
CFixedSizeAllocator::CNode &CFixedSizeAllocator::CChunk::getNode(uint index)
{
nlassert(Allocator != NULL);
nlassert(index < Allocator->getNumBlockPerChunk());
return *(CNode *) ((uint8 *) Mem + index * getBlockSizeWithOverhead());
}
// *****************************************************************************************************************
void CFixedSizeAllocator::CChunk::add()
{
nlassert(Allocator);
// a node of this chunk has been given back
nlassert(NumFreeObjs < Allocator->getNumBlockPerChunk());
++ NumFreeObjs;
if (NumFreeObjs == Allocator->getNumBlockPerChunk()) // all objects back ?
{
if (Allocator->_NumChunks > 1) // we want to have at least one chunk left
{
delete this; // kill that object
}
}
}
// *****************************************************************************************************************
void CFixedSizeAllocator::CChunk::grab()
{
// a node of this chunk has been given back
nlassert(NumFreeObjs > 0);
-- NumFreeObjs;
}
// *****************************************************************************************************************
void *CFixedSizeAllocator::CNode::unlink()
{
nlassert(Prev != NULL);
if (Next) { Next->Prev = Prev;}
*Prev = Next;
nlassert(Chunk->NumFreeObjs > 0);
Chunk->grab(); // tells the containing chunk that a node has been allocated
return (void *)((uintptr_t)(this) + aligned_offsetof(CNode, Next)); //(void *) &Next;
}
// *****************************************************************************************************************
void CFixedSizeAllocator::CNode::link()
{
// destroy the obj to get back uninitialized memory
nlassert(Chunk);
nlassert(Chunk->Allocator);
CNode *&head = Chunk->Allocator->_FreeSpace;
Next = head;
Prev = &head;
if (Next)
{
nlassert(Next->Prev = &head);
Next->Prev = &Next;
}
Chunk->Allocator->_FreeSpace = this;
Chunk->add();
}
} // NLMISC