/********************************************************************************
*	ファイル名：MemManager.cpp													*
*	用　　　途：メモリ管理クラス												*
*	備　　　考：																*	
*	履　　　歴：																*
********************************************************************************/
/****************************
*	 コンパイル環境設定		*
****************************/
/****************************
*	 プリコンパイル設定		*
****************************/
#include "stdafx.h"
/****************************
*	インクルードファイル	*
****************************/
#include "MemManager.h"
/****************************
*		 マクロ定数			*
****************************/
/****************************
*		 構造体定義			*
****************************/
/****************************
*		 クラス定義			*
****************************/
/****************************
*		 関数　宣言			*
****************************/
/****************************
*		 変数　宣言			*
****************************/
/********************************************************************************
*	関　数　名：コンストラクタ													*
*	用　　　途：																*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：																*
********************************************************************************/
CMemManager::CMemManager()
{
	m_nAllocKind	= CMEM_ALLOC_NONE;
	m_upMem			= 0;
	m_uSize			= 0;
	m_vpHeapHandle	= 0;
	ZeroMemory(&m_tPhysMemLock, sizeof m_tPhysMemLock);
}

/********************************************************************************
*	関　数　名:デストラクタ														*
*	用　　　途：																*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：																*
********************************************************************************/
CMemManager::~CMemManager()
{
	Free();
}

/********************************************************************************
*	関　数　名：SetAllocKind													*
*	用　　　途：メモリ取得方法設定												*
*	引　き　数：メモリの取得方法、管理対象メモリアドレス、メモリサイズ、		*
*				管理パラメータ格納アドレス										*
*	戻　り　数：正常終了=TRUE 異常終了=FALSE									*
*	備　　　考：すでにメモリが割り当てられている場合は設定しない。				*
*				メモリの取得方法が物理メモリの場合、							*
*				管理対象メモリおよびサイズをこのクラスに移管するためには		*
*				物理メモリ取得情報を管理パラメータとして渡す必要がある。		*
*				ヒープの場合は管理パラメータを省略するとGetProcessHeapで		*
*				取得したものと判断するのでプライベートヒープの場合は			*
*				管理パラメータにハンドルを渡すこと								*
********************************************************************************/
BOOL CMemManager::SetAllocKind(INT32 nAllocKind, PBYTE upPtr, SIZE_T uSize, PVOID vpInfo)
{
	/***** 前提条件 *****/
	if (!IsEmpty())					return FALSE;	/* 既に管理している			*/
	if (upPtr != 0 && uSize <= 0)	return FALSE;	/* 引数が壊れている			*/

	/***** ハンドル関連の初期化 *****/
	m_vpHeapHandle	= 0;
	ZeroMemory(&m_tPhysMemLock, sizeof m_tPhysMemLock);

	/***** 引数の展開 *****/
	switch(nAllocKind)
	{
	case CMEM_ALLOC_HEAP:
		/*
			ヒープハンドルが与えられていればそれを利用する。
			省略されていればプロセスヒープハンドルを利用する。
		*/
		m_vpHeapHandle = (vpInfo != 0) ? *(HANDLE *)vpInfo : 0;
		break;
	case CMEM_ALLOC_PHYS:
		if ( vpInfo != 0 )
		{
			m_tPhysMemLock	= *(PHYSMEMLOCK *)vpInfo;
		}
		else
		{
			if ( upPtr != 0 )
			{
				return FALSE;	/* 取得情報が与えられていない		*/
			}
		}
		break;
	case CMEM_ALLOC_VMEM:
	case CMEM_ALLOC_NONE:
	case CMEM_ALLOC_NEW:
	case CMEM_ALLOC_STATIC:
		break;
	default:
		return FALSE;
	}

	/***** 属性の設定 *****/
	m_nAllocKind = nAllocKind;

	if (upPtr)
	{
		m_upMem	= upPtr;
		m_uSize = uSize;
	}

	return TRUE;
}

/********************************************************************************
*	関　数　名：Alloc															*
*	用　　　途：メモリ取得														*
*	引　き　数：取得するメモリサイズ											*
*	戻　り　数：正常終了ときは、取得したメモリのポインタを返します。			*
*               異常終了の場合は、NULLを返します。								*
*	備　　　考：既にメモリが取得されている場合は失敗する。						*
*				取得されていない場合はメモリ取得方法に応じてメモリを確保する。	*
*				取得方法が静的メモリであれば何もせず成功とみなす				*
*				引数＝０は必ず失敗する。										*
********************************************************************************/
PBYTE CMemManager::Alloc(SIZE_T uSize)
{
	HANDLE	vpHandle;
	PVOID	vpPtr;

	if ( m_upMem != 0 || uSize == 0 ) return 0;

	switch (m_nAllocKind)
	{
	case CMEM_ALLOC_NEW:
		m_upMem = new BYTE[uSize];
		break;

	case CMEM_ALLOC_HEAP:
		vpHandle = (m_vpHeapHandle) ? m_vpHeapHandle : ::GetProcessHeap();

		m_upMem	= static_cast<PBYTE>(::HeapAlloc(vpHandle, 0, uSize));
		break;

	case CMEM_ALLOC_PHYS:
				
		m_tPhysMemLock.uDemandSize = uSize;
		if (::PhysicalMemoryLock(&m_tPhysMemLock))
		{
			m_upMem = static_cast<PBYTE>(m_tPhysMemLock.vpReserved);
		}
		break;
	case CMEM_ALLOC_VMEM:

		vpPtr = VirtualAlloc(0, uSize, MEM_COMMIT, PAGE_READWRITE);
		if ( vpPtr )
		{
			if ( !VirtualLock(vpPtr, uSize) )
			{
				VirtualFree(vpPtr, 0, MEM_RELEASE);
				vpPtr = 0;
			}
		}
		m_upMem = (PBYTE)vpPtr;
		break;
	default:
		break;
	}

	if ( m_upMem )
	{
		ZeroMemory(m_upMem, uSize);
		m_uSize = uSize;
	}

	return m_upMem;
}

/********************************************************************************
*	関　数　名：Realloc															*
*	用　　　途：メモリサイズ変更												*
*	引　き　数：メモリ再確保のサイズ、ヒープフラグ、オプション					*
*	戻　り　数：変更成功でメモリアドレス、失敗時はＮＵＬＬ						*
*	備　　　考：オプションはヒープ取得時のみ設定参照する						*
*				newまたは物理メモリのサイズ変更時はアドレスが変わる。			*
*				ヒープ時はヒープフラグに依存する。								*
*				オプション内容についてはMSDNでHeapReallocを参照すること。		*
*				静的メモリは変更できない。										*
*				失敗時には変更前に確保したメモリは保証される					*
*				引数＝０は必ず失敗する。										*
********************************************************************************/
PBYTE CMemManager::Realloc(SIZE_T uSize, DWORD dwFlags, INT32 nOption)
{
	HANDLE		vpHandle;
	PHYSMEMLOCK tPhysMem;
	PBYTE		upMem;
	SIZE_T		uCopySize;
	SIZE_T		uMySize;
	PVOID		vpPtr;

	if ( uSize == 0 ) return 0;

	uMySize = Size();

	/***** サイズに変更が無い場合は何もしない *****/
	if ( uSize == uMySize )
	{
		return GetPtr();
	}

	/***** 空であれば新規確保 *****/
	if ( IsEmpty() )
	{
		return Alloc(uSize);
	}

	/***** 現状が要求サイズを十分に満たす場合は現状維持 *****/
	if (nOption == CMEM_REALLOC_LT && uMySize > uSize )
	{
		return GetPtr();
	}

	/***** サイズ変更処理 *****/
	switch (m_nAllocKind)
	{
	case CMEM_ALLOC_NEW:
		upMem		= new BYTE[uSize];
		if ( upMem )
		{
			ZeroMemory(upMem, uSize);
			uCopySize	= (uSize < m_uSize) ? uSize : m_uSize;
			memcpy(upMem, m_upMem, uCopySize);
			Free();
		}
		break;

	case CMEM_ALLOC_HEAP:
		vpHandle	= (m_vpHeapHandle) ? m_vpHeapHandle : ::GetProcessHeap();
		upMem		= (PBYTE)::HeapReAlloc(vpHandle, dwFlags, m_upMem, uSize);
		break;

	case CMEM_ALLOC_PHYS:
		tPhysMem.uDemandSize	= uSize;
		if (::PhysicalMemoryLock(&tPhysMem))
		{
			upMem		= (PBYTE)tPhysMem.vpReserved;

			ZeroMemory(upMem, uSize);

			uCopySize	= (uSize < m_uSize) ? uSize : m_uSize;
			memcpy(upMem, m_upMem, uCopySize);

			Free();
			m_tPhysMemLock = tPhysMem;
		}
		break;

	case CMEM_ALLOC_VMEM:
		vpPtr = VirtualAlloc(0, uSize, MEM_COMMIT, PAGE_READWRITE);
		if ( vpPtr )
		{
			if ( !VirtualLock(vpPtr, uSize) )
			{
				VirtualFree(vpPtr, 0, MEM_RELEASE);
				upMem = 0;
			}
			else
			{
				upMem		= (PBYTE)vpPtr;
				ZeroMemory(upMem, uSize);

				uCopySize	= (uSize < m_uSize) ? uSize : m_uSize;
				memcpy(upMem, m_upMem, uCopySize);

				Free();
			}
		}

		break;
	default:
		upMem = 0;
		break;
	}

	/***** 成功で保持情報更新 *****/
	if (upMem)
	{
		m_upMem	= upMem;
		m_uSize = uSize;
	}

	return upMem;
}

