#include "StdAfx.h"
#include "SimpleMemoryPool.h"

/***************************************************************************/
//	CSimpleMemoryPoolChunk
/***************************************************************************/
CSimpleMemoryPoolChunk::CSimpleMemoryPoolChunk()
{
	m_controlBlock = NULL;
	m_buf = NULL;
	m_blockSize = 0;
	m_poolSize = 0;
	m_usingCount = 0;
}

CSimpleMemoryPoolChunk::~CSimpleMemoryPoolChunk()
{
	Close();
}

//////////////////////////////////////////////////////////////
//	操作
//////////////////////////////////////////////////////////////
/*!
	初期化
*/
int CSimpleMemoryPoolChunk::Init(int blockSize, int blockCount)
{
	//	開放しておく
	Close();

	//	変数初期化
	m_blockSize = blockSize;
	m_poolSize = blockCount;

	//	メモリ確保
	char *newData = new char[(sizeof(SSimpleMemoryPoolSubChunk) * blockCount) + (blockSize * blockCount)];
	if(newData == NULL)
		return(-1);

	//	変数初期化
	m_controlBlock = (SSimpleMemoryPoolSubChunk *)newData;
	m_buf = newData + (sizeof(SSimpleMemoryPoolSubChunk) * blockCount);

	//	構造体初期化
	memset(m_controlBlock, 0, sizeof(SSimpleMemoryPoolSubChunk) * blockCount);

	return(0);
}

/*!
	終了
*/
void CSimpleMemoryPoolChunk::Close()
{
	//	未開放チェック
	ASSERT(m_usingCount == 0);

	if(m_controlBlock)
		delete (char *)m_controlBlock;

	m_controlBlock = NULL;
	m_buf = NULL;
	m_blockSize = 0;
	m_poolSize = 0;
	m_usingCount = 0;
}



/*!
	確保
*/
void *CSimpleMemoryPoolChunk::Alloc(int blockCount)
{
	ASSERT(blockCount > 0 && blockCount <= m_poolSize);

	//	検索するまでも無い
	if(GetFreeCount() < blockCount)
		return(NULL);

	//	検索する
	for(int i=0;i<m_poolSize;)
	{
		if(m_controlBlock[i].m_using == 0)
		{
			if(blockCount == 1)
			{
				return(MarkToUsing(i, 1));
			}
			else
			{
				//	連続空き容量取得
				int count = GetFreeBlockSize(i, blockCount);
				if(count >= blockCount)
					return(MarkToUsing(i, blockCount));
				else
					i += count;
			}
		}
		else
		{
			//	使用中分移動
			i += GetUsingBlockSize(i);
		}
	}

	//	不足
	return(NULL);
}

/*!
	開放

	TRUE:解放した、FALSE:無い
*/
int CSimpleMemoryPoolChunk::Free(void *toFree)
{
	int index = GetBlockIndex(toFree);
	if(index == -1)
		return(FALSE);

	MarkToFree(index);
	return(TRUE);
}



//////////////////////////////////////////////////////////////
//	補助
//////////////////////////////////////////////////////////////
/*!
	ポインタからブロックインデックスを取得
*/
int CSimpleMemoryPoolChunk::GetBlockIndex(void *toFree)
{
	char *ptr = (char *)toFree;

	//	位置をチェック
	if(ptr < m_buf || ptr > m_buf + m_blockSize * (m_poolSize - 1))
		return(-1);

	//	取得
	int offset = ptr - m_buf;
	return(offset / m_blockSize);
}

/*!
	連続空き容量の取得
*/
int CSimpleMemoryPoolChunk::GetFreeBlockSize(int startIndex, int maxCount)
{
	int	count = 0;
	for(int i=startIndex;i<m_poolSize;i++)
	{
		if(m_controlBlock[i].m_using || count >= maxCount)
			break;
		count++;
	}
	return(count);
}

/*!
	連続使用容量の取得
*/
int CSimpleMemoryPoolChunk::GetUsingBlockSize(int startIndex)
{
	return(m_controlBlock[startIndex].m_blockSize);
}


/*!
	使用中にする
*/
void *CSimpleMemoryPoolChunk::MarkToUsing(int startIndex, int count)
{
	//	先頭取得
	SSimpleMemoryPoolSubChunk *ptr = &(m_controlBlock[startIndex]);

	//	使用中にマーク
	ASSERT(ptr->m_using == 0);
	ASSERT(ptr->m_blockSize == 0);
	ptr->m_using = 1;
	ptr->m_blockSize = count;

	//	残りのブロックをマーク
	for(int i=0;i<count-1;i++)
	{
		ptr++;
		ASSERT(ptr->m_using == 0);
		ASSERT(ptr->m_blockSize == 0);
		ptr->m_using = 1;
		ptr->m_blockSize = -1;
	}

	//	使用数増加
	m_usingCount += count;

	return(m_buf + (m_blockSize * startIndex));
}

/*!
	空きにする
*/
void CSimpleMemoryPoolChunk::MarkToFree(int startIndex)
{
	//	先頭取得
	SSimpleMemoryPoolSubChunk *ptr = &(m_controlBlock[startIndex]);

	//	サイズ取得
	ASSERT(ptr->m_using != 0);
	ASSERT(ptr->m_blockSize != 0 && ptr->m_blockSize != -1);
	int count = ptr->m_blockSize;

	//	空きにマーク
	ptr->m_using = 0;
	ptr->m_blockSize = 0;

	//	残りを空きにマーク
	for(int i=0;i<count-1;i++)
	{
		ptr++;
		ASSERT(ptr->m_using != 0);
		ASSERT(ptr->m_blockSize == -1);
		ptr->m_using = 0;
		ptr->m_blockSize = 0;
	}

	//	使用数減少
	m_usingCount -= count;
}

