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 SmallObjAllocator
|
||||
// Manages pool of fixed-size allocators.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** @class SmallObjAllocator Manages pool of fixed-size allocators.
|
||||
Designed to be a non-templated base class of AllocatorSingleton so that
|
||||
implementation details can be safely hidden in the source code file.
|
||||
*/
|
||||
class SmallObjAllocator
|
||||
{
|
||||
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,
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
/** 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 );
|
||||
|
||||
/** 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 );
|
||||
|
||||
/// Returns max # of bytes which this can allocate.
|
||||
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
||||
|
||||
/// Returns # of bytes between allocation boundaries.
|
||||
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 );
|
||||
|
||||
private:
|
||||
/// Default-constructor is not implemented.
|
||||
SmallObjAllocator( void );
|
||||
/// Copy-constructor is not implemented.
|
||||
SmallObjAllocator( const SmallObjAllocator & );
|
||||
/// Copy-assignment operator is not implemented.
|
||||
SmallObjAllocator & operator = ( const SmallObjAllocator & );
|
||||
|
||||
/// Pointer to array of fixed-size allocators.
|
||||
Loki::FixedAllocator * pool_;
|
||||
|
||||
/// Largest object size supported by allocators.
|
||||
std::size_t maxSmallObjectSize_;
|
||||
|
||||
/// Size of alignment boundaries.
|
||||
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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** @class AllocatorSingleton This template class is derived from
|
||||
SmallObjAllocator in order to pass template arguments into it, and still
|
||||
have a default constructor for the singleton. Each instance is a unique
|
||||
combination of all the template parameters, and hence is singleton only
|
||||
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 <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||
|
@ -110,16 +179,28 @@ namespace Loki
|
|||
typedef Loki::SingletonHolder< MyAllocator, Loki::CreateStatic,
|
||||
LifetimePolicy, ThreadingModel > MyAllocatorSingleton;
|
||||
|
||||
/// Returns reference to the singleton.
|
||||
inline static AllocatorSingleton & Instance( void )
|
||||
{
|
||||
return MyAllocatorSingleton::Instance();
|
||||
}
|
||||
|
||||
/// The default constructor is not meant to be called directly.
|
||||
inline AllocatorSingleton() :
|
||||
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
||||
{}
|
||||
|
||||
/// The destructor is not meant to be called directly.
|
||||
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 );
|
||||
|
||||
private:
|
||||
|
@ -145,11 +226,12 @@ namespace Loki
|
|||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallObjectBase
|
||||
// Base class for small object allocation classes.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** @class SmallObjectBase Base class for small object allocation classes.
|
||||
The shared implementation of the new and delete operators are here instead
|
||||
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 <class> class ThreadingModel,
|
||||
|
@ -176,7 +258,7 @@ namespace Loki
|
|||
|
||||
public:
|
||||
|
||||
/// Throwing single-object new.
|
||||
/// Throwing single-object new throws bad_alloc when allocation fails.
|
||||
#ifdef _MSC_VER
|
||||
/// @note MSVC complains about non-empty exception specification lists.
|
||||
static void * operator new ( std::size_t size )
|
||||
|
@ -189,7 +271,7 @@ namespace Loki
|
|||
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 ()
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
|
@ -197,7 +279,7 @@ namespace Loki
|
|||
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 )
|
||||
{
|
||||
return ::operator new( size, place );
|
||||
|
@ -211,7 +293,9 @@ namespace Loki
|
|||
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()
|
||||
{
|
||||
typename MyThreadingModel::Lock lock;
|
||||
|
@ -219,7 +303,7 @@ namespace Loki
|
|||
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 )
|
||||
{
|
||||
::operator delete ( p, place );
|
||||
|
@ -235,12 +319,13 @@ namespace Loki
|
|||
}; // end class SmallObjectBase
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallObject
|
||||
// Base class for polymorphic small objects, offers fast allocations &
|
||||
// deallocations. Destructor is virtual and public.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** @class SmallObject Base class for polymorphic small objects, offers fast
|
||||
allocations & deallocations. Destructor is virtual and public. Default
|
||||
constructor is trivial. Copy-constructor and copy-assignment operator are
|
||||
not implemented since polymorphic classes almost always disable those
|
||||
operations. Class has no data members so compilers can use
|
||||
Empty-Base-Optimization.
|
||||
*/
|
||||
template
|
||||
<
|
||||
template <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||
|
@ -266,12 +351,14 @@ namespace Loki
|
|||
}; // end class SmallObject
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class SmallValueObject
|
||||
// Base class for small objects with value semantics - offers fast allocations &
|
||||
// deallocations. Destructor is non-virtual, inline, and protected.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** @class SmallValueObject Base class for small objects with value-type
|
||||
semantics - offers fast allocations & deallocations. Destructor is
|
||||
non-virtual, inline, and protected to prevent unintentional destruction
|
||||
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 <class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||
|
@ -298,6 +385,9 @@ namespace Loki
|
|||
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
||||
//
|
||||
// $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
|
||||
// move macros into LOKI_ namespace
|
||||
//
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
// Last update: June 20, 2001
|
||||
|
||||
#include "loki/Singleton.h"
|
||||
#include "../include/loki/Singleton.h"
|
||||
|
||||
using namespace Loki::Private;
|
||||
|
||||
|
|
353
src/SmallObj.cpp
353
src/SmallObj.cpp
|
@ -16,7 +16,7 @@
|
|||
// $Header$
|
||||
|
||||
|
||||
#include "loki/SmallObj.h"
|
||||
#include "../include/loki/SmallObj.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
@ -30,21 +30,95 @@ using namespace Loki;
|
|||
namespace Loki
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class FixedAllocator
|
||||
// Offers services for allocating fixed-sized objects
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/** @class FixedAllocator
|
||||
Offers services for allocating fixed-sized objects. It has a container
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
/** 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 );
|
||||
void* Allocate(std::size_t blockSize);
|
||||
void Deallocate(void* p, std::size_t blockSize);
|
||||
void Reset(std::size_t blockSize, unsigned char blocks);
|
||||
|
||||
/** Allocate a block within the Chunk. Complexity is always O(1),
|
||||
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();
|
||||
|
||||
/// Returns true if block at address P is inside this Chunk.
|
||||
inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const
|
||||
{ return ( pData_ <= p ) && ( p < pData_ + chunkLength ); }
|
||||
|
||||
|
@ -54,65 +128,112 @@ namespace Loki
|
|||
inline bool IsFilled( void ) const
|
||||
{ return ( 0 == blocksAvailable_ ); }
|
||||
|
||||
unsigned char* pData_;
|
||||
unsigned char
|
||||
firstAvailableBlock_,
|
||||
blocksAvailable_;
|
||||
/// Pointer to array of allocated blocks.
|
||||
unsigned char * pData_;
|
||||
/// Index of first empty block.
|
||||
unsigned char firstAvailableBlock_;
|
||||
/// Count of empty blocks.
|
||||
unsigned char blocksAvailable_;
|
||||
};
|
||||
|
||||
// Internal functions
|
||||
void DoDeallocate(void* p);
|
||||
/** Deallocates the block at address p, and then handles the internal
|
||||
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 );
|
||||
|
||||
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.
|
||||
FixedAllocator(const FixedAllocator&);
|
||||
/// Not implemented.
|
||||
FixedAllocator& operator=(const FixedAllocator&);
|
||||
|
||||
// Data
|
||||
std::size_t blockSize_;
|
||||
unsigned char numBlocks_;
|
||||
typedef std::vector<Chunk> Chunks;
|
||||
/// Type of container used to hold Chunks.
|
||||
typedef std::vector< Chunk > Chunks;
|
||||
/// Iterator through container of Chunks.
|
||||
typedef Chunks::iterator ChunkIter;
|
||||
/// Iterator through const container of Chunks.
|
||||
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_;
|
||||
Chunk* allocChunk_;
|
||||
Chunk* deallocChunk_;
|
||||
/// Pointer to Chunk used for last or next allocation.
|
||||
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_;
|
||||
|
||||
public:
|
||||
// Create a FixedAllocator able to manage blocks of 'blockSize' size
|
||||
/// Create a FixedAllocator which manages blocks of 'blockSize' size.
|
||||
FixedAllocator();
|
||||
|
||||
/// Destroy the FixedAllocator and release all its Chunks.
|
||||
~FixedAllocator();
|
||||
|
||||
/// Initializes a FixedAllocator by calculating # of blocks per Chunk.
|
||||
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 );
|
||||
|
||||
// Deallocate a memory block previously allocated with Allocate()
|
||||
// (if that's not the case, the behavior is undefined)
|
||||
/** Deallocate a memory block previously allocated with Allocate. If
|
||||
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 );
|
||||
// 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 );
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Init
|
||||
// Initializes a chunk object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Init ------------------------------------------------
|
||||
|
||||
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
|
||||
// If this new operator fails, it will throw, and the exception will get
|
||||
// caught one layer up.
|
||||
pData_ = new unsigned char[ allocSize ];
|
||||
pData_ = static_cast< unsigned char * >( ::operator new ( 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.
|
||||
|
@ -137,10 +258,7 @@ bool FixedAllocator::Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Reset
|
||||
// Clears an already allocated chunk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Reset -----------------------------------------------
|
||||
|
||||
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;
|
||||
|
||||
unsigned char i = 0;
|
||||
unsigned char* p = pData_;
|
||||
for (; i != blocks; p += blockSize)
|
||||
for ( unsigned char * p = pData_; i != blocks; p += blockSize )
|
||||
{
|
||||
*p = ++i;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Release
|
||||
// Releases the data managed by a chunk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Release ---------------------------------------------
|
||||
|
||||
void FixedAllocator::Chunk::Release()
|
||||
{
|
||||
assert( NULL != pData_ );
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
delete [] pData_;
|
||||
::operator delete ( pData_ );
|
||||
#else
|
||||
::free( static_cast< void * >( pData_ ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Allocate
|
||||
// Allocates a block from a chunk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Allocate --------------------------------------------
|
||||
|
||||
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
||||
{
|
||||
|
@ -193,10 +304,7 @@ void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
|
|||
return pResult;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Deallocate
|
||||
// Dellocates a block from a chunk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Chunk::Deallocate ------------------------------------------
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::FixedAllocator
|
||||
// Creates a FixedAllocator object of a fixed block size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::FixedAllocator ---------------------------------------------
|
||||
|
||||
FixedAllocator::FixedAllocator()
|
||||
: blockSize_( 0 )
|
||||
|
@ -228,9 +333,7 @@ FixedAllocator::FixedAllocator()
|
|||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::~FixedAllocator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::~FixedAllocator --------------------------------------------
|
||||
|
||||
FixedAllocator::~FixedAllocator()
|
||||
{
|
||||
|
@ -238,10 +341,7 @@ FixedAllocator::~FixedAllocator()
|
|||
i->Release();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Initialize
|
||||
// Initializes the operational constraints for the FixedAllocator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Initialize -------------------------------------------------
|
||||
|
||||
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 -------------------------------------------
|
||||
/// Returns count of number of empty Chunks inside the chunk list.
|
||||
|
||||
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;
|
||||
for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
|
||||
{
|
||||
|
@ -270,11 +373,12 @@ std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
|||
++count;
|
||||
}
|
||||
return count;
|
||||
#else
|
||||
return ( NULL == emptyChunk_ ) ? 0 : 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
|
@ -289,10 +393,7 @@ bool FixedAllocator::HasBlock( void * p ) const
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::TrimEmptyChunk
|
||||
// Releases the memory used by the empty Chunk.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::TrimEmptyChunk ---------------------------------------------
|
||||
|
||||
bool FixedAllocator::TrimEmptyChunk( void )
|
||||
{
|
||||
|
@ -311,6 +412,7 @@ bool FixedAllocator::TrimEmptyChunk( void )
|
|||
assert( lastChunk->HasAvailable( numBlocks_ ) );
|
||||
lastChunk->Release();
|
||||
chunks_.pop_back();
|
||||
emptyChunk_ = NULL;
|
||||
|
||||
assert( 0 == CountEmptyChunks() );
|
||||
if ( chunks_.empty() )
|
||||
|
@ -331,15 +433,11 @@ bool FixedAllocator::TrimEmptyChunk( void )
|
|||
assert( allocChunk_->blocksAvailable_ < numBlocks_ );
|
||||
}
|
||||
}
|
||||
emptyChunk_ = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::MakeNewChunk
|
||||
// Allocates a new Chunk for a FixedAllocator.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::MakeNewChunk -----------------------------------------------
|
||||
|
||||
bool FixedAllocator::MakeNewChunk( void )
|
||||
{
|
||||
|
@ -371,10 +469,7 @@ bool FixedAllocator::MakeNewChunk( void )
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Allocate
|
||||
// Allocates a block of fixed size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Allocate ---------------------------------------------------
|
||||
|
||||
void * FixedAllocator::Allocate( void )
|
||||
{
|
||||
|
@ -424,11 +519,7 @@ void * FixedAllocator::Allocate( void )
|
|||
return place;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Deallocate
|
||||
// Deallocates a block previously allocated with Allocate
|
||||
// (undefined behavior if called with the wrong pointer)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::Deallocate -------------------------------------------------
|
||||
|
||||
bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||
{
|
||||
|
@ -457,12 +548,9 @@ bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::VicinityFind (internal)
|
||||
// Finds the chunk corresponding to a pointer, using an efficient search
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::VicinityFind -----------------------------------------------
|
||||
|
||||
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
||||
FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p ) const
|
||||
{
|
||||
if ( chunks_.empty() ) return NULL;
|
||||
assert(deallocChunk_);
|
||||
|
@ -470,11 +558,11 @@ FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
|||
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;
|
||||
|
||||
Chunk * lo = deallocChunk_;
|
||||
Chunk * hi = deallocChunk_ + 1;
|
||||
const Chunk * loBound = &chunks_.front();
|
||||
const Chunk * hiBound = &chunks_.back() + 1;
|
||||
|
||||
// Special case: deallocChunk_ is the last in the array
|
||||
if (hi == hiBound) hi = NULL;
|
||||
|
||||
|
@ -505,10 +593,7 @@ FixedAllocator::Chunk * FixedAllocator::VicinityFind( void * p )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::DoDeallocate (internal)
|
||||
// Performs deallocation. Assumes deallocChunk_ points to the correct chunk
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FixedAllocator::DoDeallocate -----------------------------------------------
|
||||
|
||||
void FixedAllocator::DoDeallocate(void* p)
|
||||
{
|
||||
|
@ -549,10 +634,8 @@ void FixedAllocator::DoDeallocate(void* p)
|
|||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetOffset
|
||||
// Calculates index into array where a FixedAllocator of numBytes is located.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 )
|
||||
{
|
||||
|
@ -560,11 +643,13 @@ inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment )
|
|||
return ( numBytes + alignExtra ) / alignment;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DefaultAllocator
|
||||
// Call to default allocator when SmallObjAllocator decides not to handle request.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DefaultAllocator -----------------------------------------------------------
|
||||
/** Calls the default allocator when SmallObjAllocator decides not to handle a
|
||||
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 )
|
||||
{
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
|
@ -578,11 +663,13 @@ void * DefaultAllocator( std::size_t numBytes, bool doThrow )
|
|||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DefaultDeallocator
|
||||
// Call to default deallocator when SmallObjAllocator decides not to handle request.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DefaultDeallocator ---------------------------------------------------------
|
||||
/** Calls default deallocator when SmallObjAllocator decides not to handle a
|
||||
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 )
|
||||
{
|
||||
#ifdef USE_NEW_TO_ALLOCATE
|
||||
|
@ -592,11 +679,7 @@ void DefaultDeallocator( void * p )
|
|||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::SmallObjAllocator
|
||||
// Creates a SmallObjAllocator, and all the FixedAllocators within it. Each
|
||||
// FixedAllocator is then initialized to use the correct Chunk size.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::SmallObjAllocator ---------------------------------------
|
||||
|
||||
SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,
|
||||
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 );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::~SmallObjAllocator
|
||||
// Deletes all memory consumed by SmallObjAllocator.
|
||||
// This deletes all the FixedAllocator's in the pool.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::~SmallObjAllocator --------------------------------------
|
||||
|
||||
SmallObjAllocator::~SmallObjAllocator( void )
|
||||
{
|
||||
delete [] pool_;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::TrimExcessMemory
|
||||
// Trims excess memory within all FixedAllocators.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::TrimExcessMemory ----------------------------------------
|
||||
|
||||
bool SmallObjAllocator::TrimExcessMemory( void )
|
||||
{
|
||||
|
@ -639,14 +715,7 @@ bool SmallObjAllocator::TrimExcessMemory( void )
|
|||
return found;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::Allocate ------------------------------------------------
|
||||
|
||||
void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
||||
{
|
||||
|
@ -680,12 +749,7 @@ void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
|||
return place;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::Deallocate ----------------------------------------------
|
||||
|
||||
void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
||||
{
|
||||
|
@ -707,11 +771,7 @@ void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
|||
assert( found );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SmallObjAllocator::Deallocate ----------------------------------------------
|
||||
|
||||
void SmallObjAllocator::Deallocate( void * p )
|
||||
{
|
||||
|
@ -751,6 +811,9 @@ void SmallObjAllocator::Deallocate( void * p )
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// $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
|
||||
// include as loki/
|
||||
//
|
||||
|
|
Loading…
Add table
Reference in a new issue