#ifndef _ALLOC_H_
#define _ALLOC_H_
#pragma once
#include <new>
#include "Assert.h"
#ifndef NULL
#define NULL 0
#endif
namespace KOK
{
class __CSimpleAlloc
{
public:
static void* Allocate(size_t n)
{
if(n != 0) return ::operator new(n);
}
static void Deallocate(void* p)
{
if(p != NULL) return ::operator delete(p);
}
static void Deallocate(void* p, size_t n)
{
if(p != NULL) return ::operator delete(p);
}
};
class __CSmallObjectAlloc
{
private:
struct CChunk
{
unsigned char* pData;//the real chunk address
unsigned char availableBlockIndex; //first available block index
unsigned char availableBlockCount; //the maximun block count is 255
CChunk* pNext;
CChunk(unsigned char blockCount, size_t blockSize)
{
availableBlockCount = blockCount;
availableBlockIndex = 0;
pNext = NULL;
unsigned char* pDataTemp = pData = (unsigned char*)malloc(blockCount * blockSize);
unsigned char i = 0;
while(i ^ blockCount) //initial next available index, act as a signal list
{
*pDataTemp = (++i);
pDataTemp += blockSize;
}
}
~CChunk()
{
free(pData);
}
inline bool IsAvailable()
{
return availableBlockCount != 0;
}
//locate the block specified by pointer p
inline unsigned char GetBlockIndex(unsigned char* p, unsigned blockSize)
{
KOK_ASSERT((p != NULL) && (p >= pData) && ((p - pData) % blockSize == 0));
return (p - pData)/blockSize;
}
//alloc, make blocks link each other like a signal list
unsigned char* GetBlock(unsigned blockSize)
{
KOK_ASSERT(availableBlockCount != 0);
availableBlockCount--;
unsigned char pTemp = availableBlockIndex;
if(availableBlockCount)
{
availableBlockIndex = *(pData + blockSize * availableBlockIndex);
}
return pData + blockSize * pTemp;
}
//dealloc, add free block to the front of slist
void PutBlock(unsigned char* p, unsigned blockSize)
{
KOK_ASSERT((p != NULL) && (p >= pData) && ((p - pData) % blockSize == 0) );
availableBlockCount++;
*p = availableBlockIndex;
availableBlockIndex = GetBlockIndex(p, blockSize);
}
};
struct CFixAlloc
{
unsigned char blockCount; //block count, 255 is max.
size_t blockSize; //block size, in gerneral it doesnt exceed 128k
unsigned availableChunkCount; //available chunk count, which is uesed to justify if fix allocator leaves one available chunk at least.
CChunk* pFirst;//chunk list
CChunk* pCurFreePrev;
CChunk* pCurFree;//free cache
CChunk* pCurAlloc;//alloc cache
CFixAlloc* pNext;
private:
inline CChunk* NewChunk()
{
return new CChunk(blockCount, blockSize);
}
//locate the chunk specified by pointer p
inline bool IsInChunk(unsigned char* p, CChunk* pChunk)
{
KOK_ASSERT(p != NULL);
return p >= pChunk->pData && p <= pChunk->pData + blockCount * blockSize;
}
void PutBlockAux(unsigned char* p, CChunk*& pChunk, CChunk*& pChunkPrev)
{
pChunk->PutBlock(p, blockSize);
if(pChunk->availableBlockCount == 1) //in the condition of put a block from full used chunk, note while blockCount == 1
availableChunkCount++;
if(pChunk->availableBlockCount == blockCount) //all the blocks are free
{
if(availableChunkCount > 1) //leave one available chunk at least(exclude itself)
{
if(pChunk == pChunkPrev) //in the condition of first chunk would be freed
pFirst = pChunk->pNext;
else
pChunkPrev->pNext = pChunk->pNext; //remove all-blocks free chunk from slist
free(pChunk); //free the chunk
if(pChunk == pCurAlloc) //update cache accordingly
pCurAlloc = NULL;
pChunk = NULL;
availableChunkCount--;
}
}
}
public:
CFixAlloc(unsigned blockSize, unsigned char blockCount = 255)
{
this->blockSize = blockSize;
this->blockCount = blockCount;
availableChunkCount = 0;
this->pNext = NULL;
this->pFirst = this->pCurAlloc = this->pCurFree = this->pCurFreePrev = NULL;
}
~CFixAlloc()
{
CChunk* pTemp = NULL;
while(pFirst != NULL)
{
pTemp = pFirst;
pFirst = pFirst->pNext;
delete pTemp;
}
}
void PutBlock(unsigned char* p)
{
KOK_ASSERT(p != NULL);
if(pCurFree != NULL && IsInChunk(p, pCurFree)) //case 1: cache hits
{
PutBlockAux(p, pCurFree, pCurFreePrev);
}
else //case 2: search the approciate chunk from head of link
{
CChunk* pTargetChunk = pFirst;
CChunk* pTargetChunkPrev = pFirst;
while(pTargetChunk != NULL)
{
if(IsInChunk(p, pTargetChunk))
{
PutBlockAux(p, pTargetChunk, pTargetChunkPrev);
break;
}
else
{
pTargetChunkPrev = pTargetChunk;
pTargetChunk= pTargetChunk->pNext;
}
}
pCurFree = pTargetChunk; //update free cache
pCurFreePrev = pTargetChunkPrev;
}
}
//alloc
unsigned char* GetBlock()
{
unsigned char* pReturn;
CChunk* pAvailableChunk = pFirst;
if(pCurAlloc != NULL && pCurAlloc->IsAvailable()) //case 1: cache hits
{
pReturn = pCurAlloc->GetBlock(blockSize);
if(!pCurAlloc->IsAvailable())
availableChunkCount--;
}
else //case 2: find the first available chunk from head of link
{
while(pAvailableChunk != NULL)
{
if(pAvailableChunk->IsAvailable())
{
pReturn = pAvailableChunk->GetBlock(blockSize);
if(!pAvailableChunk->IsAvailable())
availableChunkCount--;
break;
}
else
{
pAvailableChunk = pAvailableChunk->pNext;
}
}
if(pAvailableChunk == NULL) //case 3: if all the chunks are used, alloc a new chunk
{
pAvailableChunk = NewChunk();
pAvailableChunk->pNext = pFirst; //add a new chunk to the front of slist
pFirst = pAvailableChunk;
pReturn = pAvailableChunk->GetBlock(blockSize);
if(pAvailableChunk->IsAvailable())// in condition of blockcount == 1
availableChunkCount++;
}
pCurAlloc = pAvailableChunk;//update cache
}
return pReturn;
}
};
private:
CFixAlloc* m_pFirst_; //fix allocator link
CFixAlloc* m_pCurAlloc_; //cache
CFixAlloc* m_pCurFree_; //cache
size_t m_nAlign_;
size_t m_blockCount_; //number of blocks per chunk
protected:
inline bool IsInFixAlloc(size_t n, CFixAlloc* pAlloc)
{
return n == pAlloc->blockSize;
}
public:
__CSmallObjectAlloc(size_t nAlign = sizeof(char*), size_t nBlockCount = 255)
{
m_pFirst_ = m_pCurAlloc_ = m_pCurFree_ = NULL;
m_nAlign_ = nAlign;
m_blockCount_ = nBlockCount;
}
~__CSmallObjectAlloc()
{
CFixAlloc* pTemp = NULL;
while(m_pFirst_ != NULL)
{
pTemp = m_pFirst_;
m_pFirst_ = m_pFirst_->pNext;
delete pTemp;
}
}
void* Allocate(size_t n)
{
n = (n + m_nAlign_ - 1) & ~(m_nAlign_ - 1); // align
if(n > 128) //big object allocator
return ::operator new(n);
else //small object allocator
{
if(m_pCurAlloc_ != NULL && IsInFixAlloc(n, m_pCurAlloc_))
{
return m_pCurAlloc_->GetBlock();
}
else
{
CFixAlloc* pTargetFixAlloc = m_pFirst_;
while(pTargetFixAlloc != NULL)
{
if(IsInFixAlloc(n, pTargetFixAlloc))
{
m_pCurAlloc_ = pTargetFixAlloc;//update cache
return pTargetFixAlloc->GetBlock();
}
else
pTargetFixAlloc = pTargetFixAlloc->pNext;
}
if(pTargetFixAlloc == NULL)
{
pTargetFixAlloc = new CFixAlloc(n,m_blockCount_);
pTargetFixAlloc->pNext = m_pFirst_;
m_pFirst_ = m_pCurAlloc_ = pTargetFixAlloc; //update cache
return pTargetFixAlloc->GetBlock();
}
}
}
}
void Deallocate(void* p, size_t n)
{
KOK_ASSERT(p != NULL);
n = (n + m_nAlign_ - 1) & ~(m_nAlign_ - 1);//align
if(n > 128)
{
::operator delete(p);
return;
}
if(m_pCurFree_ != NULL && IsInFixAlloc(n, m_pCurFree_))
{
m_pCurFree_->PutBlock((unsigned char*)p);
}
else
{
CFixAlloc* pTargetFixAlloc = m_pFirst_;
while(pTargetFixAlloc != NULL)
{
if(IsInFixAlloc(n, pTargetFixAlloc))
{
m_pCurFree_ = pTargetFixAlloc; //update cache
pTargetFixAlloc->PutBlock((unsigned char*)p);
break;
}
else
pTargetFixAlloc = pTargetFixAlloc->pNext;
}
KOK_ASSERT(pTargetFixAlloc != NULL);
}
return;
}
};
class CSmallObjectAlloc
{
private:
CSmallObjectAlloc();
CSmallObjectAlloc(const CSmallObjectAlloc&);
CSmallObjectAlloc& operator=(const CSmallObjectAlloc&);
private:
static __CSmallObjectAlloc objectAllocator;
public:
static void* Allocate(size_t n)
{
return objectAllocator.Allocate(n);
}
static void Deallocate(void* p, size_t n)
{
objectAllocator.Deallocate(p, n);
}
};
__CSmallObjectAlloc CSmallObjectAlloc::objectAllocator;
template <class T, class Value>
inline void Construct(T* p, Value& value)
{
new(p) T(value);
}
template <class T, class Value>
inline void Construct(T* p)
{
new(p) T();
}
template <class T>
inline void Destroy(T* p)
{
p->~T();
}
#define _USE_CUSTOM_ALLOC_
#ifndef _USE_CUSTOM_ALLOC_
typedef __CSimpleAlloc alloc;
#else
typedef CSmallObjectAlloc alloc;
#endif
template <class T, class Alloc = alloc>
class CAllocAdapter
{
public:
static T* Allocate(size_t n)
{
return n == 0 ? 0 : (T*)Alloc::Allocate(n * sizeof(T));
}
static T* Allocate(void)
{
return (T*)Alloc::Allocate(sizeof(T));
}
static void Deallocate(T* p)
{
Alloc::Deallocate(p, sizeof(T));
}
static void Deallocate(T* p, size_t n)
{
Alloc::Deallocate(p, n);
}
};
}
#endif
本文介绍了一种自定义内存分配器的实现方法,包括简单分配器和小对象分配器。简单分配器直接使用标准库的new和delete操作符进行内存分配与释放;小对象分配器则采用固定大小的内存块进行分配,通过缓存和链表提高分配效率。

被折叠的 条评论
为什么被折叠?



