1
0
Fork 0
This commit is contained in:
Duncan Ogilvie 2019-08-19 15:38:42 +02:00
parent f57d69f91e
commit 208cee69eb
22 changed files with 2379 additions and 0 deletions

179
src/dbg/Allocator.h Normal file
View File

@ -0,0 +1,179 @@
#ifndef AllocatorH
#define AllocatorH
#include <memory>
namespace Moya
{
template <class T, std::size_t growSize = 1024>
class MemoryPool
{
struct Block
{
Block* next;
};
class Buffer
{
static const std::size_t blockSize = sizeof(T) > sizeof(Block) ? sizeof(T) : sizeof(Block);
uint8_t data[blockSize * growSize];
public:
Buffer* const next;
Buffer(Buffer* next) :
next(next)
{
}
T* getBlock(std::size_t index)
{
return reinterpret_cast<T*>(&data[blockSize * index]);
}
};
Block* firstFreeBlock = nullptr;
Buffer* firstBuffer = nullptr;
std::size_t bufferedBlocks = growSize;
public:
MemoryPool() = default;
MemoryPool(MemoryPool && memoryPool) = delete;
MemoryPool(const MemoryPool & memoryPool) = delete;
MemoryPool operator =(MemoryPool && memoryPool) = delete;
MemoryPool operator =(const MemoryPool & memoryPool) = delete;
~MemoryPool()
{
while(firstBuffer)
{
Buffer* buffer = firstBuffer;
firstBuffer = buffer->next;
delete buffer;
}
}
T* allocate()
{
if(firstFreeBlock)
{
Block* block = firstFreeBlock;
firstFreeBlock = block->next;
return reinterpret_cast<T*>(block);
}
if(bufferedBlocks >= growSize)
{
firstBuffer = new Buffer(firstBuffer);
bufferedBlocks = 0;
}
return firstBuffer->getBlock(bufferedBlocks++);
}
void deallocate(T* pointer)
{
Block* block = reinterpret_cast<Block*>(pointer);
block->next = firstFreeBlock;
firstFreeBlock = block;
}
};
template <class T, std::size_t growSize = 1024>
class Allocator : private MemoryPool<T, growSize>
{
#ifdef _WIN32
Allocator* copyAllocator;
std::allocator<T>* rebindAllocator = nullptr;
#endif
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T & reference;
typedef const T & const_reference;
typedef T value_type;
template <class U>
struct rebind
{
typedef Allocator<U, growSize> other;
};
#ifdef _WIN32
Allocator() = default;
Allocator(Allocator & allocator) :
copyAllocator(&allocator)
{
}
template <class U>
Allocator(const Allocator<U, growSize> & other)
{
if(!std::is_same<T, U>::value)
rebindAllocator = new std::allocator<T>();
}
~Allocator()
{
delete rebindAllocator;
}
#endif
pointer allocate(size_type n, const void* hint = 0)
{
#ifdef _WIN32
if(copyAllocator)
return copyAllocator->allocate(n, hint);
if(rebindAllocator)
return rebindAllocator->allocate(n, hint);
#endif
if(n != 1 || hint)
throw std::bad_alloc();
return MemoryPool<T, growSize>::allocate();
}
void deallocate(pointer p, size_type n)
{
#ifdef _WIN32
if(copyAllocator)
{
copyAllocator->deallocate(p, n);
return;
}
if(rebindAllocator)
{
rebindAllocator->deallocate(p, n);
return;
}
#endif
MemoryPool<T, growSize>::deallocate(p);
}
void construct(pointer p, const_reference val)
{
new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
};
}
#endif

886
src/dbg/FSBAllocator.hh Normal file
View File

@ -0,0 +1,886 @@
/*===========================================================================
This library is released under the MIT license. See FSBAllocator.html
for further information and documentation.
Copyright (c) 2008-2011 Juha Nieminen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
=============================================================================*/
#ifndef INCLUDE_FSBALLOCATOR_HH
#define INCLUDE_FSBALLOCATOR_HH
#include <new>
#include <cassert>
#include <vector>
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <boost/thread.hpp>
typedef boost::mutex FSBAllocator_Mutex;
#endif
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <omp.h>
class FSBAllocator_Mutex
{
omp_lock_t mutex;
public:
FSBAllocator_Mutex() { omp_init_lock(&mutex); }
~FSBAllocator_Mutex() { omp_destroy_lock(&mutex); }
void lock() { omp_set_lock(&mutex); }
void unlock() { omp_unset_lock(&mutex); }
};
#endif
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#include <pthread.h>
class FSBAllocator_Mutex
{
pthread_mutex_t mutex;
public:
FSBAllocator_Mutex() { pthread_mutex_init(&mutex, NULL); }
~FSBAllocator_Mutex() { pthread_mutex_destroy(&mutex); }
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
};
#endif
#if defined(FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC) || defined(FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED)
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OPENMP
#undef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_PTHREAD
#define FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED
#include <sched.h>
#endif
class FSBAllocator_Mutex
{
volatile int lockFlag;
public:
FSBAllocator_Mutex(): lockFlag(0) {}
void lock()
{
while(!__sync_bool_compare_and_swap(&lockFlag, 0, 1))
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_GCC_WITH_SCHED
sched_yield();
#endif
}
}
void unlock() { lockFlag = 0; }
};
#endif
template<unsigned ElemSize>
class FSBAllocator_ElemAllocator
{
typedef std::size_t Data_t;
static const Data_t BlockElements = 512;
static const Data_t DSize = sizeof(Data_t);
static const Data_t ElemSizeInDSize = (ElemSize + (DSize-1)) / DSize;
static const Data_t UnitSizeInDSize = ElemSizeInDSize + 1;
static const Data_t BlockSize = BlockElements*UnitSizeInDSize;
class MemBlock
{
Data_t* block;
Data_t firstFreeUnitIndex, allocatedElementsAmount, endIndex;
public:
MemBlock():
block(0),
firstFreeUnitIndex(Data_t(-1)),
allocatedElementsAmount(0)
{}
bool isFull() const
{
return allocatedElementsAmount == BlockElements;
}
void clear()
{
delete[] block;
block = 0;
firstFreeUnitIndex = Data_t(-1);
}
void* allocate(Data_t vectorIndex)
{
if(firstFreeUnitIndex == Data_t(-1))
{
if(!block)
{
block = new Data_t[BlockSize];
if(!block) return 0;
endIndex = 0;
}
Data_t* retval = block + endIndex;
endIndex += UnitSizeInDSize;
retval[ElemSizeInDSize] = vectorIndex;
++allocatedElementsAmount;
return retval;
}
else
{
Data_t* retval = block + firstFreeUnitIndex;
firstFreeUnitIndex = *retval;
++allocatedElementsAmount;
return retval;
}
}
void deallocate(Data_t* ptr)
{
*ptr = firstFreeUnitIndex;
firstFreeUnitIndex = ptr - block;
if(--allocatedElementsAmount == 0)
clear();
}
};
struct BlocksVector
{
std::vector<MemBlock> data;
BlocksVector() { data.reserve(1024); }
~BlocksVector()
{
for(std::size_t i = 0; i < data.size(); ++i)
data[i].clear();
}
};
static BlocksVector blocksVector;
static std::vector<Data_t> blocksWithFree;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
static FSBAllocator_Mutex mutex;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
struct Lock: boost::mutex::scoped_lock
{
Lock(): boost::mutex::scoped_lock(mutex) {}
};
#else
struct Lock
{
Lock() { mutex.lock(); }
~Lock() { mutex.unlock(); }
};
#endif
#endif
public:
static void* allocate()
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
if(blocksWithFree.empty())
{
blocksWithFree.push_back(blocksVector.data.size());
blocksVector.data.push_back(MemBlock());
}
const Data_t index = blocksWithFree.back();
MemBlock& block = blocksVector.data[index];
void* retval = block.allocate(index);
if(block.isFull())
blocksWithFree.pop_back();
return retval;
}
static void deallocate(void* ptr)
{
if(!ptr) return;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
Data_t* unitPtr = (Data_t*)ptr;
const Data_t blockIndex = unitPtr[ElemSizeInDSize];
MemBlock& block = blocksVector.data[blockIndex];
if(block.isFull())
blocksWithFree.push_back(blockIndex);
block.deallocate(unitPtr);
}
};
template<unsigned ElemSize>
typename FSBAllocator_ElemAllocator<ElemSize>::BlocksVector
FSBAllocator_ElemAllocator<ElemSize>::blocksVector;
template<unsigned ElemSize>
std::vector<typename FSBAllocator_ElemAllocator<ElemSize>::Data_t>
FSBAllocator_ElemAllocator<ElemSize>::blocksWithFree;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
template<unsigned ElemSize>
FSBAllocator_Mutex FSBAllocator_ElemAllocator<ElemSize>::mutex;
#endif
template<unsigned ElemSize>
class FSBAllocator2_ElemAllocator
{
static const std::size_t BlockElements = 1024;
static const std::size_t DSize = sizeof(std::size_t);
static const std::size_t ElemSizeInDSize = (ElemSize + (DSize-1)) / DSize;
static const std::size_t BlockSize = BlockElements*ElemSizeInDSize;
struct Blocks
{
std::vector<std::size_t*> ptrs;
Blocks()
{
ptrs.reserve(256);
ptrs.push_back(new std::size_t[BlockSize]);
}
~Blocks()
{
for(std::size_t i = 0; i < ptrs.size(); ++i)
delete[] ptrs[i];
}
};
static Blocks blocks;
static std::size_t headIndex;
static std::size_t* freeList;
static std::size_t allocatedElementsAmount;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
static FSBAllocator_Mutex mutex;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_BOOST
struct Lock: boost::mutex::scoped_lock
{
Lock(): boost::mutex::scoped_lock(mutex) {}
};
#else
struct Lock
{
Lock() { mutex.lock(); }
~Lock() { mutex.unlock(); }
};
#endif
#endif
static void freeAll()
{
for(std::size_t i = 1; i < blocks.ptrs.size(); ++i)
delete[] blocks.ptrs[i];
blocks.ptrs.resize(1);
headIndex = 0;
freeList = 0;
}
public:
static void* allocate()
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
++allocatedElementsAmount;
if(freeList)
{
std::size_t* retval = freeList;
freeList = reinterpret_cast<std::size_t*>(*freeList);
return retval;
}
if(headIndex == BlockSize)
{
blocks.ptrs.push_back(new std::size_t[BlockSize]);
headIndex = 0;
}
std::size_t* retval = &(blocks.ptrs.back()[headIndex]);
headIndex += ElemSizeInDSize;
return retval;
}
static void deallocate(void* ptr)
{
if(ptr)
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
std::size_t* sPtr = (std::size_t*)ptr;
*sPtr = reinterpret_cast<std::size_t>(freeList);
freeList = sPtr;
if(--allocatedElementsAmount == 0)
freeAll();
}
}
static void cleanSweep(std::size_t unusedValue = std::size_t(-1))
{
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
Lock lock;
#endif
while(freeList)
{
std::size_t* current = freeList;
freeList = reinterpret_cast<std::size_t*>(*freeList);
*current = unusedValue;
}
for(std::size_t i = headIndex; i < BlockSize; i += ElemSizeInDSize)
blocks.ptrs.back()[i] = unusedValue;
for(std::size_t blockInd = 1; blockInd < blocks.ptrs.size();)
{
std::size_t* block = blocks.ptrs[blockInd];
std::size_t freeAmount = 0;
for(std::size_t i = 0; i < BlockSize; i += ElemSizeInDSize)
if(block[i] == unusedValue)
++freeAmount;
if(freeAmount == BlockElements)
{
delete[] block;
blocks.ptrs[blockInd] = blocks.ptrs.back();
blocks.ptrs.pop_back();
}
else ++blockInd;
}
const std::size_t* lastBlock = blocks.ptrs.back();
for(headIndex = BlockSize; headIndex > 0; headIndex -= ElemSizeInDSize)
if(lastBlock[headIndex-ElemSizeInDSize] != unusedValue)
break;
const std::size_t lastBlockIndex = blocks.ptrs.size() - 1;
for(std::size_t blockInd = 0; blockInd <= lastBlockIndex; ++blockInd)
{
std::size_t* block = blocks.ptrs[blockInd];
for(std::size_t i = 0; i < BlockSize; i += ElemSizeInDSize)
{
if(blockInd == lastBlockIndex && i == headIndex)
break;
if(block[i] == unusedValue)
deallocate(block + i);
}
}
}
};
template<unsigned ElemSize>
typename FSBAllocator2_ElemAllocator<ElemSize>::Blocks
FSBAllocator2_ElemAllocator<ElemSize>::blocks;
template<unsigned ElemSize>
std::size_t FSBAllocator2_ElemAllocator<ElemSize>::headIndex = 0;
template<unsigned ElemSize>
std::size_t* FSBAllocator2_ElemAllocator<ElemSize>::freeList = 0;
template<unsigned ElemSize>
std::size_t FSBAllocator2_ElemAllocator<ElemSize>::allocatedElementsAmount = 0;
#ifdef FSBALLOCATOR_USE_THREAD_SAFE_LOCKING_OBJECT
template<unsigned ElemSize>
FSBAllocator_Mutex FSBAllocator2_ElemAllocator<ElemSize>::mutex;
#endif
template<typename Ty>
class FSBAllocator
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef Ty *pointer;
typedef const Ty *const_pointer;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty value_type;
pointer address(reference val) const { return &val; }
const_pointer address(const_reference val) const { return &val; }
template<class Other>
struct rebind
{
typedef FSBAllocator<Other> other;
};
FSBAllocator() throw() {}
template<class Other>
FSBAllocator(const FSBAllocator<Other>&) throw() {}
template<class Other>
FSBAllocator& operator=(const FSBAllocator<Other>&) { return *this; }
pointer allocate(size_type count, const void* = 0)
{
assert(count == 1);
return static_cast<pointer>
(FSBAllocator_ElemAllocator<sizeof(Ty)>::allocate());
}
void deallocate(pointer ptr, size_type)
{
FSBAllocator_ElemAllocator<sizeof(Ty)>::deallocate(ptr);
}
void construct(pointer ptr, const Ty& val)
{
new ((void *)ptr) Ty(val);
}
void destroy(pointer ptr)
{
ptr->Ty::~Ty();
}
size_type max_size() const throw() { return 1; }
};
template<typename Ty>
class FSBAllocator2
{
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef Ty *pointer;
typedef const Ty *const_pointer;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty value_type;
pointer address(reference val) const { return &val; }
const_pointer address(const_reference val) const { return &val; }
template<class Other>
struct rebind
{
typedef FSBAllocator2<Other> other;
};
FSBAllocator2() throw() {}
template<class Other>
FSBAllocator2(const FSBAllocator2<Other>&) throw() {}
template<class Other>
FSBAllocator2& operator=(const FSBAllocator2<Other>&) { return *this; }
pointer allocate(size_type count, const void* = 0)
{
assert(count == 1);
return static_cast<pointer>
(FSBAllocator2_ElemAllocator<sizeof(Ty)>::allocate());
}
void deallocate(pointer ptr, size_type)
{
FSBAllocator2_ElemAllocator<sizeof(Ty)>::deallocate(ptr);
}
void construct(pointer ptr, const Ty& val)
{
new ((void *)ptr) Ty(val);
}
void destroy(pointer ptr)
{
ptr->Ty::~Ty();
}
size_type max_size() const throw() { return 1; }
void cleanSweep(std::size_t unusedValue = std::size_t(-1))
{
FSBAllocator2_ElemAllocator<sizeof(Ty)>::cleanSweep(unusedValue);
}
};
typedef FSBAllocator2<std::size_t> FSBRefCountAllocator;
// <MyCustomAlloc>
extern long g_nCnt; // total # blocks allocated
extern long g_nTot; // total allocated
template <class T>
class MyCustomAlloc
/*
A custom allocator: given a pool of memory to start, just dole out consecutive memory blocks.
this could be faster than a general purpose allocator.
E.G. it could take advantage of constant sized requests (as in a RedBlack tree)
*/
{
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
MyCustomAlloc(byte *pool, int nPoolSize)
{
Init();
m_pool = pool;
m_nPoolSize = nPoolSize;
}
MyCustomAlloc(int n)
{
Init();
}
MyCustomAlloc()
{
Init();
}
void Init()
{
m_pool = 0;
m_nPoolSize = 0;
g_nCnt = 0;
g_nTot = 0;
}
MyCustomAlloc(const MyCustomAlloc &obj) // copy constructor
{
Init();
m_pool = obj.m_pool;
m_nPoolSize = obj.m_nPoolSize;
}
private:
void operator =(const MyCustomAlloc &);
public:
byte *m_pool;
unsigned m_nPoolSize;
template <class _Other>
MyCustomAlloc(const MyCustomAlloc<_Other> &other)
{
Init();
m_pool = other.m_pool;
m_nPoolSize = other.m_nPoolSize;
}
~MyCustomAlloc()
{
}
template <class U>
struct rebind
{
typedef MyCustomAlloc<U> other;
};
pointer
address(reference r) const
{
return &r;
}
const_pointer
address(const_reference r) const
{
return &r;
}
pointer
allocate(size_type n, const void* /*hint*/ = 0)
{
pointer p;
unsigned nSize = n * sizeof(T);
if (m_pool) // if we have a mem pool from which to allocated
{
p = (pointer)m_pool;// just return the next available mem in the pool
if (g_nTot + nSize > m_nPoolSize)
{
_ASSERT(0);//,"out of mem pool");
return 0;
}
m_pool += nSize; // and bump the pointer
}
else
{
p = (pointer)malloc(nSize);// no pool: just use malloc
}
g_nCnt += 1;
g_nTot += nSize;
_ASSERTE(p);
return p;
}
void
deallocate(pointer p, size_type /*n*/)
{
if (!m_pool)// if there's a pool, nothing to do
{
free(p);
}
}
void
construct(pointer p, const T& val)
{
new (p) T(val);
}
void
destroy(pointer p)
{
p->~T();
}
size_type
max_size() const
{
return ULONG_MAX / sizeof(T);
}
};
template <class T>
bool
operator==(const MyCustomAlloc<T>& left, const MyCustomAlloc<T>& right)
{
if (left.m_pool == right.m_pool)
{
return true;
}
return false;
}
template <class T>
bool
operator!=(const MyCustomAlloc<T>& left, const MyCustomAlloc<T>& right)
{
if (left.m_pool != right.m_pool)
{
return true;
}
return false;
}
// </MyCustomAlloc>
#endif

View File

@ -0,0 +1,141 @@
#include "Allocator.h"
#include "DataTypes.h"
#include <new>
#include <assert.h>
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
Allocator::Allocator(size_t size, UINT objects, CHAR* memory, const CHAR* name) :
m_blockSize(size < sizeof(long*) ? sizeof(long*) : size),
m_objectSize(size),
m_maxObjects(objects),
m_pHead(NULL),
m_poolIndex(0),
m_blockCnt(0),
m_blocksInUse(0),
m_allocations(0),
m_deallocations(0),
m_name(name)
{
// If using a fixed memory pool
if(m_maxObjects)
{
// If caller provided an external memory pool
if(memory)
{
m_pPool = memory;
m_allocatorMode = STATIC_POOL;
}
else
{
m_pPool = (CHAR*)new CHAR[m_blockSize * m_maxObjects];
m_allocatorMode = HEAP_POOL;
}
}
else
m_allocatorMode = HEAP_BLOCKS;
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
Allocator::~Allocator()
{
// If using pool then destroy it, otherwise traverse free-list and
// destroy each individual block
if(m_allocatorMode == HEAP_POOL)
delete [] m_pPool;
else if(m_allocatorMode == HEAP_BLOCKS)
{
while(m_pHead)
delete [](CHAR*)Pop();
}
}
//------------------------------------------------------------------------------
// Allocate
//------------------------------------------------------------------------------
void* Allocator::Allocate(size_t size)
{
assert(size <= m_objectSize);
// If can't obtain existing block then get a new one
void* pBlock = Pop();
if(!pBlock)
{
// If using a pool method then get block from pool,
// otherwise using dynamic so get block from heap
if(m_maxObjects)
{
// If we have not exceeded the pool maximum
if(m_poolIndex < m_maxObjects)
{
pBlock = (void*)(m_pPool + (m_poolIndex++ * m_blockSize));
}
else
{
// Get the pointer to the new handler
std::new_handler handler = std::set_new_handler(0);
std::set_new_handler(handler);
// If a new handler is defined, call it
if(handler)
(*handler)();
else
assert(0);
}
}
else
{
m_blockCnt++;
pBlock = (void*)new CHAR[m_blockSize];
}
}
m_blocksInUse++;
m_allocations++;
return pBlock;
}
//------------------------------------------------------------------------------
// Deallocate
//------------------------------------------------------------------------------
void Allocator::Deallocate(void* pBlock)
{
Push(pBlock);
m_blocksInUse--;
m_deallocations++;
}
//------------------------------------------------------------------------------
// Push
//------------------------------------------------------------------------------
void Allocator::Push(void* pMemory)
{
Block* pBlock = (Block*)pMemory;
pBlock->pNext = m_pHead;
m_pHead = pBlock;
}
//------------------------------------------------------------------------------
// Pop
//------------------------------------------------------------------------------
void* Allocator::Pop()
{
Block* pBlock = NULL;
if(m_pHead)
{
pBlock = m_pHead;
m_pHead = m_pHead->pNext;
}
return (void*)pBlock;
}

View File

@ -0,0 +1,118 @@
#ifndef __ALLOCATOR_H
#define __ALLOCATOR_H
#include "DataTypes.h"
#include <stddef.h>
/// See http://www.codeproject.com/Articles/1083210/An-efficient-Cplusplus-fixed-block-memory-allocato
class Allocator
{
public:
/// Constructor
/// @param[in] size - size of the fixed blocks
/// @param[in] objects - maximum number of object. If 0, new blocks are
/// created off the heap as necessary.
/// @param[in] memory - pointer to a block of static memory for allocator or NULL
/// to obtain memory from global heap. If not NULL, the objects argument
/// defines the size of the memory block (size x objects = memory size in bytes).
/// @param[in] name - optional allocator name string.
Allocator(size_t size, UINT objects = 0, CHAR* memory = NULL, const CHAR* name = NULL);
/// Destructor
~Allocator();
/// Get a pointer to a memory block.
/// @param[in] size - size of the block to allocate
/// @return Returns pointer to the block. Otherwise NULL if unsuccessful.
void* Allocate(size_t size);
/// Return a pointer to the memory pool.
/// @param[in] pBlock - block of memory deallocate (i.e push onto free-list)
void Deallocate(void* pBlock);
/// Get the allocator name string.
/// @return A pointer to the allocator name or NULL if none was assigned.
const CHAR* GetName() { return m_name; }
/// Gets the fixed block memory size, in bytes, handled by the allocator.
/// @return The fixed block size in bytes.
size_t GetBlockSize() { return m_blockSize; }
/// Gets the maximum number of blocks created by the allocator.
/// @return The number of fixed memory blocks created.
UINT GetBlockCount() { return m_blockCnt; }
/// Gets the number of blocks in use.
/// @return The number of blocks in use by the application.
UINT GetBlocksInUse() { return m_blocksInUse; }
/// Gets the total number of allocations for this allocator instance.
/// @return The total number of allocations.
UINT GetAllocations() { return m_allocations; }
/// Gets the total number of deallocations for this allocator instance.
/// @return The total number of deallocations.
UINT GetDeallocations() { return m_deallocations; }
private:
/// Push a memory block onto head of free-list.
/// @param[in] pMemory - block of memory to push onto free-list
void Push(void* pMemory);
/// Pop a memory block from head of free-list.
/// @return Returns pointer to the block. Otherwise NULL if unsuccessful.
void* Pop();
struct Block
{
Block* pNext;
};
enum AllocatorMode { HEAP_BLOCKS, HEAP_POOL, STATIC_POOL };
const size_t m_blockSize;
const size_t m_objectSize;
const UINT m_maxObjects;
AllocatorMode m_allocatorMode;
Block* m_pHead;
CHAR* m_pPool;
UINT m_poolIndex;
UINT m_blockCnt;
UINT m_blocksInUse;
UINT m_allocations;
UINT m_deallocations;
const CHAR* m_name;
};
// Template class to create external memory pool
template <class T, UINT Objects>
class AllocatorPool : public Allocator
{
public:
AllocatorPool() : Allocator(sizeof(T), Objects, m_memory)
{
}
private:
CHAR m_memory[sizeof(T) * Objects];
};
// macro to provide header file interface
#define DECLARE_ALLOCATOR \
public: \
void* operator new(size_t size) { \
return _allocator.Allocate(size); \
} \
void operator delete(void* pObject) { \
_allocator.Deallocate(pObject); \
} \
private: \
static Allocator _allocator;
// macro to provide source file interface
#define IMPLEMENT_ALLOCATOR(class, objects, memory) \
Allocator class::_allocator(sizeof(class), objects, memory, #class);
#endif

View File

@ -0,0 +1,42 @@
#ifndef _DATA_TYPES_H
#define _DATA_TYPES_H
#if WIN32
#include "windows.h"
#else
typedef signed char INT8;
typedef unsigned char UINT8;
typedef signed short INT16;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef int INT32;
typedef char CHAR;
typedef short SHORT;
typedef long LONG;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef double DOUBLE;
typedef int BOOL;
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif
#endif

View File

@ -0,0 +1,16 @@
#include "Fault.h"
#include "DataTypes.h"
#include <assert.h>
//----------------------------------------------------------------------------
// FaultHandler
//----------------------------------------------------------------------------
void FaultHandler(const char* file, unsigned short line)
{
#if WIN32
// If you hit this line, it means one of the ASSERT macros failed.
DebugBreak();
#endif
assert(0);
}

View File

@ -0,0 +1,23 @@
#ifndef _FAULT_H
#define _FAULT_H
#ifdef __cplusplus
extern "C" {
#endif
#define ASSERT() \
FaultHandler(__FILE__, (unsigned short) __LINE__)
#define ASSERT_TRUE(condition) \
do {if (!(condition)) FaultHandler(__FILE__, (unsigned short) __LINE__);} while (0)
/// Handles all software assertions in the system.
/// @param[in] file - the file name that the software assertion occurred on
/// @param[in] line - the line number that the software assertion occurred on
void FaultHandler(const char* file, unsigned short line);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,194 @@
#include "stdafx.h"
#include <new>
#include <iostream>
#include "xlist.h"
#include "xmap.h"
#include "xqueue.h"
#include "xset.h"
#include "xsstream.h"
#include "xstring.h"
// On VisualStudio, to disable the debug heap for faster performance when using
// the debugger use this option:
// Debugging > Environment _NO_DEBUG_HEAP=1
using namespace std;
static int MAX_BENCHMARK = 10000;
typedef void (*TestFunc)();
void ListGlobalHeapTest();
void MapGlobalHeapTest();
void StringGlobalHeapTest();
void ListFixedBlockTest();
void MapFixedBlockTest();
void StringFixedBlockTest();
void Benchmark(const char* name, TestFunc testFunc);
static void out_of_memory()
{
// new-handler function called by Allocator when pool is out of memory
ASSERT();
}
//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
int main(void)
{
std::set_new_handler(out_of_memory);
xlist<int> myList;
myList.push_back(123);
xmap<char, int> myMap;
myMap['a'] = 10;
xqueue<int> myQueue;
myQueue.push(123);
xset<xstring> mySet;
mySet.insert("hello");
mySet.insert("world");
xstringstream myStringStream;
myStringStream << "hello world " << 2016 << ends;
xwstringstream myWStringStream;
myWStringStream << L"hello world " << 2016 << ends;
xstring myString("hello world");
Benchmark("std::list Global Heap (Run 1)", ListGlobalHeapTest);
Benchmark("std::list Global Heap (Run 2)", ListGlobalHeapTest);
Benchmark("std::list Global Heap (Run 3)", ListGlobalHeapTest);
Benchmark("xlist Fixed Block (Run 1)", ListFixedBlockTest);
Benchmark("xlist Fixed Block (Run 2)", ListFixedBlockTest);
Benchmark("xlist Fixed Block (Run 3)", ListFixedBlockTest);
Benchmark("std::map Global Heap (Run 1)", MapGlobalHeapTest);
Benchmark("std::map Global Heap (Run 2)", MapGlobalHeapTest);
Benchmark("std::map Global Heap (Run 3)", MapGlobalHeapTest);
Benchmark("xmap Fixed Block (Run 1)", MapFixedBlockTest);
Benchmark("xmap Fixed Block (Run 2)", MapFixedBlockTest);
Benchmark("xmap Fixed Block (Run 3)", MapFixedBlockTest);
Benchmark("std::string Global Heap (Run 1)", StringGlobalHeapTest);
Benchmark("std::string Global Heap (Run 2)", StringGlobalHeapTest);
Benchmark("std::string Global Heap (Run 3)", StringGlobalHeapTest);
Benchmark("xstring Fixed Block (Run 1)", StringFixedBlockTest);
Benchmark("xstring Fixed Block (Run 2)", StringFixedBlockTest);
Benchmark("xstring Fixed Block (Run 3)", StringFixedBlockTest);
xalloc_stats();
return 0;
}
//------------------------------------------------------------------------------
// MapGlobalHeapTest
//------------------------------------------------------------------------------
void MapGlobalHeapTest()
{
map<int, char> myMap;
for(int i = 0; i < MAX_BENCHMARK; i++)
myMap[i] = 'a';
myMap.clear();
}
//------------------------------------------------------------------------------
// MapFixedBlockTest
//------------------------------------------------------------------------------
void MapFixedBlockTest()
{
xmap<int, char> myMap;
for(int i = 0; i < MAX_BENCHMARK; i++)
myMap[i] = 'a';
myMap.clear();
}
//------------------------------------------------------------------------------
// ListGlobalHeapTest
//------------------------------------------------------------------------------
void ListGlobalHeapTest()
{
list<int> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
myList.push_back(123);
myList.clear();
}
//------------------------------------------------------------------------------
// ListFixedBlockTest
//------------------------------------------------------------------------------
void ListFixedBlockTest()
{
xlist<int> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
myList.push_back(123);
myList.clear();
}
//------------------------------------------------------------------------------
// StringGlobalHeapTest
//------------------------------------------------------------------------------
void StringGlobalHeapTest()
{
list<string> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
{
string myString("benchmark");
myString += "benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test "
"benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test";
myList.push_back(myString);
}
myList.clear();
}
//------------------------------------------------------------------------------
// StringFixedBlockTest
//------------------------------------------------------------------------------
void StringFixedBlockTest()
{
xlist<xstring> myList;
for(int i = 0; i < MAX_BENCHMARK; i++)
{
xstring myString("benchmark");
myString += "benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test "
"benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test benchmark test";
myList.push_back(myString);
}
myList.clear();
}
//------------------------------------------------------------------------------
// Benchmark
//------------------------------------------------------------------------------
void Benchmark(const char* name, TestFunc testFunc)
{
#if WIN32
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds = {0};
LARGE_INTEGER Frequency;
SetProcessPriorityBoost(GetCurrentProcess(), true);
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&StartingTime);
// Call test function
testFunc();
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
std::cout << name << " Elapsed time: " << ElapsedMicroseconds.QuadPart << std::endl;
SetProcessPriorityBoost(GetCurrentProcess(), false);
#endif
}

View File

@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// Allocator.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View File

@ -0,0 +1,14 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#include <stdio.h>
// TODO: reference additional headers your program requires here

View File

@ -0,0 +1,138 @@
#ifndef _STL_ALLOCATOR_H
#define _STL_ALLOCATOR_H
// See http://www.codeproject.com/Articles/1089905/A-Custom-STL-std-allocator-Replacement-Improves-Performance-
#include "xallocator.h"
#include "Fault.h"
template <typename T> class stl_allocator;
template <> class stl_allocator<void>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
// reference to void members are impossible.
typedef void value_type;
template <class U>
struct rebind { typedef stl_allocator<U> other; };
};
/// @brief stl_allocator is STL-compatible allocator used to provide fixed
/// block allocations.
/// @details The default allocator for the STL is the global heap. The
/// stl_allocator is custom allocator where xmalloc/xfree is used to obtain
/// and release memory.
template <typename T>
class stl_allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T & reference;
typedef const T & const_reference;
typedef T value_type;
/// Constructor
stl_allocator() {}
/// Destructor
~stl_allocator() {}
/// Copy constructor
template <class U> stl_allocator(const stl_allocator<U> &) {}
template <class U>
struct rebind { typedef stl_allocator<U> other; };
/// Return reference address.
/// @return Pointer to T memory.
pointer address(reference x) const {return &x;}
/// Return reference address.
/// @return Const pointer to T memory.
const_pointer address(const_reference x) const {return &x;}
/// Get the maximum size of memory.
/// @return Max memory size in bytes.
size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
/// Allocates a fixed block of memory
/// @param[in] n - size of memory to allocate in bytes
/// @param[in] hint
/// @return Pointer to the allocated memory.
pointer allocate(size_type n, stl_allocator<void>::const_pointer hint = 0)
{
return static_cast<pointer>(xmalloc(n * sizeof(T)));
}
/// Deallocate a previously allocated fixed memory block.
/// @param[in] p - pointer to the memory block
/// @param[in] n - size of memory in bytes
void deallocate(pointer p, size_type n)
{
xfree(p);
}
/// Constructs a new instance.
/// @param[in] p - pointer to the memory where the instance is constructed
/// using placement new.
/// @param[in] val - instance of object to copy construct.
void construct(pointer p, const T & val)
{
new(static_cast<void*>(p)) T(val);
}
/// Create a new object instance using placement new.
/// @param[in] p - pointer to the memory where the instance is constructed
/// using placement new.
void construct(pointer p)
{
new(static_cast<void*>(p)) T();
}
/// Destroys an instance. Objects created with placement new must
/// explicitly call the destructor.
/// @param[in] p - pointer to object instance.
void destroy(pointer p)
{
p->~T();
}
};
template <typename T, typename U>
inline bool operator==(const stl_allocator<T> &, const stl_allocator<U>) {return true;}
template <typename T, typename U>
inline bool operator!=(const stl_allocator<T> &, const stl_allocator<U>) {return false;}
// For VC6/STLPort 4-5-3 see /stl/_alloc.h, line 464
// "If custom allocators are being used without member template classes support :
// user (on purpose) is forced to define rebind/get operations !!!"
#ifdef _WIN32
#define STD_ALLOC_CDECL __cdecl
#else
#define STD_ALLOC_CDECL
#endif
namespace std
{
template <class _Tp1, class _Tp2>
inline stl_allocator<_Tp2> & STD_ALLOC_CDECL
__stl_alloc_rebind(stl_allocator<_Tp1> & __a, const _Tp2*)
{
return (stl_allocator<_Tp2> &)(__a);
}
template <class _Tp1, class _Tp2>
inline stl_allocator<_Tp2> STD_ALLOC_CDECL
__stl_alloc_create(const stl_allocator<_Tp1> &, const _Tp2*)
{
return stl_allocator<_Tp2>();
}
}
#endif

View File

@ -0,0 +1,13 @@
#pragma once
// The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified.
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif

View File

@ -0,0 +1,404 @@
#include "xallocator.h"
#include "Allocator.h"
#include "Fault.h"
#include <iostream>
using namespace std;
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
static CRITICAL_SECTION _criticalSection;
static BOOL _xallocInitialized = FALSE;
// Define STATIC_POOLS to switch from heap blocks mode to static pools mode
//#define STATIC_POOLS
#ifdef STATIC_POOLS
// Update this section as necessary if you want to use static memory pools.
// See also xalloc_init() and xalloc_destroy() for additional updates required.
#define MAX_ALLOCATORS 12
#define MAX_BLOCKS 32
// Create static storage for each static allocator instance
CHAR* _allocator8 [sizeof(AllocatorPool<CHAR[8], MAX_BLOCKS>)];
CHAR* _allocator16 [sizeof(AllocatorPool<CHAR[16], MAX_BLOCKS>)];
CHAR* _allocator32 [sizeof(AllocatorPool<CHAR[32], MAX_BLOCKS>)];
CHAR* _allocator64 [sizeof(AllocatorPool<CHAR[64], MAX_BLOCKS>)];
CHAR* _allocator128 [sizeof(AllocatorPool<CHAR[128], MAX_BLOCKS>)];
CHAR* _allocator256 [sizeof(AllocatorPool<CHAR[256], MAX_BLOCKS>)];
CHAR* _allocator396 [sizeof(AllocatorPool<CHAR[396], MAX_BLOCKS>)];
CHAR* _allocator512 [sizeof(AllocatorPool<CHAR[512], MAX_BLOCKS>)];
CHAR* _allocator768 [sizeof(AllocatorPool<CHAR[768], MAX_BLOCKS>)];
CHAR* _allocator1024 [sizeof(AllocatorPool<CHAR[1024], MAX_BLOCKS>)];
CHAR* _allocator2048 [sizeof(AllocatorPool<CHAR[2048], MAX_BLOCKS>)];
CHAR* _allocator4096 [sizeof(AllocatorPool<CHAR[4096], MAX_BLOCKS>)];
// Array of pointers to all allocator instances
static Allocator* _allocators[MAX_ALLOCATORS];
#else
#define MAX_ALLOCATORS 15
static Allocator* _allocators[MAX_ALLOCATORS];
#endif // STATIC_POOLS
// For C++ applications, must define AUTOMATIC_XALLOCATOR_INIT_DESTROY to
// correctly ensure allocators are initialized before any static user C++
// construtor/destructor executes which might call into the xallocator API.
// This feature costs 1-byte of RAM per C++ translation unit. This feature
// can be disabled only under the following circumstances:
//
// 1) The xallocator is only used within C files.
// 2) STATIC_POOLS is undefined and the application never exits main (e.g.
// an embedded system).
//
// In either of the two cases above, call xalloc_init() in main at startup,
// and xalloc_destroy() before main exits. In all other situations
// XallocInitDestroy must be used to call xalloc_init() and xalloc_destroy().
#ifdef AUTOMATIC_XALLOCATOR_INIT_DESTROY
INT XallocInitDestroy::refCount = 0;
XallocInitDestroy::XallocInitDestroy()
{
// Track how many static instances of XallocInitDestroy are created
if(refCount++ == 0)
xalloc_init();
}
XallocInitDestroy::~XallocInitDestroy()
{
// Last static instance to have destructor called?
if(--refCount == 0)
xalloc_destroy();
}
#endif // AUTOMATIC_XALLOCATOR_INIT_DESTROY
/// Returns the next higher powers of two. For instance, pass in 12 and
/// the value returned would be 16.
/// @param[in] k - numeric value to compute the next higher power of two.
/// @return The next higher power of two based on the input k.
template <class T>
T nexthigher(T k)
{
k--;
for(size_t i = 1; i < sizeof(T)*CHAR_BIT; i <<= 1)
k |= (k >> i);
return k + 1;
}
/// Create the xallocator lock. Call only one time at startup.
static void lock_init()
{
BOOL success = InitializeCriticalSectionAndSpinCount(&_criticalSection, 0x00000400);
ASSERT_TRUE(success != 0);
_xallocInitialized = TRUE;
}
/// Destroy the xallocator lock.
static void lock_destroy()
{
DeleteCriticalSection(&_criticalSection);
_xallocInitialized = FALSE;
}
/// Lock the shared resource.
static inline void lock_get()
{
if(_xallocInitialized == FALSE)
return;
EnterCriticalSection(&_criticalSection);
}
/// Unlock the shared resource.
static inline void lock_release()
{
if(_xallocInitialized == FALSE)
return;
LeaveCriticalSection(&_criticalSection);
}
/// Stored a pointer to the allocator instance within the block region.
/// a pointer to the client's area within the block.
/// @param[in] block - a pointer to the raw memory block.
/// @param[in] size - the client requested size of the memory block.
/// @return A pointer to the client's address within the raw memory block.
static inline void* set_block_allocator(void* block, Allocator* allocator)
{
// Cast the raw block memory to a Allocator pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Write the size into the memory block
*pAllocatorInBlock = allocator;
// Advance the pointer past the Allocator* block size and return a pointer to
// the client's memory region
return ++pAllocatorInBlock;
}
/// Gets the size of the memory block stored within the block.
/// @param[in] block - a pointer to the client's memory block.
/// @return The original allocator instance stored in the memory block.
static inline Allocator* get_block_allocator(void* block)
{
// Cast the client memory to a Allocator pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Back up one Allocator* position to get the stored allocator instance
pAllocatorInBlock--;
// Return the allocator instance stored within the memory block
return *pAllocatorInBlock;
}
/// Returns the raw memory block pointer given a client memory pointer.
/// @param[in] block - a pointer to the client memory block.
/// @return A pointer to the original raw memory block address.
static inline void* get_block_ptr(void* block)
{
// Cast the client memory to a Allocator* pointer
Allocator** pAllocatorInBlock = static_cast<Allocator**>(block);
// Back up one Allocator* position and return the original raw memory block pointer
return --pAllocatorInBlock;
}
/// Returns an allocator instance matching the size provided
/// @param[in] size - allocator block size
/// @return Allocator instance handling requested block size or NULL
/// if no allocator exists.
static inline Allocator* find_allocator(size_t size)
{
for(INT i = 0; i < MAX_ALLOCATORS; i++)
{
if(_allocators[i] == 0)
break;
if(_allocators[i]->GetBlockSize() == size)
return _allocators[i];
}
return NULL;
}
/// Insert an allocator instance into the array
/// @param[in] allocator - An allocator instance
static inline void insert_allocator(Allocator* allocator)
{
for(INT i = 0; i < MAX_ALLOCATORS; i++)
{
if(_allocators[i] == 0)
{
_allocators[i] = allocator;
return;
}
}
ASSERT();
}
/// This function must be called exactly one time *before* any other xallocator
/// API is called. XallocInitDestroy constructor calls this function automatically.
extern "C" void xalloc_init()
{
lock_init();
#ifdef STATIC_POOLS
// For STATIC_POOLS mode, the allocators must be initialized before any other
// static user class constructor is run. Therefore, use placement new to initialize
// each allocator into the previously reserved static memory locations.
new(&_allocator8) AllocatorPool<CHAR[8], MAX_BLOCKS>();
new(&_allocator16) AllocatorPool<CHAR[16], MAX_BLOCKS>();
new(&_allocator32) AllocatorPool<CHAR[32], MAX_BLOCKS>();
new(&_allocator64) AllocatorPool<CHAR[64], MAX_BLOCKS>();
new(&_allocator128) AllocatorPool<CHAR[128], MAX_BLOCKS>();
new(&_allocator256) AllocatorPool<CHAR[256], MAX_BLOCKS>();
new(&_allocator396) AllocatorPool<CHAR[396], MAX_BLOCKS>();
new(&_allocator512) AllocatorPool<CHAR[512], MAX_BLOCKS>();
new(&_allocator768) AllocatorPool<CHAR[768], MAX_BLOCKS>();
new(&_allocator1024) AllocatorPool<CHAR[1024], MAX_BLOCKS>();
new(&_allocator2048) AllocatorPool<CHAR[2048], MAX_BLOCKS>();
new(&_allocator4096) AllocatorPool<CHAR[4096], MAX_BLOCKS>();
// Populate allocator array with all instances
_allocators[0] = (Allocator*)&_allocator8;
_allocators[1] = (Allocator*)&_allocator16;
_allocators[2] = (Allocator*)&_allocator32;
_allocators[3] = (Allocator*)&_allocator64;
_allocators[4] = (Allocator*)&_allocator128;
_allocators[5] = (Allocator*)&_allocator256;
_allocators[6] = (Allocator*)&_allocator396;
_allocators[7] = (Allocator*)&_allocator512;
_allocators[8] = (Allocator*)&_allocator768;
_allocators[9] = (Allocator*)&_allocator1024;
_allocators[10] = (Allocator*)&_allocator2048;
_allocators[11] = (Allocator*)&_allocator4096;
#endif
}
/// Called one time when the application exits to cleanup any allocated memory.
/// ~XallocInitDestroy destructor calls this function automatically.
extern "C" void xalloc_destroy()
{
lock_get();
#ifdef STATIC_POOLS
for(INT i = 0; i < MAX_ALLOCATORS; i++)
{
_allocators[i]->~Allocator();
_allocators[i] = 0;
}
#else
for(INT i = 0; i < MAX_ALLOCATORS; i++)
{
if(_allocators[i] == 0)
break;
delete _allocators[i];
_allocators[i] = 0;
}
#endif
lock_release();
lock_destroy();
}
/// Get an Allocator instance based upon the client's requested block size.
/// If a Allocator instance is not currently available to handle the size,
/// then a new Allocator instance is create.
/// @param[in] size - the client's requested block size.
/// @return An Allocator instance that handles blocks of the requested
/// size.
extern "C" Allocator* xallocator_get_allocator(size_t size)
{
// Based on the size, find the next higher powers of two value.
// Add sizeof(Allocator*) to the requested block size to hold the size
// within the block memory region. Most blocks are powers of two,
// however some common allocator block sizes can be explicitly defined
// to minimize wasted storage. This offers application specific tuning.
size_t blockSize = size + sizeof(Allocator*);
if(blockSize > 256 && blockSize <= 396)
blockSize = 396;
else if(blockSize > 512 && blockSize <= 768)
blockSize = 768;
else
blockSize = nexthigher<size_t>(blockSize);
Allocator* allocator = find_allocator(blockSize);
#ifdef STATIC_POOLS
ASSERT_TRUE(allocator != NULL);
#else
// If there is not an allocator already created to handle this block size
if(allocator == NULL)
{
// Create a new allocator to handle blocks of the size required
allocator = new Allocator(blockSize, 0, 0, "xallocator");
// Insert allocator into array
insert_allocator(allocator);
}
#endif
return allocator;
}
/// Allocates a memory block of the requested size. The blocks are created from
/// the fixed block allocators.
/// @param[in] size - the client requested size of the block.
/// @return A pointer to the client's memory block.
extern "C" void* xmalloc(size_t size)
{
lock_get();
// Allocate a raw memory block
Allocator* allocator = xallocator_get_allocator(size);
void* blockMemoryPtr = allocator->Allocate(sizeof(Allocator*) + size);
lock_release();
// Set the block Allocator* within the raw memory block region
void* clientsMemoryPtr = set_block_allocator(blockMemoryPtr, allocator);
return clientsMemoryPtr;
}
/// Frees a memory block previously allocated with xalloc. The blocks are returned
/// to the fixed block allocator that originally created it.
/// @param[in] ptr - a pointer to a block created with xalloc.
extern "C" void xfree(void* ptr)
{
if(ptr == 0)
return;
// Extract the original allocator instance from the caller's block pointer
Allocator* allocator = get_block_allocator(ptr);
// Convert the client pointer into the original raw block pointer
void* blockPtr = get_block_ptr(ptr);
lock_get();
// Deallocate the block
allocator->Deallocate(blockPtr);
lock_release();
}
/// Reallocates a memory block previously allocated with xalloc.
/// @param[in] ptr - a pointer to a block created with xalloc.
/// @param[in] size - the client requested block size to create.
extern "C" void* xrealloc(void* oldMem, size_t size)
{
if(oldMem == 0)
return xmalloc(size);
if(size == 0)
{
xfree(oldMem);
return 0;
}
else
{
// Create a new memory block
void* newMem = xmalloc(size);
if(newMem != 0)
{
// Get the original allocator instance from the old memory block
Allocator* oldAllocator = get_block_allocator(oldMem);
size_t oldSize = oldAllocator->GetBlockSize() - sizeof(Allocator*);
// Copy the bytes from the old memory block into the new (as much as will fit)
memcpy(newMem, oldMem, (oldSize < size) ? oldSize : size);
// Free the old memory block
xfree(oldMem);
// Return the client pointer to the new memory block
return newMem;
}
return 0;
}
}
/// Output xallocator usage statistics
extern "C" void xalloc_stats()
{
lock_get();
for(INT i = 0; i < MAX_ALLOCATORS; i++)
{
if(_allocators[i] == 0)
break;
if(_allocators[i]->GetName() != NULL)
cout << _allocators[i]->GetName();
cout << " Block Size: " << _allocators[i]->GetBlockSize();
cout << " Block Count: " << _allocators[i]->GetBlockCount();
cout << " Blocks In Use: " << _allocators[i]->GetBlocksInUse();
cout << endl;
}
lock_release();
}