/********************************************************************************
*	関　数　名：ReallocNe														*
*	用　　　途：メモリサイズ変更												*
*	引　き　数：メモリ再確保のサイズ											*
*	戻　り　数：変更成功でメモリアドレス、失敗時はＮＵＬＬ						*
*	備　　　考：ReallocをCMEM_REALLOC_NE引数で呼び出す							*
********************************************************************************/
PBYTE CMemManager::ReallocNe(SIZE_T uSize)
{
	return Realloc(uSize, HEAP_ZERO_MEMORY, CMEM_REALLOC_NE);
}

/********************************************************************************
*	関　数　名：ReallocLt														*
*	用　　　途：メモリサイズ変更												*
*	引　き　数：メモリ再確保のサイズ											*
*	戻　り　数：変更成功でメモリアドレス、失敗時はＮＵＬＬ						*
*	備　　　考：ReallocをCMEM_REALLOC_LT引数で呼び出す							*
********************************************************************************/
PBYTE CMemManager::ReallocLt(SIZE_T uSize)
{
	return Realloc(uSize, HEAP_ZERO_MEMORY, CMEM_REALLOC_LT);
}

/********************************************************************************
*	関　数　名：Free															*
*	用　　　途：メモリ開放														*
*	引　き　数：正常終了=TRUE、異常終了=FALSE									*
*	戻　り　数：																*
*	備　　　考：メモリを確保していない状態で呼び出された場合は成功とみなす		*
*				異常終了時は復旧はできない（致命的エラー）						*
********************************************************************************/
BOOL CMemManager::Free()
{
	HANDLE	vpHandle;
	BOOL	bRet;

	if(m_upMem == 0) return TRUE;

	switch (m_nAllocKind)
	{
	case CMEM_ALLOC_NEW:
		delete[] m_upMem;
		bRet	= TRUE;
		break;
	case CMEM_ALLOC_HEAP:		//ヒープ領域の開放
		vpHandle = (m_vpHeapHandle) ? m_vpHeapHandle : ::GetProcessHeap();
		bRet	= ::HeapFree(vpHandle, 0, m_upMem);
		break;
	case CMEM_ALLOC_PHYS:		//物理メモリの開放
		bRet	= ::PhysicalMemoryUnlock(&m_tPhysMemLock);
		break;
	case CMEM_ALLOC_VMEM:
		bRet  = VirtualUnlock(m_upMem, m_uSize);
		bRet &= VirtualFree(m_upMem, 0, MEM_RELEASE);
		break;
	case CMEM_ALLOC_STATIC:
		bRet	= TRUE;
		break;
	default:					//管理対象外
		bRet	= FALSE;
		break;
	}

	if ( bRet )
	{
		m_upMem	= 0;
		m_uSize = 0;
	}
	else
	{
		/***** 自オブジェクトが破壊されている *****/
		ASSERT(bRet);
	}

	return bRet;
}

