Changed include path to be direct instead of relying upon project settings.
git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@271 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
parent
dcb1d09cd5
commit
e529d13e1b
3 changed files with 333 additions and 180 deletions
|
@ -41,52 +41,121 @@ namespace Loki
|
||||||
{
|
{
|
||||||
class FixedAllocator;
|
class FixedAllocator;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class SmallObjAllocator Manages pool of fixed-size allocators.
|
||||||
// class SmallObjAllocator
|
Designed to be a non-templated base class of AllocatorSingleton so that
|
||||||
// Manages pool of fixed-size allocators.
|
implementation details can be safely hidden in the source code file.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
*/
|
||||||
|
|
||||||
class SmallObjAllocator
|
class SmallObjAllocator
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
/** The only available constructor needs certain parameters in order to
|
||||||
|
initialize all the FixedAllocator's. This throws only if
|
||||||
|
@param pageSize # of bytes in a page of memory.
|
||||||
|
@param maxObjectSize Max # of bytes which this may allocate.
|
||||||
|
@param objectAlignSize # of bytes between alignment boundaries.
|
||||||
|
*/
|
||||||
SmallObjAllocator( std::size_t pageSize, std::size_t maxObjectSize,
|
SmallObjAllocator( std::size_t pageSize, std::size_t maxObjectSize,
|
||||||
std::size_t objectAlignSize );
|
std::size_t objectAlignSize );
|
||||||
|
|
||||||
|
/** Destructor releases all blocks, all Chunks, and FixedAllocator's.
|
||||||
|
Any outstanding blocks are unavailable, and should not be used after
|
||||||
|
this destructor is called.
|
||||||
|
*/
|
||||||
~SmallObjAllocator( void );
|
~SmallObjAllocator( void );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/** Allocates a block of memory of requested size. Complexity is often
|
||||||
|
constant-time, but might be O(C) where C is the number of Chunks in a
|
||||||
|
FixedAllocator.
|
||||||
|
|
||||||
|
@par Exception Safety Level
|
||||||
|
Provides either strong-exception safety, or no-throw exception-safety
|
||||||
|
level depending upon doThrow parameter. The reason it provides two
|
||||||
|
levels of exception safety is because it is used by both the nothrow
|
||||||
|
and throwing new operators. The underlying implementation will never
|
||||||
|
throw of its own accord, but this can decide to throw if it does not
|
||||||
|
allocate. The only exception it should emit is std::bad_alloc.
|
||||||
|
|
||||||
|
@par Allocation Failure
|
||||||
|
If it does not allocate, it will call TrimExcessMemory and attempt to
|
||||||
|
allocate again, before it decides to throw or return NULL. Many
|
||||||
|
allocators loop through several new_handler functions, and terminate
|
||||||
|
if they can not allocate, but not this one. It only makes one attempt
|
||||||
|
using its own implementation of the new_handler, and then returns NULL
|
||||||
|
or throws so that the program can decide what to do at a higher level.
|
||||||
|
(Side note: Even though the C++ Standard allows allocators and
|
||||||
|
new_handlers to terminate if they fail, the Loki allocator does not do
|
||||||
|
that since that policy is not polite to a host program.)
|
||||||
|
|
||||||
|
@param size # of bytes needed for allocation.
|
||||||
|
@param doThrow True if this should throw if unable to allocate, false
|
||||||
|
if it should provide no-throw exception safety level.
|
||||||
|
@return NULL if nothing allocated and doThrow is false. Else the
|
||||||
|
pointer to an available block of memory.
|
||||||
|
*/
|
||||||
void * Allocate( std::size_t size, bool doThrow );
|
void * Allocate( std::size_t size, bool doThrow );
|
||||||
|
|
||||||
|
/** Deallocates a block of memory at a given place and of a specific
|
||||||
|
size. Complexity is almost always constant-time, and is O(C) only if
|
||||||
|
it has to search for which Chunk deallocates. This never throws.
|
||||||
|
*/
|
||||||
void Deallocate( void * p, std::size_t size );
|
void Deallocate( void * p, std::size_t size );
|
||||||
|
|
||||||
|
/** Deallocates a block of memory at a given place but of unknown size
|
||||||
|
size. Complexity is O(F + C) where F is the count of FixedAllocator's
|
||||||
|
in the pool, and C is the number of Chunks in all FixedAllocator's. This
|
||||||
|
does not throw exceptions. This overloaded version of Deallocate is
|
||||||
|
called by the nothow delete operator - which is called when the nothrow
|
||||||
|
new operator is used, but a constructor throws an exception.
|
||||||
|
*/
|
||||||
void Deallocate( void * p );
|
void Deallocate( void * p );
|
||||||
|
|
||||||
|
/// Returns max # of bytes which this can allocate.
|
||||||
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
||||||
|
|
||||||
|
/// Returns # of bytes between allocation boundaries.
|
||||||
inline std::size_t GetAlignment() const { return objectAlignSize_; }
|
inline std::size_t GetAlignment() const { return objectAlignSize_; }
|
||||||
|
|
||||||
|
/** Releases empty Chunks from memory. Complexity is O(F + C) where F
|
||||||
|
is the count of FixedAllocator's in the pool, and C is the number of
|
||||||
|
Chunks in all FixedAllocator's. This will never throw. This is called
|
||||||
|
by AllocatorSingleto::ClearExtraMemory, the new_handler function for
|
||||||
|
Loki's allocator, and is called internally when an allocation fails.
|
||||||
|
@return True if any memory released, or false if none released.
|
||||||
|
*/
|
||||||
bool TrimExcessMemory( void );
|
bool TrimExcessMemory( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Default-constructor is not implemented.
|
||||||
|
SmallObjAllocator( void );
|
||||||
/// Copy-constructor is not implemented.
|
/// Copy-constructor is not implemented.
|
||||||
SmallObjAllocator( const SmallObjAllocator & );
|
SmallObjAllocator( const SmallObjAllocator & );
|
||||||
/// Copy-assignment operator is not implemented.
|
/// Copy-assignment operator is not implemented.
|
||||||
SmallObjAllocator & operator = ( const SmallObjAllocator & );
|
SmallObjAllocator & operator = ( const SmallObjAllocator & );
|
||||||
|
|
||||||
|
/// Pointer to array of fixed-size allocators.
|
||||||
Loki::FixedAllocator * pool_;
|
Loki::FixedAllocator * pool_;
|
||||||
|
|
||||||
|
/// Largest object size supported by allocators.
|
||||||
std::size_t maxSmallObjectSize_;
|
std::size_t maxSmallObjectSize_;
|
||||||
|
|
||||||
|
/// Size of alignment boundaries.
|
||||||
std::size_t objectAlignSize_;
|
std::size_t objectAlignSize_;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class AllocatorSingleton This template class is derived from
|
||||||
// class AllocatorSingleton
|
SmallObjAllocator in order to pass template arguments into it, and still
|
||||||
// This template class is derived from SmallObjAllocator in order to pass template
|
have a default constructor for the singleton. Each instance is a unique
|
||||||
// arguments into SmallObjAllocator, and still have a default constructor for the
|
combination of all the template parameters, and hence is singleton only
|
||||||
// singleton.
|
with respect to those parameters. The template parameters have default
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
values and the class has typedefs identical to both SmallObject and
|
||||||
|
SmallValueObject so that this class can be used directly instead of going
|
||||||
|
through SmallObject or SmallValueObject. That design feature allows
|
||||||
|
clients to use the new_handler without having the name of the new_handler
|
||||||
|
function show up in classes derived from SmallObject or SmallValueObject.
|
||||||
|
Thus, the only functions in the allocator which show up in SmallObject or
|
||||||
|
SmallValueObject inheritance heierarchies are the new and delete operators.
|
||||||
|
*/
|
||||||
template
|
template
|
||||||
<
|
<
|
||||||
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||||
|
@ -110,16 +179,28 @@ namespace Loki
|
||||||
typedef Loki::SingletonHolder< MyAllocator, Loki::CreateStatic,
|
typedef Loki::SingletonHolder< MyAllocator, Loki::CreateStatic,
|
||||||
LifetimePolicy, ThreadingModel > MyAllocatorSingleton;
|
LifetimePolicy, ThreadingModel > MyAllocatorSingleton;
|
||||||
|
|
||||||
|
/// Returns reference to the singleton.
|
||||||
inline static AllocatorSingleton & Instance( void )
|
inline static AllocatorSingleton & Instance( void )
|
||||||
{
|
{
|
||||||
return MyAllocatorSingleton::Instance();
|
return MyAllocatorSingleton::Instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default constructor is not meant to be called directly.
|
||||||
inline AllocatorSingleton() :
|
inline AllocatorSingleton() :
|
||||||
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
/// The destructor is not meant to be called directly.
|
||||||
inline ~AllocatorSingleton( void ) {}
|
inline ~AllocatorSingleton( void ) {}
|
||||||
|
|
||||||
|
/** Clears any excess memory used by the allocator. Complexity is
|
||||||
|
O(F + C) where F is the count of FixedAllocator's in the pool, and C
|
||||||
|
is the number of Chunks in all FixedAllocator's. This never throws.
|
||||||
|
@note This function can be used as a new_handler when Loki and other
|
||||||
|
memory allocators can no longer allocate. Although the C++ Standard
|
||||||
|
allows new_handler functions to terminate the program when they can
|
||||||
|
not release any memory, this will not do so.
|
||||||
|
*/
|
||||||
static void ClearExtraMemory( void );
|
static void ClearExtraMemory( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -145,11 +226,12 @@ namespace Loki
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class SmallObjectBase Base class for small object allocation classes.
|
||||||
// class SmallObjectBase
|
The shared implementation of the new and delete operators are here instead
|
||||||
// Base class for small object allocation classes.
|
of being duplicated in both SmallObject or SmallValueObject. This class
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
is not meant to be used directly by clients, or derived from by clients.
|
||||||
|
Class has no data members so compilers can use Empty-Base-Optimization.
|
||||||
|
*/
|
||||||
template
|
template
|
||||||
<
|
<
|
||||||
template <class> class ThreadingModel,
|
template <class> class ThreadingModel,
|
||||||
|
@ -176,7 +258,7 @@ namespace Loki
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Throwing single-object new.
|
/// Throwing single-object new throws bad_alloc when allocation fails.
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
/// @note MSVC complains about non-empty exception specification lists.
|
/// @note MSVC complains about non-empty exception specification lists.
|
||||||
static void * operator new ( std::size_t size )
|
static void * operator new ( std::size_t size )
|
||||||
|
@ -189,7 +271,7 @@ namespace Loki
|
||||||
return MyAllocatorSingleton::Instance().Allocate( size, true );
|
return MyAllocatorSingleton::Instance().Allocate( size, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-throwing single-object new.
|
/// Non-throwing single-object new returns NULL if allocation fails.
|
||||||
static void * operator new ( std::size_t size, const std::nothrow_t & ) throw ()
|
static void * operator new ( std::size_t size, const std::nothrow_t & ) throw ()
|
||||||
{
|
{
|
||||||
typename MyThreadingModel::Lock lock;
|
typename MyThreadingModel::Lock lock;
|
||||||
|
@ -197,7 +279,7 @@ namespace Loki
|
||||||
return MyAllocatorSingleton::Instance().Allocate( size, false );
|
return MyAllocatorSingleton::Instance().Allocate( size, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Placement single-object new.
|
/// Placement single-object new merely calls global placement new.
|
||||||
inline static void * operator new ( std::size_t size, void * place )
|
inline static void * operator new ( std::size_t size, void * place )
|
||||||
{
|
{
|
||||||
return ::operator new( size, place );
|
return ::operator new( size, place );
|
||||||
|
@ -211,7 +293,9 @@ namespace Loki
|
||||||
MyAllocatorSingleton::Instance().Deallocate( p, size );
|
MyAllocatorSingleton::Instance().Deallocate( p, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-throwing single-object delete.
|
/** Non-throwing single-object delete is only called when nothrow
|
||||||
|
new operator is used, and the constructor throws an exception.
|
||||||
|
*/
|
||||||
static void operator delete ( void * p, const std::nothrow_t & ) throw()
|
static void operator delete ( void * p, const std::nothrow_t & ) throw()
|
||||||
{
|
{
|
||||||
typename MyThreadingModel::Lock lock;
|
typename MyThreadingModel::Lock lock;
|
||||||
|
@ -219,7 +303,7 @@ namespace Loki
|
||||||
MyAllocatorSingleton::Instance().Deallocate( p );
|
MyAllocatorSingleton::Instance().Deallocate( p );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Placement single-object delete.
|
/// Placement single-object delete merely calls global placement delete.
|
||||||
inline static void operator delete ( void * p, void * place )
|
inline static void operator delete ( void * p, void * place )
|
||||||
{
|
{
|
||||||
::operator delete ( p, place );
|
::operator delete ( p, place );
|
||||||
|
@ -235,12 +319,13 @@ namespace Loki
|
||||||
}; // end class SmallObjectBase
|
}; // end class SmallObjectBase
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class SmallObject Base class for polymorphic small objects, offers fast
|
||||||
// class SmallObject
|
allocations & deallocations. Destructor is virtual and public. Default
|
||||||
// Base class for polymorphic small objects, offers fast allocations &
|
constructor is trivial. Copy-constructor and copy-assignment operator are
|
||||||
// deallocations. Destructor is virtual and public.
|
not implemented since polymorphic classes almost always disable those
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
operations. Class has no data members so compilers can use
|
||||||
|
Empty-Base-Optimization.
|
||||||
|
*/
|
||||||
template
|
template
|
||||||
<
|
<
|
||||||
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||||
|
@ -266,12 +351,14 @@ namespace Loki
|
||||||
}; // end class SmallObject
|
}; // end class SmallObject
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class SmallValueObject Base class for small objects with value-type
|
||||||
// class SmallValueObject
|
semantics - offers fast allocations & deallocations. Destructor is
|
||||||
// Base class for small objects with value semantics - offers fast allocations &
|
non-virtual, inline, and protected to prevent unintentional destruction
|
||||||
// deallocations. Destructor is non-virtual, inline, and protected.
|
through base class. Default constructor is trivial. Copy-constructor
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
and copy-assignment operator are trivial since value-types almost always
|
||||||
|
need those operations. Class has no data members so compilers can use
|
||||||
|
Empty-Base-Optimization.
|
||||||
|
*/
|
||||||
template
|
template
|
||||||
<
|
<
|
||||||
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||||
|
@ -298,6 +385,9 @@ namespace Loki
|
||||||
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
||||||
//
|
//
|
||||||
// $Log$
|
// $Log$
|
||||||
|
// Revision 1.10 2005/09/26 21:38:54 rich_sposato
|
||||||
|
// Changed include path to be direct instead of relying upon project settings.
|
||||||
|
//
|
||||||
// Revision 1.9 2005/09/26 07:33:04 syntheticpp
|
// Revision 1.9 2005/09/26 07:33:04 syntheticpp
|
||||||
// move macros into LOKI_ namespace
|
// move macros into LOKI_ namespace
|
||||||
//
|
//
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
// Last update: June 20, 2001
|
// Last update: June 20, 2001
|
||||||
|
|
||||||
#include "loki/Singleton.h"
|
#include "../include/loki/Singleton.h"
|
||||||
|
|
||||||
using namespace Loki::Private;
|
using namespace Loki::Private;
|
||||||
|
|
||||||
|
|
351
src/SmallObj.cpp
351
src/SmallObj.cpp
|
@ -16,7 +16,7 @@
|
||||||
// $Header$
|
// $Header$
|
||||||
|
|
||||||
|
|
||||||
#include "loki/SmallObj.h"
|
#include "../include/loki/SmallObj.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -30,21 +30,95 @@ using namespace Loki;
|
||||||
namespace Loki
|
namespace Loki
|
||||||
{
|
{
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/** @class FixedAllocator
|
||||||
// class FixedAllocator
|
Offers services for allocating fixed-sized objects. It has a container
|
||||||
// Offers services for allocating fixed-sized objects
|
of "containers" of fixed-size blocks. The outer container has all the
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
Chunks. The inner container is a Chunk which owns some blocks.
|
||||||
|
|
||||||
|
@par Class Level Invariants
|
||||||
|
- There is always either zero or one Chunk which is empty.
|
||||||
|
- If this has no empty Chunk, then emptyChunk_ is NULL.
|
||||||
|
- If this has an empty Chunk, then emptyChunk_ points to it.
|
||||||
|
- If the Chunk container is empty, then deallocChunk_ and allocChunk_
|
||||||
|
are NULL.
|
||||||
|
- If the Chunk container is not-empty, then deallocChunk_ and allocChunk_
|
||||||
|
are either NULL or point to Chunks within the container.
|
||||||
|
- allocChunk_ will often point to the last Chunk in the container since
|
||||||
|
it was likely allocated most recently, and therefore likely to have an
|
||||||
|
available block.
|
||||||
|
*/
|
||||||
class FixedAllocator
|
class FixedAllocator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
/** @struct Chunk Contains info about each allocated Chunk.
|
||||||
|
This is a POD-style struct with value-semantics.
|
||||||
|
|
||||||
|
@par Minimal Interface
|
||||||
|
For the sake of runtime efficiency, no constructor, destructor, or
|
||||||
|
copy-assignment operator is defined. The inline functions made by the
|
||||||
|
compiler should be sufficient, and perhaps faster than hand-crafted
|
||||||
|
functions. The lack of these functions allows vector to create and
|
||||||
|
copy Chunks as needed without overhead. The Init and Release
|
||||||
|
functions do what the default constructor and destructor would do. A
|
||||||
|
Chunk is not in a usable state after it is constructed and before
|
||||||
|
calling Init. Nor is a Chunk usable after Release is called, but
|
||||||
|
before the destructor.
|
||||||
|
|
||||||
|
@par Efficiency
|
||||||
|
Down near the lowest level of the allocator, runtime efficiencies
|
||||||
|
trump almost all other considerations. Each function does the minimum
|
||||||
|
required of it. All functions should execute in constant time to
|
||||||
|
prevent higher-level code from unwittingly using a version of
|
||||||
|
Shlemiel the Painter's Algorithm.
|
||||||
|
|
||||||
|
@par Stealth Indexes
|
||||||
|
The first char of each empty block contains the index of the next
|
||||||
|
empty block. These stealth indexes form a singly-linked list within
|
||||||
|
the blocks. A Chunk is corrupt if this singly-linked list has a loop
|
||||||
|
or is shorter than blocksAvailable_. Much of the allocator's time and
|
||||||
|
space efficiency comes from how these stealth indexes are implemented.
|
||||||
|
*/
|
||||||
struct Chunk
|
struct Chunk
|
||||||
{
|
{
|
||||||
|
/** Initializes a just-constructed Chunk.
|
||||||
|
@param blockSize Number of bytes per block.
|
||||||
|
@param blocks Number of blocks per Chunk.
|
||||||
|
@return True for success, false for failure.
|
||||||
|
*/
|
||||||
bool Init( std::size_t blockSize, unsigned char blocks );
|
bool Init( std::size_t blockSize, unsigned char blocks );
|
||||||
void* Allocate(std::size_t blockSize);
|
|
||||||
void Deallocate(void* p, std::size_t blockSize);
|
/** Allocate a block within the Chunk. Complexity is always O(1),
|
||||||
void Reset(std::size_t blockSize, unsigned char blocks);
|
and this will never throw. Does not actually "allocate" by
|
||||||
|
calling malloc, new, or any other function, but merely adjusts
|
||||||
|
some internal indexes to indicate an already allocated block is
|
||||||
|
no longer available.
|
||||||
|
@return Pointer to block within Chunk.
|
||||||
|
*/
|
||||||
|
void * Allocate( std::size_t blockSize );
|
||||||
|
|
||||||
|
/** Deallocate a block within the Chunk. Complexity is always
|
||||||
|
O(1), and this will never throw. For efficiency, this assumes
|
||||||
|
the address is within the block and aligned along the correct
|
||||||
|
byte boundary. An assertion checks the alignment, and a call to
|
||||||
|
HasBlock is done from within VicinityFind. Does not actually
|
||||||
|
"deallocate" by calling free, delete, or any other function, but
|
||||||
|
merely adjusts some internal indexes to indicate a block is no
|
||||||
|
longer available.
|
||||||
|
*/
|
||||||
|
void Deallocate( void * p, std::size_t blockSize );
|
||||||
|
|
||||||
|
/** Resets the Chunk back to pristine values. The available count
|
||||||
|
is set back to zero, and the first available index is set to the
|
||||||
|
zeroth block. The stealth indexes inside each block are set to
|
||||||
|
point to the next block. This assumes the Chunk's data was already
|
||||||
|
using Init.
|
||||||
|
*/
|
||||||
|
void Reset( std::size_t blockSize, unsigned char blocks );
|
||||||
|
|
||||||
|
/// Releases the allocated block of memory.
|
||||||
void Release();
|
void Release();
|
||||||
|
|
||||||
|
/// Returns true if block at address P is inside this Chunk.
|
||||||
inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const
|
inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const
|
||||||
{ return ( pData_ <= p ) && ( p < pData_ + chunkLength ); }
|
{ return ( pData_ <= p ) && ( p < pData_ + chunkLength ); }
|
||||||
|
|
||||||
|
@ -54,65 +128,112 @@ namespace Loki
|
||||||
inline bool IsFilled( void ) const
|
inline bool IsFilled( void ) const
|
||||||
{ return ( 0 == blocksAvailable_ ); }
|
{ return ( 0 == blocksAvailable_ ); }
|
||||||
|
|
||||||
unsigned char* pData_;
|
/// Pointer to array of allocated blocks.
|
||||||
unsigned char
|
unsigned char * pData_;
|
||||||
firstAvailableBlock_,
|
/// Index of first empty block.
|
||||||
blocksAvailable_;
|
unsigned char firstAvailableBlock_;
|
||||||
|
/// Count of empty blocks.
|
||||||
|
unsigned char blocksAvailable_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Internal functions
|
/** Deallocates the block at address p, and then handles the internal
|
||||||
void DoDeallocate(void* p);
|
bookkeeping needed to maintain class invariants. This assumes that
|
||||||
|
deallocChunk_ points to the correct chunk.
|
||||||
|
*/
|
||||||
|
void DoDeallocate( void * p );
|
||||||
|
|
||||||
|
/** Creates an empty Chunk and adds it to the end of the ChunkList.
|
||||||
|
All calls to the lower-level memory allocation functions occur inside
|
||||||
|
this function, and so the only try-catch block is inside here.
|
||||||
|
@return true for success, false for failure.
|
||||||
|
*/
|
||||||
bool MakeNewChunk( void );
|
bool MakeNewChunk( void );
|
||||||
|
|
||||||
Chunk * VicinityFind( void * p );
|
/** Finds the Chunk which owns the block at address p. It starts at
|
||||||
|
deallocChunk_ and searches in both forwards and backwards directions
|
||||||
|
from there until it finds the Chunk which owns p. This algorithm
|
||||||
|
should find the Chunk quickly if it is deallocChunk_ or is close to it
|
||||||
|
in the Chunks container. This goes both forwards and backwards since
|
||||||
|
that works well for both same-order and opposite-order deallocations.
|
||||||
|
(Same-order = objects are deallocated in the same order in which they
|
||||||
|
were allocated. Opposite order = objects are deallocated in a last to
|
||||||
|
first order. Complexity is O(C) where C is count of all Chunks. This
|
||||||
|
never throws.
|
||||||
|
@return Pointer to Chunk that owns p, or NULL if no owner found.
|
||||||
|
*/
|
||||||
|
Chunk * VicinityFind( void * p ) const;
|
||||||
|
|
||||||
/// Not implemented.
|
/// Not implemented.
|
||||||
FixedAllocator(const FixedAllocator&);
|
FixedAllocator(const FixedAllocator&);
|
||||||
/// Not implemented.
|
/// Not implemented.
|
||||||
FixedAllocator& operator=(const FixedAllocator&);
|
FixedAllocator& operator=(const FixedAllocator&);
|
||||||
|
|
||||||
// Data
|
/// Type of container used to hold Chunks.
|
||||||
std::size_t blockSize_;
|
typedef std::vector< Chunk > Chunks;
|
||||||
unsigned char numBlocks_;
|
/// Iterator through container of Chunks.
|
||||||
typedef std::vector<Chunk> Chunks;
|
|
||||||
typedef Chunks::iterator ChunkIter;
|
typedef Chunks::iterator ChunkIter;
|
||||||
|
/// Iterator through const container of Chunks.
|
||||||
typedef Chunks::const_iterator ChunkCIter;
|
typedef Chunks::const_iterator ChunkCIter;
|
||||||
|
|
||||||
|
/// Number of bytes in a single block within a Chunk.
|
||||||
|
std::size_t blockSize_;
|
||||||
|
/// Number of blocks managed by each Chunk.
|
||||||
|
unsigned char numBlocks_;
|
||||||
|
|
||||||
|
/// Container of Chunks.
|
||||||
Chunks chunks_;
|
Chunks chunks_;
|
||||||
Chunk* allocChunk_;
|
/// Pointer to Chunk used for last or next allocation.
|
||||||
Chunk* deallocChunk_;
|
Chunk * allocChunk_;
|
||||||
|
/// Pointer to Chunk used for last or next deallocation.
|
||||||
|
Chunk * deallocChunk_;
|
||||||
|
/// Pointer to the only empty Chunk if there is one, else NULL.
|
||||||
Chunk * emptyChunk_;
|
Chunk * emptyChunk_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create a FixedAllocator able to manage blocks of 'blockSize' size
|
/// Create a FixedAllocator which manages blocks of 'blockSize' size.
|
||||||
FixedAllocator();
|
FixedAllocator();
|
||||||
|
|
||||||
|
/// Destroy the FixedAllocator and release all its Chunks.
|
||||||
~FixedAllocator();
|
~FixedAllocator();
|
||||||
|
|
||||||
|
/// Initializes a FixedAllocator by calculating # of blocks per Chunk.
|
||||||
void Initialize( std::size_t blockSize, std::size_t pageSize );
|
void Initialize( std::size_t blockSize, std::size_t pageSize );
|
||||||
|
|
||||||
// Allocate a memory block
|
/** Returns pointer to allocated memory block of fixed size - or NULL
|
||||||
|
if it failed to allocate.
|
||||||
|
*/
|
||||||
void * Allocate( void );
|
void * Allocate( void );
|
||||||
|
|
||||||
// Deallocate a memory block previously allocated with Allocate()
|
/** Deallocate a memory block previously allocated with Allocate. If
|
||||||
// (if that's not the case, the behavior is undefined)
|
the block is not owned by this FixedAllocator, it returns false so
|
||||||
|
that SmallObjAllocator can call the default deallocator. If the
|
||||||
|
block was found, this returns true.
|
||||||
|
*/
|
||||||
bool Deallocate( void * p, bool doChecks );
|
bool Deallocate( void * p, bool doChecks );
|
||||||
// Returns the block size with which the FixedAllocator was initialized
|
|
||||||
inline std::size_t BlockSize() const
|
|
||||||
{ return blockSize_; }
|
|
||||||
|
|
||||||
|
/// Returns block size with which the FixedAllocator was initialized.
|
||||||
|
inline std::size_t BlockSize() const { return blockSize_; }
|
||||||
|
|
||||||
|
/** Releases the memory used by the empty Chunk. This will take
|
||||||
|
constant time under any situation.
|
||||||
|
*/
|
||||||
bool TrimEmptyChunk( void );
|
bool TrimEmptyChunk( void );
|
||||||
|
|
||||||
|
/** Returns count of empty Chunks held by this allocator. Complexity
|
||||||
|
is O(C) where C is the total number of Chunks - empty or used.
|
||||||
|
*/
|
||||||
std::size_t CountEmptyChunks( void ) const;
|
std::size_t CountEmptyChunks( void ) const;
|
||||||
|
|
||||||
|
/** Returns true if the block at address p is within a Chunk owned by
|
||||||
|
this FixedAllocator. Complexity is O(C) where C is the total number
|
||||||
|
of Chunks - empty or used.
|
||||||
|
*/
|
||||||
bool HasBlock( void * p ) const;
|
bool HasBlock( void * p ) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Chunk::Init ------------------------------------------------
|
||||||
// FixedAllocator::Chunk::Init
|
|
||||||
// Initializes a chunk object
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
||||||
{
|
{
|
||||||
|
@ -125,7 +246,7 @@ bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
||||||
#ifdef USE_NEW_TO_ALLOCATE
|
#ifdef USE_NEW_TO_ALLOCATE
|
||||||
// If this new operator fails, it will throw, and the exception will get
|
// If this new operator fails, it will throw, and the exception will get
|
||||||
// caught one layer up.
|
// caught one layer up.
|
||||||
pData_ = new unsigned char[ allocSize ];
|
pData_ = static_cast< unsigned char * >( ::operator new ( allocSize ) );
|
||||||
#else
|
#else
|
||||||
// malloc can't throw, so its only way to indicate an error is to return
|
// 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.
|
// a NULL pointer, so we have to check for that.
|
||||||
|
@ -137,10 +258,7 @@ bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Chunk::Reset -----------------------------------------------
|
||||||
// FixedAllocator::Chunk::Reset
|
|
||||||
// Clears an already allocated chunk
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
|
void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
|
||||||
{
|
{
|
||||||
|
@ -153,32 +271,25 @@ void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks)
|
||||||
blocksAvailable_ = blocks;
|
blocksAvailable_ = blocks;
|
||||||
|
|
||||||
unsigned char i = 0;
|
unsigned char i = 0;
|
||||||
unsigned char* p = pData_;
|
for ( unsigned char * p = pData_; i != blocks; p += blockSize )
|
||||||
for (; i != blocks; p += blockSize)
|
|
||||||
{
|
{
|
||||||
*p = ++i;
|
*p = ++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Chunk::Release ---------------------------------------------
|
||||||
// FixedAllocator::Chunk::Release
|
|
||||||
// Releases the data managed by a chunk
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FixedAllocator::Chunk::Release()
|
void FixedAllocator::Chunk::Release()
|
||||||
{
|
{
|
||||||
assert( NULL != pData_ );
|
assert( NULL != pData_ );
|
||||||
#ifdef USE_NEW_TO_ALLOCATE
|
#ifdef USE_NEW_TO_ALLOCATE
|
||||||
delete [] pData_;
|
::operator delete ( pData_ );
|
||||||
#else
|
#else
|
||||||
::free( static_cast< void * >( pData_ ) );
|
::free( static_cast< void * >( pData_ ) );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Chunk::Allocate --------------------------------------------
|
||||||
// FixedAllocator::Chunk::Allocate
|
|
||||||
// Allocates a block from a chunk
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
||||||
{
|
{
|
||||||
|
@ -193,10 +304,7 @@ void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
||||||
return pResult;
|
return pResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Chunk::Deallocate ------------------------------------------
|
||||||
// FixedAllocator::Chunk::Deallocate
|
|
||||||
// Dellocates a block from a chunk
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
|
void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
|
||||||
{
|
{
|
||||||
|
@ -215,10 +323,7 @@ void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize)
|
||||||
++blocksAvailable_;
|
++blocksAvailable_;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::FixedAllocator ---------------------------------------------
|
||||||
// FixedAllocator::FixedAllocator
|
|
||||||
// Creates a FixedAllocator object of a fixed block size
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FixedAllocator::FixedAllocator()
|
FixedAllocator::FixedAllocator()
|
||||||
: blockSize_( 0 )
|
: blockSize_( 0 )
|
||||||
|
@ -228,9 +333,7 @@ FixedAllocator::FixedAllocator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::~FixedAllocator --------------------------------------------
|
||||||
// FixedAllocator::~FixedAllocator
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FixedAllocator::~FixedAllocator()
|
FixedAllocator::~FixedAllocator()
|
||||||
{
|
{
|
||||||
|
@ -238,10 +341,7 @@ FixedAllocator::~FixedAllocator()
|
||||||
i->Release();
|
i->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Initialize -------------------------------------------------
|
||||||
// FixedAllocator::Initialize
|
|
||||||
// Initializes the operational constraints for the FixedAllocator
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
|
void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
|
||||||
{
|
{
|
||||||
|
@ -258,10 +358,13 @@ void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixedAllocator::CountEmptyChunks -------------------------------------------
|
// FixedAllocator::CountEmptyChunks -------------------------------------------
|
||||||
/// Returns count of number of empty Chunks inside the chunk list.
|
|
||||||
|
|
||||||
std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
||||||
{
|
{
|
||||||
|
#ifdef DO_EXTRA_LOKI_TESTS
|
||||||
|
// This code is only used for specialized tests of the allocator.
|
||||||
|
// It is #ifdef-ed so that its O(C) complexity does not overwhelm the
|
||||||
|
// functions which call it.
|
||||||
std::size_t count = 0;
|
std::size_t count = 0;
|
||||||
for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
|
for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
|
||||||
{
|
{
|
||||||
|
@ -270,11 +373,12 @@ std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
#else
|
||||||
|
return ( NULL == emptyChunk_ ) ? 0 : 1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixedAllocator::HasBlock ---------------------------------------------------
|
// FixedAllocator::HasBlock ---------------------------------------------------
|
||||||
/// Determines if any Chunk inside FixedAllocator has a block at address p.
|
|
||||||
/// @return True if Chunk owned by this has the block, else false.
|
|
||||||
|
|
||||||
bool FixedAllocator::HasBlock( void * p ) const
|
bool FixedAllocator::HasBlock( void * p ) const
|
||||||
{
|
{
|
||||||
|
@ -289,10 +393,7 @@ bool FixedAllocator::HasBlock( void * p ) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::TrimEmptyChunk ---------------------------------------------
|
||||||
// FixedAllocator::TrimEmptyChunk
|
|
||||||
// Releases the memory used by the empty Chunk.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool FixedAllocator::TrimEmptyChunk( void )
|
bool FixedAllocator::TrimEmptyChunk( void )
|
||||||
{
|
{
|
||||||
|
@ -311,6 +412,7 @@ bool FixedAllocator::TrimEmptyChunk( void )
|
||||||
assert( lastChunk->HasAvailable( numBlocks_ ) );
|
assert( lastChunk->HasAvailable( numBlocks_ ) );
|
||||||
lastChunk->Release();
|
lastChunk->Release();
|
||||||
chunks_.pop_back();
|
chunks_.pop_back();
|
||||||
|
emptyChunk_ = NULL;
|
||||||
|
|
||||||
assert( 0 == CountEmptyChunks() );
|
assert( 0 == CountEmptyChunks() );
|
||||||
if ( chunks_.empty() )
|
if ( chunks_.empty() )
|
||||||
|
@ -331,15 +433,11 @@ bool FixedAllocator::TrimEmptyChunk( void )
|
||||||
assert( allocChunk_->blocksAvailable_ < numBlocks_ );
|
assert( allocChunk_->blocksAvailable_ < numBlocks_ );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyChunk_ = NULL;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::MakeNewChunk -----------------------------------------------
|
||||||
// FixedAllocator::MakeNewChunk
|
|
||||||
// Allocates a new Chunk for a FixedAllocator.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool FixedAllocator::MakeNewChunk( void )
|
bool FixedAllocator::MakeNewChunk( void )
|
||||||
{
|
{
|
||||||
|
@ -371,10 +469,7 @@ bool FixedAllocator::MakeNewChunk( void )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Allocate ---------------------------------------------------
|
||||||
// FixedAllocator::Allocate
|
|
||||||
// Allocates a block of fixed size
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void * FixedAllocator::Allocate( void )
|
void * FixedAllocator::Allocate( void )
|
||||||
{
|
{
|
||||||
|
@ -424,11 +519,7 @@ void * FixedAllocator::Allocate( void )
|
||||||
return place;
|
return place;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::Deallocate -------------------------------------------------
|
||||||
// FixedAllocator::Deallocate
|
|
||||||
// Deallocates a block previously allocated with Allocate
|
|
||||||
// (undefined behavior if called with the wrong pointer)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||||
{
|
{
|
||||||
|
@ -457,12 +548,9 @@ bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::VicinityFind -----------------------------------------------
|
||||||
// FixedAllocator::VicinityFind (internal)
|
|
||||||
// Finds the chunk corresponding to a pointer, using an efficient search
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p ) const
|
||||||
{
|
{
|
||||||
if ( chunks_.empty() ) return NULL;
|
if ( chunks_.empty() ) return NULL;
|
||||||
assert(deallocChunk_);
|
assert(deallocChunk_);
|
||||||
|
@ -470,10 +558,10 @@ FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
||||||
unsigned char * pc = static_cast< unsigned char * >( p );
|
unsigned char * pc = static_cast< unsigned char * >( p );
|
||||||
const std::size_t chunkLength = numBlocks_ * blockSize_;
|
const std::size_t chunkLength = numBlocks_ * blockSize_;
|
||||||
|
|
||||||
Chunk* lo = deallocChunk_;
|
Chunk * lo = deallocChunk_;
|
||||||
Chunk* hi = deallocChunk_ + 1;
|
Chunk * hi = deallocChunk_ + 1;
|
||||||
Chunk* loBound = &chunks_.front();
|
const Chunk * loBound = &chunks_.front();
|
||||||
Chunk* hiBound = &chunks_.back() + 1;
|
const Chunk * hiBound = &chunks_.back() + 1;
|
||||||
|
|
||||||
// Special case: deallocChunk_ is the last in the array
|
// Special case: deallocChunk_ is the last in the array
|
||||||
if (hi == hiBound) hi = NULL;
|
if (hi == hiBound) hi = NULL;
|
||||||
|
@ -505,10 +593,7 @@ FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// FixedAllocator::DoDeallocate -----------------------------------------------
|
||||||
// FixedAllocator::DoDeallocate (internal)
|
|
||||||
// Performs deallocation. Assumes deallocChunk_ points to the correct chunk
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void FixedAllocator::DoDeallocate(void* p)
|
void FixedAllocator::DoDeallocate(void* p)
|
||||||
{
|
{
|
||||||
|
@ -549,10 +634,8 @@ void FixedAllocator::DoDeallocate(void* p)
|
||||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// GetOffset ------------------------------------------------------------------
|
||||||
// GetOffset
|
/// Calculates index into array where a FixedAllocator of numBytes is located.
|
||||||
// Calculates index into array where a FixedAllocator of numBytes is located.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
|
inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
|
||||||
{
|
{
|
||||||
|
@ -560,11 +643,13 @@ inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
|
||||||
return ( numBytes + alignExtra ) / alignment;
|
return ( numBytes + alignExtra ) / alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// DefaultAllocator -----------------------------------------------------------
|
||||||
// DefaultAllocator
|
/** Calls the default allocator when SmallObjAllocator decides not to handle a
|
||||||
// Call to default allocator when SmallObjAllocator decides not to handle request.
|
request. SmallObjAllocator calls this if the number of bytes is bigger than
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
the size which can be handled by any FixedAllocator.
|
||||||
|
@param doThrow True if this function should throw an exception, or false if it
|
||||||
|
should indicate failure by returning a NULL pointer.
|
||||||
|
*/
|
||||||
void * DefaultAllocator( std::size_t numBytes, bool doThrow )
|
void * DefaultAllocator( std::size_t numBytes, bool doThrow )
|
||||||
{
|
{
|
||||||
#ifdef USE_NEW_TO_ALLOCATE
|
#ifdef USE_NEW_TO_ALLOCATE
|
||||||
|
@ -578,11 +663,13 @@ void * DefaultAllocator( std::size_t numBytes, bool doThrow )
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// DefaultDeallocator ---------------------------------------------------------
|
||||||
// DefaultDeallocator
|
/** Calls default deallocator when SmallObjAllocator decides not to handle a
|
||||||
// Call to default deallocator when SmallObjAllocator decides not to handle request.
|
request. The default deallocator could be the global delete operator or the
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
free function. The free function is the preferred default deallocator since
|
||||||
|
it matches malloc which is the preferred default allocator. SmallObjAllocator
|
||||||
|
will call this if an address was not found among any of its own blocks.
|
||||||
|
*/
|
||||||
void DefaultDeallocator( void * p )
|
void DefaultDeallocator( void * p )
|
||||||
{
|
{
|
||||||
#ifdef USE_NEW_TO_ALLOCATE
|
#ifdef USE_NEW_TO_ALLOCATE
|
||||||
|
@ -592,11 +679,7 @@ void DefaultDeallocator( void * p )
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::SmallObjAllocator ---------------------------------------
|
||||||
// SmallObjAllocator::SmallObjAllocator
|
|
||||||
// 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 pageSize,
|
SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,
|
||||||
std::size_t maxObjectSize, std::size_t objectAlignSize ) :
|
std::size_t maxObjectSize, std::size_t objectAlignSize ) :
|
||||||
|
@ -611,21 +694,14 @@ SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,
|
||||||
pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize );
|
pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::~SmallObjAllocator --------------------------------------
|
||||||
// SmallObjAllocator::~SmallObjAllocator
|
|
||||||
// Deletes all memory consumed by SmallObjAllocator.
|
|
||||||
// This deletes all the FixedAllocator's in the pool.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
SmallObjAllocator::~SmallObjAllocator( void )
|
SmallObjAllocator::~SmallObjAllocator( void )
|
||||||
{
|
{
|
||||||
delete [] pool_;
|
delete [] pool_;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::TrimExcessMemory ----------------------------------------
|
||||||
// SmallObjAllocator::TrimExcessMemory
|
|
||||||
// Trims excess memory within all FixedAllocators.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool SmallObjAllocator::TrimExcessMemory( void )
|
bool SmallObjAllocator::TrimExcessMemory( void )
|
||||||
{
|
{
|
||||||
|
@ -639,14 +715,7 @@ bool SmallObjAllocator::TrimExcessMemory( void )
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::Allocate ------------------------------------------------
|
||||||
// SmallObjAllocator::Allocate
|
|
||||||
// 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, bool doThrow )
|
void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
||||||
{
|
{
|
||||||
|
@ -680,12 +749,7 @@ void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
||||||
return place;
|
return place;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::Deallocate ----------------------------------------------
|
||||||
// SmallObjAllocator::Deallocate
|
|
||||||
// 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 )
|
||||||
{
|
{
|
||||||
|
@ -707,11 +771,7 @@ void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
||||||
assert( found );
|
assert( found );
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// SmallObjAllocator::Deallocate ----------------------------------------------
|
||||||
// SmallObjAllocator::Deallocate
|
|
||||||
// Handles request to deallocate 1 object that was allocated by non-throwing
|
|
||||||
// new operator. This will act in linear-time. It will never throw.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void SmallObjAllocator::Deallocate( void * p )
|
void SmallObjAllocator::Deallocate( void * p )
|
||||||
{
|
{
|
||||||
|
@ -751,6 +811,9 @@ void SmallObjAllocator::Deallocate( void * p )
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// $Log$
|
// $Log$
|
||||||
|
// Revision 1.6 2005/09/26 21:38:54 rich_sposato
|
||||||
|
// Changed include path to be direct instead of relying upon project settings.
|
||||||
|
//
|
||||||
// Revision 1.5 2005/09/24 15:48:29 syntheticpp
|
// Revision 1.5 2005/09/24 15:48:29 syntheticpp
|
||||||
// include as loki/
|
// include as loki/
|
||||||
//
|
//
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue