replace old implementation with the ingeious from Rich Sposato
git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@200 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
parent
8d3a79495c
commit
7382c3dde0
2 changed files with 590 additions and 320 deletions
|
@ -13,7 +13,8 @@
|
|||
// without express or implied warranty.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Last update: June 20, 2001
|
||||
// $Header$
|
||||
|
||||
|
||||
#ifndef SMALLOBJ_INC_
|
||||
#define SMALLOBJ_INC_
|
||||
|
@ -21,161 +22,249 @@
|
|||
#include "Threads.h"
|
||||
#include "Singleton.h"
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <new> // needed for std::nothrow_t parameter.
|
||||
|
||||
#ifndef DEFAULT_CHUNK_SIZE
|
||||
#define DEFAULT_CHUNK_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef MAX_SMALL_OBJECT_SIZE
|
||||
#define MAX_SMALL_OBJECT_SIZE 64
|
||||
#define MAX_SMALL_OBJECT_SIZE 256
|
||||
#endif
|
||||
|
||||
#ifndef LOKI_DEFAULT_OBJECT_ALIGNMENT
|
||||
#define LOKI_DEFAULT_OBJECT_ALIGNMENT 4
|
||||
#endif
|
||||
|
||||
|
||||
namespace Loki
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class FixedAllocator
|
||||
// Offers services for allocating fixed-sized objects
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class FixedAllocator;
|
||||
|
||||
class FixedAllocator
|
||||
{
|
||||
private:
|
||||
struct Chunk
|
||||
{
|
||||
void Init(std::size_t blockSize, unsigned char blocks);
|
||||
void* Allocate(std::size_t blockSize);
|
||||
void Deallocate(void* p, std::size_t blockSize);
|
||||
void Reset(std::size_t blockSize, unsigned char blocks);
|
||||
void Release();
|
||||
unsigned char* pData_;
|
||||
unsigned char
|
||||
firstAvailableBlock_,
|
||||
blocksAvailable_;
|
||||
};
|
||||
|
||||
// Internal functions
|
||||
void DoDeallocate(void* p);
|
||||
Chunk* VicinityFind(void* p);
|
||||
|
||||
// Data
|
||||
std::size_t blockSize_;
|
||||
unsigned char numBlocks_;
|
||||
typedef std::vector<Chunk> Chunks;
|
||||
Chunks chunks_;
|
||||
Chunk* allocChunk_;
|
||||
Chunk* deallocChunk_;
|
||||
// For ensuring proper copy semantics
|
||||
mutable const FixedAllocator* prev_;
|
||||
mutable const FixedAllocator* next_;
|
||||
|
||||
public:
|
||||
// Create a FixedAllocator able to manage blocks of 'blockSize' size
|
||||
explicit FixedAllocator(std::size_t blockSize = 0);
|
||||
FixedAllocator(const FixedAllocator&);
|
||||
FixedAllocator& operator=(const FixedAllocator&);
|
||||
~FixedAllocator();
|
||||
|
||||
void Swap(FixedAllocator& rhs);
|
||||
|
||||
// Allocate a memory block
|
||||
void* Allocate();
|
||||
// Deallocate a memory block previously allocated with Allocate()
|
||||
// (if that's not the case, the behavior is undefined)
|
||||
void Deallocate(void* p);
|
||||
// Returns the block size with which the FixedAllocator was initialized
|
||||
std::size_t BlockSize() const
|
||||
{ return blockSize_; }
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallObjAllocator
|
||||
// Offers services for allocating small-sized objects
|
||||
// Manages pool of fixed-size allocators.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SmallObjAllocator
|
||||
{
|
||||
protected:
|
||||
SmallObjAllocator( std::size_t pageSize, std::size_t maxObjectSize,
|
||||
std::size_t objectAlignSize );
|
||||
|
||||
~SmallObjAllocator( void );
|
||||
|
||||
public:
|
||||
SmallObjAllocator(
|
||||
std::size_t chunkSize,
|
||||
std::size_t maxObjectSize);
|
||||
|
||||
void* Allocate(std::size_t numBytes);
|
||||
void Deallocate(void* p, std::size_t size);
|
||||
|
||||
void * Allocate( std::size_t size, bool doThrow );
|
||||
|
||||
void Deallocate( void * p, std::size_t size );
|
||||
|
||||
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
||||
|
||||
inline std::size_t GetAlignment() const { return objectAlignSize_; }
|
||||
|
||||
private:
|
||||
SmallObjAllocator(const SmallObjAllocator&);
|
||||
SmallObjAllocator& operator=(const SmallObjAllocator&);
|
||||
|
||||
typedef std::vector<FixedAllocator> Pool;
|
||||
Pool pool_;
|
||||
FixedAllocator* pLastAlloc_;
|
||||
FixedAllocator* pLastDealloc_;
|
||||
std::size_t chunkSize_;
|
||||
std::size_t maxObjectSize_;
|
||||
/// Copy-constructor is not implemented.
|
||||
SmallObjAllocator( const SmallObjAllocator & );
|
||||
/// Copy-assignment operator is not implemented.
|
||||
SmallObjAllocator & operator = ( const SmallObjAllocator & );
|
||||
|
||||
Loki::FixedAllocator * pool_;
|
||||
|
||||
std::size_t maxSmallObjectSize_;
|
||||
|
||||
std::size_t objectAlignSize_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class AllocatorSingleton
|
||||
// This template class is derived from SmallObjAllocator in order to pass template
|
||||
// arguments into SmallObjAllocator, and still have a default constructor for the
|
||||
// singleton.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template
|
||||
<
|
||||
template <class> class ThreadingModel,
|
||||
std::size_t chunkSize,
|
||||
std::size_t maxSmallObjectSize,
|
||||
std::size_t objectAlignSize
|
||||
>
|
||||
class AllocatorSingleton : public SmallObjAllocator
|
||||
{
|
||||
public:
|
||||
inline AllocatorSingleton() :
|
||||
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
||||
{}
|
||||
inline ~AllocatorSingleton( void ) {}
|
||||
|
||||
private:
|
||||
/// Copy-constructor is not implemented.
|
||||
AllocatorSingleton( const AllocatorSingleton & );
|
||||
/// Copy-assignment operator is not implemented.
|
||||
AllocatorSingleton & operator = ( const AllocatorSingleton & );
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallObjectBase
|
||||
// Base class for small object allocation classes.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template
|
||||
<
|
||||
template <class> class ThreadingModel,
|
||||
std::size_t chunkSize,
|
||||
std::size_t maxSmallObjectSize,
|
||||
std::size_t objectAlignSize,
|
||||
template <class> class LifetimePolicy
|
||||
>
|
||||
class SmallObjectBase
|
||||
{
|
||||
|
||||
#if (MAX_SMALL_OBJECT_SIZE != 0) && (DEFAULT_CHUNK_SIZE != 0) && (LOKI_DEFAULT_OBJECT_ALIGNMENT != 0)
|
||||
|
||||
/// Defines type of allocator.
|
||||
typedef AllocatorSingleton< ThreadingModel, chunkSize,
|
||||
maxSmallObjectSize, objectAlignSize > MyAllocator;
|
||||
|
||||
/// Defines type for thread-safety locking mechanism.
|
||||
typedef ThreadingModel< MyAllocator > MyThreadingModel;
|
||||
|
||||
/// Defines singleton made from allocator.
|
||||
typedef Loki::SingletonHolder< MyAllocator, Loki::CreateStatic,
|
||||
LifetimePolicy, ThreadingModel > MyAllocatorSingleton;
|
||||
|
||||
public:
|
||||
|
||||
/** Throwing single-object new.
|
||||
@note The exception specification is commented to prevent warning
|
||||
from MS compiler.
|
||||
*/
|
||||
static void * operator new ( std::size_t size ) // throw ( std::bad_alloc )
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
return MyAllocatorSingleton::Instance().Allocate( size, true );
|
||||
}
|
||||
|
||||
/// Non-throwing single-object new.
|
||||
static void * operator new ( std::size_t size, const std::nothrow_t & ) throw ()
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
return MyAllocatorSingleton::Instance().Allocate( size, false );
|
||||
}
|
||||
|
||||
/// Placement single-object new.
|
||||
inline static void * operator new ( std::size_t size, void * place )
|
||||
{
|
||||
return ::operator new( size, place );
|
||||
}
|
||||
|
||||
/// Single-object delete.
|
||||
static void operator delete ( void * p, std::size_t size ) throw ()
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
MyAllocatorSingleton::Instance().Deallocate( p, size );
|
||||
}
|
||||
|
||||
/// Non-throwing single-object delete.
|
||||
static void operator delete ( void * p, std::size_t size,
|
||||
const std::nothrow_t & ) throw()
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
MyAllocatorSingleton::Instance().Deallocate( p, size );
|
||||
}
|
||||
|
||||
/// Placement single-object delete.
|
||||
inline static void operator delete ( void * p, void * place )
|
||||
{
|
||||
::operator delete ( p, place );
|
||||
}
|
||||
|
||||
#endif // #if default template parameters are not zero
|
||||
|
||||
protected:
|
||||
inline SmallObjectBase( void ) {}
|
||||
inline SmallObjectBase( const SmallObjectBase & ) {}
|
||||
inline SmallObjectBase & operator = ( const SmallObjectBase & ) {}
|
||||
inline ~SmallObjectBase() {}
|
||||
}; // end class SmallObjectBase
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallObject
|
||||
// Base class for polymorphic small objects, offers fast
|
||||
// allocations/deallocations
|
||||
// Base class for polymorphic small objects, offers fast allocations &
|
||||
// deallocations. Destructor is virtual and public.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template
|
||||
<
|
||||
template <class> class ThreadingModel = DEFAULT_THREADING,
|
||||
std::size_t chunkSize = DEFAULT_CHUNK_SIZE,
|
||||
std::size_t maxSmallObjectSize = MAX_SMALL_OBJECT_SIZE
|
||||
std::size_t maxSmallObjectSize = MAX_SMALL_OBJECT_SIZE,
|
||||
std::size_t objectAlignSize = LOKI_DEFAULT_OBJECT_ALIGNMENT,
|
||||
template <class> class LifetimePolicy = Loki::NoDestroy
|
||||
>
|
||||
class SmallObject : public ThreadingModel<
|
||||
SmallObject<ThreadingModel, chunkSize, maxSmallObjectSize> >
|
||||
class SmallObject : public SmallObjectBase< ThreadingModel, chunkSize,
|
||||
maxSmallObjectSize, objectAlignSize, LifetimePolicy >
|
||||
{
|
||||
typedef ThreadingModel< SmallObject<ThreadingModel,
|
||||
chunkSize, maxSmallObjectSize> > MyThreadingModel;
|
||||
|
||||
struct MySmallObjAllocator : public SmallObjAllocator
|
||||
{
|
||||
MySmallObjAllocator()
|
||||
: SmallObjAllocator(chunkSize, maxSmallObjectSize)
|
||||
{}
|
||||
};
|
||||
// The typedef below would make things much simpler,
|
||||
// but MWCW won't like it
|
||||
// typedef SingletonHolder<MySmallObjAllocator/*, CreateStatic,
|
||||
// DefaultLifetime, ThreadingModel*/> MyAllocator;
|
||||
|
||||
|
||||
public:
|
||||
static void* operator new(std::size_t size)
|
||||
{
|
||||
#if (MAX_SMALL_OBJECT_SIZE != 0) && (DEFAULT_CHUNK_SIZE != 0)
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
|
||||
return SingletonHolder<MySmallObjAllocator, CreateStatic,
|
||||
PhoenixSingleton>::Instance().Allocate(size);
|
||||
#else
|
||||
return ::operator new(size);
|
||||
#endif
|
||||
}
|
||||
static void operator delete(void* p, std::size_t size)
|
||||
{
|
||||
#if (MAX_SMALL_OBJECT_SIZE != 0) && (DEFAULT_CHUNK_SIZE != 0)
|
||||
typename MyThreadingModel::Lock lock;
|
||||
(void)lock; // get rid of warning
|
||||
|
||||
SingletonHolder<MySmallObjAllocator, CreateStatic,
|
||||
PhoenixSingleton>::Instance().Deallocate(p, size);
|
||||
#else
|
||||
::operator delete(p);
|
||||
#endif
|
||||
}
|
||||
virtual ~SmallObject() {}
|
||||
};
|
||||
protected:
|
||||
inline SmallObject( void ) {}
|
||||
|
||||
private:
|
||||
/// Copy-constructor is not implemented.
|
||||
SmallObject( const SmallObject & );
|
||||
/// Copy-assignment operator is not implemented.
|
||||
SmallObject & operator = ( const SmallObject & );
|
||||
}; // end class SmallObject
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallValueObject
|
||||
// Base class for small objects with value semantics - offers fast allocations &
|
||||
// deallocations. Destructor is non-virtual, inline, and protected.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template
|
||||
<
|
||||
template <class> class ThreadingModel = DEFAULT_THREADING,
|
||||
std::size_t chunkSize = DEFAULT_CHUNK_SIZE,
|
||||
std::size_t maxSmallObjectSize = MAX_SMALL_OBJECT_SIZE,
|
||||
std::size_t objectAlignSize = LOKI_DEFAULT_OBJECT_ALIGNMENT,
|
||||
template <class> class LifetimePolicy = Loki::NoDestroy
|
||||
>
|
||||
class SmallValueObject : public SmallObjectBase< ThreadingModel, chunkSize,
|
||||
maxSmallObjectSize, objectAlignSize, LifetimePolicy >
|
||||
{
|
||||
protected:
|
||||
inline SmallValueObject( void ) {}
|
||||
inline SmallValueObject( const SmallValueObject & ) {}
|
||||
inline SmallValueObject & operator = ( const SmallValueObject & ) {}
|
||||
inline ~SmallValueObject() {}
|
||||
}; // end class SmallValueObject
|
||||
|
||||
} // namespace Loki
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Change log:
|
||||
// June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
|
||||
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // SMALLOBJ_INC_
|
||||
|
||||
// $Log$
|
||||
// Revision 1.2 2005/07/31 13:51:31 syntheticpp
|
||||
// replace old implementation with the ingeious from Rich Sposato
|
||||
//
|
||||
// Revision 1.2 2005/07/22 00:22:38 rich_sposato
|
||||
// Added SmallValueObject, SmallObjectBase, and AllocatorSingleton classes.
|
||||
//
|
||||
|
|
585
src/SmallObj.cpp
585
src/SmallObj.cpp
|
@ -13,29 +13,122 @@
|
|||
// without express or implied warranty.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Last update: March 20, 2001
|
||||
// $Header$
|
||||
|
||||
|
||||
#include "SmallObj.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
|
||||
using namespace Loki;
|
||||
|
||||
|
||||
namespace Loki
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class FixedAllocator
|
||||
// Offers services for allocating fixed-sized objects
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FixedAllocator
|
||||
{
|
||||
private:
|
||||
struct Chunk
|
||||
{
|
||||
bool Init( std::size_t blockSize, unsigned char blocks );
|
||||
void* Allocate(std::size_t blockSize);
|
||||
void Deallocate(void* p, std::size_t blockSize);
|
||||
void Reset(std::size_t blockSize, unsigned char blocks);
|
||||
void Release();
|
||||
inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const
|
||||
{ return ( pData_ <= p ) && ( p < pData_ + chunkLength ); }
|
||||
|
||||
inline bool HasAvailable( unsigned char numBlocks ) const
|
||||
{ return ( blocksAvailable_ == numBlocks ); }
|
||||
|
||||
inline bool IsFilled( void ) const
|
||||
{ return ( 0 == blocksAvailable_ ); }
|
||||
|
||||
unsigned char* pData_;
|
||||
unsigned char
|
||||
firstAvailableBlock_,
|
||||
blocksAvailable_;
|
||||
};
|
||||
|
||||
// Internal functions
|
||||
void DoDeallocate(void* p);
|
||||
|
||||
bool MakeNewChunk( void );
|
||||
|
||||
Chunk * VicinityFind( void * p );
|
||||
|
||||
/// Not implemented.
|
||||
FixedAllocator(const FixedAllocator&);
|
||||
/// Not implemented.
|
||||
FixedAllocator& operator=(const FixedAllocator&);
|
||||
|
||||
// Data
|
||||
std::size_t blockSize_;
|
||||
unsigned char numBlocks_;
|
||||
typedef std::vector<Chunk> Chunks;
|
||||
typedef Chunks::iterator ChunkIter;
|
||||
typedef Chunks::const_iterator ChunkCIter;
|
||||
Chunks chunks_;
|
||||
Chunk* allocChunk_;
|
||||
Chunk* deallocChunk_;
|
||||
Chunk * emptyChunk_;
|
||||
|
||||
public:
|
||||
// Create a FixedAllocator able to manage blocks of 'blockSize' size
|
||||
FixedAllocator();
|
||||
~FixedAllocator();
|
||||
|
||||
void Initialize( std::size_t blockSize, std::size_t pageSize );
|
||||
|
||||
// Allocate a memory block
|
||||
void * Allocate( void );
|
||||
|
||||
// Deallocate a memory block previously allocated with Allocate()
|
||||
// (if that's not the case, the behavior is undefined)
|
||||
bool Deallocate( void * p, bool doChecks );
|
||||
// Returns the block size with which the FixedAllocator was initialized
|
||||
inline std::size_t BlockSize() const
|
||||
{ return blockSize_; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Init
|
||||
// Initializes a chunk object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks)
|
||||
bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
||||
{
|
||||
assert(blockSize > 0);
|
||||
assert(blocks > 0);
|
||||
// Overflow check
|
||||
assert((blockSize * blocks) / blockSize == blocks);
|
||||
|
||||
pData_ = new unsigned char[blockSize * blocks];
|
||||
Reset(blockSize, blocks);
|
||||
const std::size_t allocSize = blockSize * blocks;
|
||||
assert( allocSize / blockSize == blocks);
|
||||
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
// If this new operator fails, it will throw, and the exception will get
|
||||
// caught one layer up.
|
||||
pData_ = new unsigned char[ allocSize ];
|
||||
#else
|
||||
// malloc can't throw, so its only way to indicate an error is to return
|
||||
// a NULL pointer, so we have to check for that.
|
||||
pData_ = static_cast< unsigned char * >( ::malloc( allocSize ) );
|
||||
if ( NULL == pData_ ) return false;
|
||||
#endif
|
||||
|
||||
Reset( blockSize, blocks );
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -68,7 +161,12 @@ void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
|
|||
|
||||
void FixedAllocator::Chunk::Release()
|
||||
{
|
||||
delete[] pData_;
|
||||
assert( NULL != pData_ );
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
delete [] pData_;
|
||||
#else
|
||||
::free( static_cast< void * >( pData_ ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -78,16 +176,14 @@ void FixedAllocator::Chunk::Release()
|
|||
|
||||
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
||||
{
|
||||
if (!blocksAvailable_) return 0;
|
||||
|
||||
if ( IsFilled() ) return NULL;
|
||||
|
||||
assert((firstAvailableBlock_ * blockSize) / blockSize ==
|
||||
firstAvailableBlock_);
|
||||
|
||||
unsigned char* pResult =
|
||||
pData_ + (firstAvailableBlock_ * blockSize);
|
||||
unsigned char * pResult = pData_ + (firstAvailableBlock_ * blockSize);
|
||||
firstAvailableBlock_ = *pResult;
|
||||
--blocksAvailable_;
|
||||
|
||||
|
||||
return pResult;
|
||||
}
|
||||
|
||||
|
@ -118,52 +214,12 @@ void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
|
|||
// Creates a FixedAllocator object of a fixed block size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FixedAllocator::FixedAllocator(std::size_t blockSize)
|
||||
: blockSize_(blockSize)
|
||||
, allocChunk_(0)
|
||||
, deallocChunk_(0)
|
||||
FixedAllocator::FixedAllocator()
|
||||
: blockSize_( 0 )
|
||||
, allocChunk_( NULL )
|
||||
, deallocChunk_( NULL )
|
||||
, emptyChunk_( NULL )
|
||||
{
|
||||
assert(blockSize_ > 0);
|
||||
|
||||
prev_ = next_ = this;
|
||||
|
||||
std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize;
|
||||
if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
|
||||
else if (numBlocks == 0) numBlocks = 8 * blockSize;
|
||||
|
||||
numBlocks_ = static_cast<unsigned char>(numBlocks);
|
||||
assert(numBlocks_ == numBlocks);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::FixedAllocator(const FixedAllocator&)
|
||||
// Creates a FixedAllocator object of a fixed block size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FixedAllocator::FixedAllocator(const FixedAllocator& rhs)
|
||||
: blockSize_(rhs.blockSize_)
|
||||
, numBlocks_(rhs.numBlocks_)
|
||||
, chunks_(rhs.chunks_)
|
||||
{
|
||||
prev_ = &rhs;
|
||||
next_ = rhs.next_;
|
||||
rhs.next_->prev_ = this;
|
||||
rhs.next_ = this;
|
||||
|
||||
allocChunk_ = rhs.allocChunk_
|
||||
? &chunks_.front() + (rhs.allocChunk_ - &rhs.chunks_.front())
|
||||
: 0;
|
||||
|
||||
deallocChunk_ = rhs.deallocChunk_
|
||||
? &chunks_.front() + (rhs.deallocChunk_ - &rhs.chunks_.front())
|
||||
: 0;
|
||||
}
|
||||
|
||||
FixedAllocator& FixedAllocator::operator=(const FixedAllocator& rhs)
|
||||
{
|
||||
FixedAllocator copy(rhs);
|
||||
copy.Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -172,35 +228,57 @@ FixedAllocator& FixedAllocator::operator=(const FixedAllocator& rhs)
|
|||
|
||||
FixedAllocator::~FixedAllocator()
|
||||
{
|
||||
if (prev_ != this)
|
||||
{
|
||||
prev_->next_ = next_;
|
||||
next_->prev_ = prev_;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(prev_ == next_);
|
||||
Chunks::iterator i = chunks_.begin();
|
||||
for (; i != chunks_.end(); ++i)
|
||||
{
|
||||
assert(i->blocksAvailable_ == numBlocks_);
|
||||
for ( ChunkIter i( chunks_.begin() ); i != chunks_.end(); ++i )
|
||||
i->Release();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Swap
|
||||
// FixedAllocator::Initialize
|
||||
// Initializes the operational constraints for the FixedAllocator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FixedAllocator::Swap(FixedAllocator& rhs)
|
||||
void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
swap(blockSize_, rhs.blockSize_);
|
||||
swap(numBlocks_, rhs.numBlocks_);
|
||||
chunks_.swap(rhs.chunks_);
|
||||
swap(allocChunk_, rhs.allocChunk_);
|
||||
swap(deallocChunk_, rhs.deallocChunk_);
|
||||
assert( blockSize > 0 );
|
||||
assert( pageSize >= blockSize );
|
||||
blockSize_ = blockSize;
|
||||
|
||||
std::size_t numBlocks = pageSize / blockSize;
|
||||
if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
|
||||
else if ( numBlocks < 8 ) numBlocks = 8;
|
||||
|
||||
numBlocks_ = static_cast<unsigned char>(numBlocks);
|
||||
assert(numBlocks_ == numBlocks);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::MakeNewChunk
|
||||
// Allocates a new Chunk for a FixedAllocator.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool FixedAllocator::MakeNewChunk( void )
|
||||
{
|
||||
bool allocated = false;
|
||||
try
|
||||
{
|
||||
// Calling chunks_.reserve *before* creating and initializing the new
|
||||
// Chunk means that nothing is leaked by this function in case an
|
||||
// exception is thrown from reserve.
|
||||
chunks_.reserve( chunks_.size() + 1 );
|
||||
Chunk newChunk;
|
||||
allocated = newChunk.Init( blockSize_, numBlocks_ );
|
||||
if ( allocated )
|
||||
chunks_.push_back( newChunk );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
allocated = false;
|
||||
}
|
||||
if ( !allocated ) return false;
|
||||
|
||||
allocChunk_ = &chunks_.back();
|
||||
deallocChunk_ = &chunks_.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -208,35 +286,50 @@ void FixedAllocator::Swap(FixedAllocator& rhs)
|
|||
// Allocates a block of fixed size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* FixedAllocator::Allocate()
|
||||
void * FixedAllocator::Allocate( void )
|
||||
{
|
||||
if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
|
||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
|
||||
if ( ( NULL == allocChunk_ ) || allocChunk_->IsFilled() )
|
||||
{
|
||||
Chunks::iterator i = chunks_.begin();
|
||||
for (;; ++i)
|
||||
if ( NULL != emptyChunk_ )
|
||||
{
|
||||
if (i == chunks_.end())
|
||||
allocChunk_ = emptyChunk_;
|
||||
emptyChunk_ = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( ChunkIter i( chunks_.begin() ); ; ++i )
|
||||
{
|
||||
// Initialize
|
||||
chunks_.reserve(chunks_.size() + 1);
|
||||
Chunk newChunk;
|
||||
newChunk.Init(blockSize_, numBlocks_);
|
||||
chunks_.push_back(newChunk);
|
||||
allocChunk_ = &chunks_.back();
|
||||
deallocChunk_ = &chunks_.front();
|
||||
break;
|
||||
}
|
||||
if (i->blocksAvailable_ > 0)
|
||||
{
|
||||
allocChunk_ = &*i;
|
||||
break;
|
||||
if ( chunks_.end() == i )
|
||||
{
|
||||
if ( !MakeNewChunk() )
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
if ( !i->IsFilled() )
|
||||
{
|
||||
allocChunk_ = &*i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(allocChunk_ != 0);
|
||||
assert(allocChunk_->blocksAvailable_ > 0);
|
||||
|
||||
return allocChunk_->Allocate(blockSize_);
|
||||
else if ( allocChunk_ == emptyChunk_)
|
||||
// detach emptyChunk_ from allocChunk_, because after
|
||||
// calling allocChunk_->Allocate(blockSize_); the chunk
|
||||
// isn't any more empty
|
||||
emptyChunk_ = NULL;
|
||||
|
||||
assert( allocChunk_ != NULL );
|
||||
assert( !allocChunk_->IsFilled() );
|
||||
void *place = allocChunk_->Allocate(blockSize_);
|
||||
|
||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
|
||||
return place;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -245,16 +338,28 @@ void* FixedAllocator::Allocate()
|
|||
// (undefined behavior if called with the wrong pointer)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FixedAllocator::Deallocate(void* p)
|
||||
bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||
{
|
||||
assert(!chunks_.empty());
|
||||
assert(&chunks_.front() <= deallocChunk_);
|
||||
assert(&chunks_.back() >= deallocChunk_);
|
||||
|
||||
deallocChunk_ = VicinityFind(p);
|
||||
assert(deallocChunk_);
|
||||
if ( doChecks )
|
||||
{
|
||||
assert(!chunks_.empty());
|
||||
assert(&chunks_.front() <= deallocChunk_);
|
||||
assert(&chunks_.back() >= deallocChunk_);
|
||||
assert( &chunks_.front() <= allocChunk_ );
|
||||
assert( &chunks_.back() >= allocChunk_ );
|
||||
}
|
||||
|
||||
Chunk * foundChunk = VicinityFind( p );
|
||||
if ( doChecks )
|
||||
{
|
||||
assert( NULL != foundChunk );
|
||||
}
|
||||
else if ( NULL == foundChunk )
|
||||
return false;
|
||||
|
||||
deallocChunk_ = foundChunk;
|
||||
DoDeallocate(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -262,44 +367,47 @@ void FixedAllocator::Deallocate(void* p)
|
|||
// Finds the chunk corresponding to a pointer, using an efficient search
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
|
||||
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
||||
{
|
||||
assert(!chunks_.empty());
|
||||
if ( chunks_.empty() ) return NULL;
|
||||
assert(deallocChunk_);
|
||||
|
||||
unsigned char * pc = static_cast< unsigned char * >( p );
|
||||
const std::size_t chunkLength = numBlocks_ * blockSize_;
|
||||
|
||||
Chunk* lo = deallocChunk_;
|
||||
Chunk* hi = deallocChunk_ + 1;
|
||||
Chunk* loBound = &chunks_.front();
|
||||
Chunk* hiBound = &chunks_.back() + 1;
|
||||
|
||||
// Special case: deallocChunk_ is the last in the array
|
||||
if (hi == hiBound) hi = 0;
|
||||
|
||||
// Special case: deallocChunk_ is the last in the array
|
||||
if (hi == hiBound) hi = NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (lo)
|
||||
{
|
||||
if (p >= lo->pData_ && p < lo->pData_ + chunkLength)
|
||||
if ( lo->HasBlock( pc, chunkLength ) ) return lo;
|
||||
if ( lo == loBound )
|
||||
{
|
||||
return lo;
|
||||
lo = NULL;
|
||||
if ( NULL == hi ) break;
|
||||
}
|
||||
if (lo == loBound) lo = 0;
|
||||
else --lo;
|
||||
}
|
||||
|
||||
|
||||
if (hi)
|
||||
{
|
||||
if (p >= hi->pData_ && p < hi->pData_ + chunkLength)
|
||||
if ( hi->HasBlock( pc, chunkLength ) ) return hi;
|
||||
if ( ++hi == hiBound )
|
||||
{
|
||||
return hi;
|
||||
hi = NULL;
|
||||
if ( NULL == lo ) break;
|
||||
}
|
||||
if (++hi == hiBound) hi = 0;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -309,129 +417,202 @@ FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
|
|||
|
||||
void FixedAllocator::DoDeallocate(void* p)
|
||||
{
|
||||
assert(deallocChunk_->pData_ <= p);
|
||||
assert(deallocChunk_->pData_ + numBlocks_ * blockSize_ > p);
|
||||
assert( deallocChunk_->HasBlock( static_cast< unsigned char * >( p ),
|
||||
numBlocks_ * blockSize_ ) );
|
||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
|
||||
// call into the chunk, will adjust the inner list but won't release memory
|
||||
deallocChunk_->Deallocate(p, blockSize_);
|
||||
|
||||
if (deallocChunk_->blocksAvailable_ == numBlocks_)
|
||||
if ( deallocChunk_->HasAvailable( numBlocks_ ) )
|
||||
{
|
||||
// deallocChunk_ is completely free, should we release it?
|
||||
|
||||
Chunk& lastChunk = chunks_.back();
|
||||
|
||||
if (&lastChunk == deallocChunk_)
|
||||
assert( emptyChunk_ != deallocChunk_ );
|
||||
// deallocChunk_ is empty, but a Chunk is only released if there are 2
|
||||
// empty chunks. Since emptyChunk_ may only point to a previously
|
||||
// cleared Chunk, if it points to something else besides deallocChunk_,
|
||||
// then FixedAllocator currently has 2 empty Chunks.
|
||||
if ( NULL != emptyChunk_ )
|
||||
{
|
||||
// check if we have two last chunks empty
|
||||
|
||||
if (chunks_.size() > 1 &&
|
||||
deallocChunk_[-1].blocksAvailable_ == numBlocks_)
|
||||
{
|
||||
// Two free chunks, discard the last one
|
||||
lastChunk.Release();
|
||||
chunks_.pop_back();
|
||||
allocChunk_ = deallocChunk_ = &chunks_.front();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastChunk.blocksAvailable_ == numBlocks_)
|
||||
{
|
||||
// Two free blocks, discard one
|
||||
lastChunk.Release();
|
||||
// If last Chunk is empty, just change what deallocChunk_
|
||||
// points to, and release the last. Otherwise, swap an empty
|
||||
// Chunk with the last, and then release it.
|
||||
Chunk * lastChunk = &chunks_.back();
|
||||
if ( lastChunk == deallocChunk_ )
|
||||
deallocChunk_ = emptyChunk_;
|
||||
else if ( lastChunk != emptyChunk_ )
|
||||
std::swap( *emptyChunk_, *lastChunk );
|
||||
assert( lastChunk->HasAvailable( numBlocks_ ) );
|
||||
lastChunk->Release();
|
||||
chunks_.pop_back();
|
||||
allocChunk_ = deallocChunk_;
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the empty chunk to the end
|
||||
std::swap(*deallocChunk_, lastChunk);
|
||||
allocChunk_ = &chunks_.back();
|
||||
}
|
||||
emptyChunk_ = deallocChunk_;
|
||||
}
|
||||
|
||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetOffset
|
||||
// Calculates index into array where a FixedAllocator of numBytes is located.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
|
||||
{
|
||||
const std::size_t alignExtra = alignment-1;
|
||||
return ( numBytes + alignExtra ) / alignment;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DefaultAllocator
|
||||
// Call to default allocator when SmallObjAllocator decides not to handle request.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void * DefaultAllocator( std::size_t numBytes, bool doThrow )
|
||||
{
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
return doThrow ? ::operator new( numBytes ) :
|
||||
::operator new( numBytes, std::nothrow_t() );
|
||||
#else
|
||||
void * p = ::malloc( numBytes );
|
||||
if ( doThrow && ( NULL == p ) )
|
||||
throw std::bad_alloc();
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DefaultDeallocator
|
||||
// Call to default deallocator when SmallObjAllocator decides not to handle request.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DefaultDeallocator( void * p )
|
||||
{
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
::operator delete( p );
|
||||
#else
|
||||
::free( p );
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::SmallObjAllocator
|
||||
// Creates an allocator for small objects given chunk size and maximum 'small'
|
||||
// object size
|
||||
// Creates a SmallObjAllocator, and all the FixedAllocators within it. Each
|
||||
// FixedAllocator is then initialized to use the correct Chunk size.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SmallObjAllocator::SmallObjAllocator(
|
||||
std::size_t chunkSize,
|
||||
std::size_t maxObjectSize)
|
||||
: pLastAlloc_(0), pLastDealloc_(0)
|
||||
, chunkSize_(chunkSize), maxObjectSize_(maxObjectSize)
|
||||
{
|
||||
SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,
|
||||
std::size_t maxObjectSize, std::size_t objectAlignSize ) :
|
||||
pool_( NULL ),
|
||||
maxSmallObjectSize_( maxObjectSize ),
|
||||
objectAlignSize_( objectAlignSize )
|
||||
{
|
||||
assert( 0 != objectAlignSize );
|
||||
const std::size_t allocCount = GetOffset( maxObjectSize, objectAlignSize );
|
||||
pool_ = new FixedAllocator[ allocCount ];
|
||||
for ( std::size_t i = 0; i < allocCount; ++i )
|
||||
pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize );
|
||||
}
|
||||
|
||||
namespace { // anoymous
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::~SmallObjAllocator
|
||||
// Deletes all memory consumed by SmallObjAllocator.
|
||||
// This deletes all the FixedAllocator's in the pool.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// See LWG DR #270
|
||||
struct CompareFixedAllocatorSize
|
||||
: std::binary_function<const FixedAllocator &, std::size_t, bool>
|
||||
SmallObjAllocator::~SmallObjAllocator( void )
|
||||
{
|
||||
bool operator()(const FixedAllocator &x, std::size_t numBytes) const
|
||||
{
|
||||
return x.BlockSize() < numBytes;
|
||||
}
|
||||
};
|
||||
|
||||
} // anoymous namespace
|
||||
delete [] pool_;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::Allocate
|
||||
// Allocates 'numBytes' memory
|
||||
// Uses an internal pool of FixedAllocator objects for small objects
|
||||
// Handles request to allocate numBytes for 1 object.
|
||||
// This acts in constant-time - except for the calls to DefaultAllocator
|
||||
// and sometimes FixedAllocator::Allocate. It throws bad_alloc only if the
|
||||
// doThrow parameter is true and can't allocate another block. Otherwise, it
|
||||
// provides the no-throw exception safety level.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* SmallObjAllocator::Allocate(std::size_t numBytes)
|
||||
void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
||||
{
|
||||
if (numBytes > maxObjectSize_) return operator new(numBytes);
|
||||
|
||||
if (pLastAlloc_ && pLastAlloc_->BlockSize() == numBytes)
|
||||
if ( numBytes > GetMaxObjectSize() )
|
||||
return DefaultAllocator( numBytes, doThrow );
|
||||
|
||||
assert( NULL != pool_ );
|
||||
if ( 0 == numBytes ) numBytes = 1;
|
||||
const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
|
||||
const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
|
||||
assert( index < allocCount );
|
||||
|
||||
FixedAllocator & allocator = pool_[ index ];
|
||||
assert( allocator.BlockSize() >= numBytes );
|
||||
assert( allocator.BlockSize() < numBytes + GetAlignment() );
|
||||
void * place = allocator.Allocate();
|
||||
if ( ( NULL == place ) && doThrow )
|
||||
{
|
||||
return pLastAlloc_->Allocate();
|
||||
#if _MSC_VER
|
||||
throw std::bad_alloc( "could not allocate small object" );
|
||||
#else
|
||||
// GCC did not like a literal string passed to std::bad_alloc.
|
||||
// so just throw the default-constructed exception.
|
||||
throw std::bad_alloc();
|
||||
#endif
|
||||
}
|
||||
Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes,
|
||||
CompareFixedAllocatorSize());
|
||||
if (i == pool_.end() || i->BlockSize() != numBytes)
|
||||
{
|
||||
i = pool_.insert(i, FixedAllocator(numBytes));
|
||||
pLastDealloc_ = &*pool_.begin();
|
||||
}
|
||||
pLastAlloc_ = &*i;
|
||||
return pLastAlloc_->Allocate();
|
||||
return place;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::Deallocate
|
||||
// Deallocates memory previously allocated with Allocate
|
||||
// (undefined behavior if you pass any other pointer)
|
||||
// Handles request to deallocate numBytes for 1 object.
|
||||
// This will act in constant-time - except for the calls to DefaultDeallocator
|
||||
// and sometimes FixedAllocator::Deallocate. It will never throw.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SmallObjAllocator::Deallocate(void* p, std::size_t numBytes)
|
||||
void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
||||
{
|
||||
if (numBytes > maxObjectSize_) return operator delete(p);
|
||||
|
||||
if (pLastDealloc_ && pLastDealloc_->BlockSize() == numBytes)
|
||||
if ( NULL == p ) return;
|
||||
if ( numBytes > GetMaxObjectSize() )
|
||||
{
|
||||
pLastDealloc_->Deallocate(p);
|
||||
DefaultDeallocator( p );
|
||||
return;
|
||||
}
|
||||
Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes,
|
||||
CompareFixedAllocatorSize());
|
||||
assert(i != pool_.end());
|
||||
assert(i->BlockSize() == numBytes);
|
||||
pLastDealloc_ = &*i;
|
||||
pLastDealloc_->Deallocate(p);
|
||||
assert( NULL != pool_ );
|
||||
if ( 0 == numBytes ) numBytes = 1;
|
||||
const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
|
||||
const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
|
||||
assert( index < allocCount );
|
||||
FixedAllocator & allocator = pool_[ index ];
|
||||
assert( allocator.BlockSize() >= numBytes );
|
||||
assert( allocator.BlockSize() < numBytes + GetAlignment() );
|
||||
const bool found = allocator.Deallocate( p, true );
|
||||
assert( found );
|
||||
}
|
||||
|
||||
} // end namespace Loki
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Change log:
|
||||
// March 20: fix exception safety issue in FixedAllocator::Allocate
|
||||
// (thanks to Chris Udazvinis for pointing that out)
|
||||
// June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
|
||||
// Aug 02, 2002: Fix in VicinityFind sent by Pavel Vozenilek
|
||||
// Nov 26, 2004: Re-implemented by Rich Sposato.
|
||||
// Jun 22, 2005: Fix in FixedAllocator::Allocate by Chad Lehman
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// $Log$
|
||||
// Revision 1.2 2005/07/31 13:51:31 syntheticpp
|
||||
// replace old implementation with the ingeious from Rich Sposato
|
||||
//
|
||||
// Revision 1.3 2005/07/28 07:02:58 syntheticpp
|
||||
// gcc -pedantic correction
|
||||
//
|
||||
// Revision 1.2 2005/07/20 08:44:19 syntheticpp
|
||||
// move MSVC
|
||||
//
|
||||
// Revision 1.9 2005/07/20 00:34:15 rich_sposato
|
||||
// Fixed overflow bug in calculating number of blocks per Chunk.
|
||||
//
|
||||
|
|
Loading…
Add table
Reference in a new issue