/********************************************************************************
*	関　数　名：Copy															*
*	用　　　途：メモリオブジェクトのコピー										*
*	引　き　数：コピー先、コピーサイズ											*
*	戻　り　数：コピー成功でＴＲＵＥ、失敗でＦＡＬＳＥ							*
*	備　　　考：コピー先は未初期化の場合は、コピー元と同じメモリ取得方法が		*
*				選択されるがコピー元が静的メモリであればヒープに代替される		*
*				(コピー元とコピー先が同一アドレスになってしまうのを防ぐため）	*
*				コピー先がコピー元のデータサイズより小さい場合は拡張されるが	*
*				大きい場合には縮小はしない										*
********************************************************************************/
BOOL CMemManager::Copy(CMemManager& dstMem, SIZE_T uSize)
{
	SIZE_T	uMemSize;
	PBYTE	upPtr;
	INT32	nKind;

	if ( m_upMem == 0 ) return FALSE;

	/***** nSizeが省略された場合、コピー元と同じサイズとする *****/
	if ( uSize == 0 )
	{
		uSize = m_uSize;
	}

	/***** 移動先が未初期化の場合は同一種別で初期化する *****/
	if ( dstMem.IsEmpty() )
	{
		switch(m_nAllocKind)
		{
		case CMEM_ALLOC_STATIC:
			/***** 静的メモリのコピーは作れないのでヒープを代替として利用する *****/
			nKind	= CMEM_ALLOC_HEAP;
			break;
		case CMEM_ALLOC_HEAP:
			/*
				ヒープハンドルはどこで破棄されるかわからないので
				コピー元のハンドルは利用せずにプロセスヒープとする。
			*/
		default:
			nKind	= m_nAllocKind;
			break;
		}

		dstMem.SetAllocKind(nKind);
	}

	/***** メモリ確保 *****/
	upPtr		= dstMem.Realloc(uSize, HEAP_ZERO_MEMORY, CMEM_REALLOC_LT); 
	uMemSize	= dstMem.Size();
	ZeroMemory(upPtr, uMemSize);

	/***** コピー先メモリ取得失敗 *****/
	if ( upPtr == 0 ) return FALSE;

	/***** コピー *****/
	if ( m_uSize < uSize )
	{
		uSize = m_uSize;
	}

	memcpy(upPtr, m_upMem, uSize);

	return  TRUE;
}

/********************************************************************************
*	関　数　名：Move															*
*	用　　　途：メモリ内容の移動												*
*	引　き　数：移動先															*
*	戻　り　数：正常終了=TRUE、異常終了=FALSE									*
*	備　　　考：																*
********************************************************************************/
BOOL CMemManager::Move(CMemManager& dstMem)
{
	PVOID	vpInfo;
	BOOL	bRet;

	if (m_upMem == 0) return FALSE;

	/***** 移動先が未初期化の場合は状態を移管する *****/
	if ( dstMem.IsEmpty() )
	{
		switch(m_nAllocKind)
		{
		case CMEM_ALLOC_PHYS:
			vpInfo = (PVOID)&m_tPhysMemLock;
			break;
		case CMEM_ALLOC_HEAP:
			vpInfo = (PVOID)&m_vpHeapHandle;
			break;
		default:
			vpInfo = 0;
			break;
		}
		bRet = dstMem.SetAllocKind(m_nAllocKind, m_upMem, m_uSize, vpInfo);
	}
	else
	{
		/***** 移動先が既に初期化されている場合はコピー扱い *****/
		bRet = Copy(dstMem);
	}

	/***** 移管成功時は自身の内容は破棄する *****/
	if ( bRet )
	{
		Free();
	}
	
	return bRet;
}

/********************************************************************************
*	関　数　名：GetPtr															*
*	用　　　途：メモリアドレスの取得											*
*	引　き　数：																*
*	戻　り　数：メモリアドレス													*
*	備　　　考：																*
********************************************************************************/
PBYTE CMemManager::GetPtr()
{
	return m_upMem;
}

/********************************************************************************
*	関　数　名：GetAllocKind													*
*	用　　　途：メモリ取得方法の取得											*
*	引　き　数：																*
*	戻　り　数：メモリ取得方法													*
*	備　　　考：																*
********************************************************************************/
INT32 CMemManager::GetAllocKind()
{
	return m_nAllocKind;
}

/********************************************************************************
*	関　数　名：Size															*
*	用　　　途：メモリサイズを取得												*
*	引　き　数：																*
*	戻　り　数：メモリサイズ													*
*	備　　　考：																*
********************************************************************************/
SIZE_T CMemManager::Size()
{
	return m_uSize;
}

/********************************************************************************
*	関　数　名：IsEmpty															*
*	用　　　途：未設定状態かを取得												*
*	引　き　数：																*
*	戻　り　数：未設定であればＴＲＵＥ、何かしら設定されていればＦＡＬＳＥ		*
*	備　　　考：																*
********************************************************************************/
BOOL CMemManager::IsEmpty()
{
	return (m_upMem == 0 && m_uSize == 0);
}

/********************************************************************************
*	関　数　名：Clear															*
*	用　　　途：メモリをゼロクリア												*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：																*
********************************************************************************/
void CMemManager::Clear()
{
	if ( !IsEmpty() )
	{
		ZeroMemory(m_upMem, m_uSize);
	}
}