/***************************************************************************/
//	CSimpleMemoryPool
/***************************************************************************/
//////////////////////////////////////////////////////////////
//	初期化
//////////////////////////////////////////////////////////////
CSimpleMemoryPool::CSimpleMemoryPool()
{
	m_blockSize = 0;
	m_growCount = 0;
	m_init = 0;
}

CSimpleMemoryPool::~CSimpleMemoryPool(void)
{
	Close();
}

//////////////////////////////////////////////////////////////
//	操作
//////////////////////////////////////////////////////////////
/*!
	初期化
*/
void CSimpleMemoryPool::Init(int blockSize, int growCount)
{
	Close();

	//	初期化
	m_blockSize = blockSize;
	m_growCount = growCount;
	m_init = TRUE;
}

/*!
	確保しておく
*/
void CSimpleMemoryPool::PreCharge()
{
	//	必要なら確保
	Grow();
}


/*!
	確保
*/
void *CSimpleMemoryPool::Alloc(int size)
{
	//	初期化確認
	ASSERT(m_init);
	if(!m_init)
		return(NULL);

	//	確保サイズ計算
	int blockCount = size / m_blockSize;
	if(size % m_blockSize != 0)	//	割り切れない？
		blockCount++;

	//	サイズオーバーなので、外部に確保
	if(blockCount > m_growCount)
	{
		char *extBlock = new char[m_blockSize * blockCount];
		m_extBlocks.Add(extBlock);
		return (void *)extBlock;
	}

	//	一度確保を試みる
	void *ptr = TryAlloc(blockCount);
	if(ptr == NULL)
	{
		//	増加を試みる
		if(Grow())
			return(NULL);

		//	再度トライ
		ptr = TryAlloc(blockCount);
	}

	ASSERT(ptr != NULL);
	return(ptr);
}

/*!
	開放
*/
int CSimpleMemoryPool::TryFree(void *toFree)
{
	//	初期化確認
	ASSERT(m_init);
	if(!m_init)
		return(-1);

	//	外部ブロック扱いで開放してみる
	if(TryFreeExtBlock(toFree))
		return(0);

	//	解放
	if(TryFreeManaged(toFree))
		return(0);
	return(-1);
}


/*!
	開放
*/
void CSimpleMemoryPool::Free(void *toFree)
{
	//	解放
	ASSERT(TryFree(toFree) == 0);
}

/*!
	現在のチャンク数取得
*/
int CSimpleMemoryPool::GetChunkCount()
{
	return m_memoryPools.GetCount();
}


//////////////////////////////////////////////////////////////
//	補助
//////////////////////////////////////////////////////////////
/*!
	増大させる
*/
int CSimpleMemoryPool::Grow()
{
	//	必要？
	if(GetFreePoolCount() > 0)
		return(0);

	//	確保
	CSimpleMemoryPoolChunk *newPool = new CSimpleMemoryPoolChunk;
	if(newPool == NULL || newPool->Init(m_blockSize, m_growCount))
	{
		if(newPool)
			delete newPool;
		return(-1);
	}
	m_memoryPools.Add(newPool);

	return(0);
}

/*!
	減少させる
*/
int CSimpleMemoryPool::Slim()
{
	//	必要？
	if(GetFreePoolCount() <= 1)
		return(0);

	//	解放
	for(int i=0;i<m_memoryPools.GetCount();i++)
	{
		if(m_memoryPools[i]->GetAllocedCount() == 0)
		{
			delete m_memoryPools[i];
			m_memoryPools.RemoveAt(i);
			return(0);
		}
	}
	return(0);
}

/*!
	現在の完全空きプール数取得
*/
int CSimpleMemoryPool::GetFreePoolCount()
{
	int	count = 0;
	for(int i=0;i<m_memoryPools.GetCount();i++)
	{
		if(m_memoryPools[i]->GetAllocedCount() == 0)
			count++;
	}
	return(count);
}

/*!
	解放
*/
void CSimpleMemoryPool::Close()
{
	if(!m_init)
		return;

	//	開放
	for(int i=0;i<m_memoryPools.GetCount();i++)
		delete m_memoryPools[i];
	m_memoryPools.RemoveAll();

	m_init = 0;
}


/*!
	確保を試みる
*/
void *CSimpleMemoryPool::TryAlloc(int blockCount)
{
	//	確保を試みる
	for(int i=0;i<m_memoryPools.GetCount();i++)
	{
		void *ptr = m_memoryPools[i]->Alloc(blockCount);
		if(ptr)
			return(ptr);
	}

	return(NULL);
}


/*!
	もし拡張ブロックなら、解放する

	TRUE:解放した、FALSE:無い
*/
int CSimpleMemoryPool::TryFreeExtBlock(void *ptr)
{
	for(int i=0;i<m_extBlocks.GetCount();i++)
	{
		if(m_extBlocks[i] == (char *)ptr)
		{
			delete m_extBlocks[i];
			m_extBlocks.RemoveAt(i);
			return(TRUE);
		}
	}
	return(FALSE);
}


/*!
	解放を試みる

	TRUE:解放した、FALSE:無い
*/
int CSimpleMemoryPool::TryFreeManaged(void *ptr)
{
	//	解放を試みる
	for(int i=0;i<m_memoryPools.GetCount();i++)
	{
		if(m_memoryPools[i]->Free(ptr))
		{
			//	必要ならメモリプール開放
			if(m_memoryPools[i]->GetAllocedCount() == 0)
				Slim();
			return(TRUE);
		}
	}

	return(FALSE);
}