View File

@ -0,0 +1,85 @@
#ifndef _XALLOCATOR_H
#define _XALLOCATOR_H
#include <stddef.h>
#include "DataTypes.h"
// See http://www.codeproject.com/Articles/1084801/Replace-malloc-free-with-a-Fast-Fixed-Block-Memory
#ifdef __cplusplus
// Define AUTOMATIC_XALLOCATOR_INIT_DESTROY to automatically call xalloc_init() and
// xalloc_destroy() when using xallocator in C++ projects. On embedded systems that
// never exit, you can save 1-byte of RAM storage per translation unit by undefining
// AUTOMATIC_XALLOCATOR_INIT_DESTROY and calling xalloc_init() manually before the OS
// starts.
#define AUTOMATIC_XALLOCATOR_INIT_DESTROY
#ifdef AUTOMATIC_XALLOCATOR_INIT_DESTROY
/// If a C++ translation unit, create a static instance of XallocInitDestroy. Any C++
/// file including xallocator.h will have the xallocDestroy instance declared first
/// within the translation unit and thus will be constructed first. Destruction
/// will occur in the reverse order so xallocInitDestroy is called last. This way,
/// any static user objects relying on xallocator will be destroyed first before
/// xalloc_destroy() is called.
class XallocInitDestroy
{
public:
XallocInitDestroy();
~XallocInitDestroy();
private:
static INT refCount;
};
static XallocInitDestroy xallocInitDestroy;
#endif // AUTOMATIC_XALLOCATOR_INIT_DESTROY
#endif // __cplusplus
#ifdef __cplusplus
extern "C" {
#endif
/// This function must be called exactly one time before the operating system
/// threading starts. If using xallocator exclusively in C files within your application
/// code, you must call this function before the OS starts. If using C++, client code
/// does not call xalloc_init. Let XallocInitDestroy() call xalloc_init automatically.
/// Embedded systems that never exit can call xalloc_init() manually at startup
/// and eliminate XallocInitDestroy usage. When the system is still single threaded at
/// startup, the xallocator API does not need mutex protection.
void xalloc_init();
/// This function must be called once when the application exits. Never call xalloc_destroy()
/// manually except if using xallocator in a C-only application. If using xallocator
/// exclusively in C files within your application code, you must call this function before
/// the program exits. If using C++, ~XallocInitDestroy() must call xalloc_destroy automatically.
/// Embedded systems that never exit need not call this function at all.
void xalloc_destroy();
/// Allocate a block of memory
/// @param[in] size - the size of the block to allocate.
void* xmalloc(size_t size);
/// Frees a previously xalloc allocated block
/// @param[in] ptr - a pointer to a previously allocated memory using xalloc.
void xfree(void* ptr);
/// Reallocates an existing xalloc block to a new size
/// @param[in] ptr - a pointer to a previously allocated memory using xalloc.
/// @param[in] size - the size of the new block
void* xrealloc(void* ptr, size_t size);
/// Output allocator statistics to the standard output
void xalloc_stats();
// Macro to overload new/delete with xalloc/xfree
#define XALLOCATOR \
public: \
void* operator new(size_t size) { \
return xmalloc(size); \
} \
void operator delete(void* pObject) { \
xfree(pObject); \
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,15 @@
#ifndef _XLIST_H
#define _XLIST_H
#include "stl_allocator.h"
#include <list>
template<class _Ty,
class _Ax = stl_allocator<_Ty>>
class xlist
: public std::list<_Ty, _Ax>
{
};
#endif

View File

@ -0,0 +1,26 @@
#ifndef _XMAP_H
#define _XMAP_H
#include "stl_allocator.h"
#include <map>
template<class _Kty,
class _Ty,
class _Pr = std::less<_Kty>,
class _Alloc = stl_allocator<std::pair<const _Kty, _Ty>>>
class xmap
: public std::map<_Kty, _Ty, _Pr, _Alloc>
{
};
template<class _Kty,
class _Ty,
class _Pr = std::less<_Kty>,
class _Alloc = stl_allocator<std::pair<const _Kty, _Ty>>>
class xmultimap
: public std::multimap<_Kty, _Ty, _Pr, _Alloc>
{
};
#endif

View File

@ -0,0 +1,16 @@
#ifndef _XQUEUE_H
#define _XQUEUE_H
#include "stl_allocator.h"
#include <queue>
#include <list>
template<class _Tp,
class _Sequence = std::list<_Tp, stl_allocator<_Tp>>>
class xqueue
: public std::queue<_Tp, _Sequence>
{
};
#endif

View File

@ -0,0 +1,24 @@
#ifndef _XSET_H
#define _XSET_H
#include "stl_allocator.h"
#include <set>
template<class _Kty,
class _Pr = std::less<_Kty>,
class _Alloc = stl_allocator<_Kty>>
class xset
: public std::set<_Kty, _Pr, _Alloc>
{
};
/// @see xset
template<class _Kty,
class _Pr = std::less<_Kty>,
class _Alloc = stl_allocator<_Kty>>
class xmultiset
: public std::multiset<_Kty, _Pr, _Alloc>
{
};
#endif

View File

@ -0,0 +1,14 @@
#ifndef _XSSTREAM_H
#define _XSSTREAM_H
#include "stl_allocator.h"
#include <sstream>
typedef std::basic_stringstream<char, std::char_traits<char>, stl_allocator<char>> xstringstream;
typedef std::basic_ostringstream<char, std::char_traits<char>, stl_allocator<char>> xostringstream;
typedef std::basic_stringstream<wchar_t, std::char_traits<wchar_t>, stl_allocator<wchar_t>> xwstringstream;
typedef std::basic_ostringstream<wchar_t, std::char_traits<wchar_t>, stl_allocator<wchar_t>> xwostringstream;
#endif

View File

@ -0,0 +1,11 @@
#ifndef _XSTRING_H
#define _XSTRING_H
#include "stl_allocator.h"
#include <string>
typedef std::basic_string<char, std::char_traits<char>, stl_allocator<char>> xstring;
typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, stl_allocator<wchar_t>> xwstring;
#endif

View File

@ -97,6 +97,9 @@
<ClCompile Include="reference.cpp" />
<ClCompile Include="simplescript.cpp" />
<ClCompile Include="stackinfo.cpp" />
<ClCompile Include="stl_allocator\Allocator.cpp" />
<ClCompile Include="stl_allocator\Fault.cpp" />
<ClCompile Include="stl_allocator\xallocator.cpp" />
<ClCompile Include="stringformat.cpp" />
<ClCompile Include="stringutils.cpp" />
<ClCompile Include="symbolinfo.cpp" />

View File

@ -458,6 +458,15 @@
<ClCompile Include="symbolsourcebase.cpp">
<Filter>Source Files\Symbols</Filter>
</ClCompile>
<ClCompile Include="stl_allocator\Allocator.cpp">
<Filter>Source Files\Symbols</Filter>
</ClCompile>
<ClCompile Include="stl_allocator\Fault.cpp">
<Filter>Source Files\Symbols</Filter>
</ClCompile>
<ClCompile Include="stl_allocator\xallocator.cpp">
<Filter>Source Files\Symbols</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="dbghelp\dbghelp.h">