2014-06-12 17:27:09 +00:00
// 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 ;
2014-06-13 17:26:22 +00:00
const uint mask = NL_DEFAULT_MEMORY_ALIGNMENT - 1 ;
_NumBytesPerBlock = ( _NumBytesPerBlock + mask ) & ~ mask ;
nlassert ( _NumBytesPerBlock > = numBytesPerBlock ) ;
2014-06-12 17:27:09 +00:00
_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 ( ) ;
}
2014-06-13 17:26:22 +00:00
# define aligned_offsetof(s, m) ((offsetof(s, m) + (NL_DEFAULT_MEMORY_ALIGNMENT - 1)) & ~(NL_DEFAULT_MEMORY_ALIGNMENT - 1))
2014-06-12 17:27:09 +00:00
// *****************************************************************************************************************
void CFixedSizeAllocator : : free ( void * block )
{
if ( ! block ) return ;
/// get the node from the object
2014-06-13 17:26:22 +00:00
CNode * node = ( CNode * ) ( ( uint8 * ) block - aligned_offsetof ( CNode , Next ) ) ;
2014-06-12 17:27:09 +00:00
//
nlassert ( node - > Chunk ! = NULL ) ;
nlassert ( node - > Chunk - > Allocator = = this ) ;
//
- - _NumAlloc ;
node - > link ( ) ;
}
// *****************************************************************************************************************
uint CFixedSizeAllocator : : CChunk : : getBlockSizeWithOverhead ( ) const
{
2014-06-13 17:26:22 +00:00
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 ) ;
2014-06-12 17:27:09 +00:00
}
// *****************************************************************************************************************
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 ) ;
2014-06-13 17:26:22 +00:00
aligned_free ( Mem ) ; //delete[] Mem;
2014-06-12 17:27:09 +00:00
}
// *****************************************************************************************************************
void CFixedSizeAllocator : : CChunk : : init ( CFixedSizeAllocator * alloc )
{
nlassert ( ! Allocator ) ;
nlassert ( alloc ! = NULL ) ;
Allocator = alloc ;
//
2014-06-13 17:26:22 +00:00
Mem = ( uint8 * ) aligned_malloc ( getBlockSizeWithOverhead ( ) * alloc - > getNumBlockPerChunk ( ) , NL_DEFAULT_MEMORY_ALIGNMENT ) ; // new uint8[getBlockSizeWithOverhead() * alloc->getNumBlockPerChunk()];
2014-06-12 17:27:09 +00:00
//
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
2014-06-13 17:26:22 +00:00
return ( void * ) ( ( uintptr_t ) ( this ) + aligned_offsetof ( CNode , Next ) ) ; //(void *) &Next;
2014-06-12 17:27:09 +00:00
}
// *****************************************************************************************************************
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