/*******************************************************************************
*	ファイル名：Mem.cpp
*	用　　　途：メモリ管理クラス実装
*	備　　　考：
*	履　　　歴：
********************************************************************************/

/****************************
*	インクルードファイル	*
****************************/
#include "StdAfx.h"
#include "Mem.h"


/*******************************************************************************
*	関　数　名：SetLFH
*	用　　　途：対象のヒープをメモリ断片化抑止ヒープにする
*	引　き　数：対象ヒープハンドル(NULLの場合はプロセスヒープ)
*	戻　り　値：
*	備　　　考：※Debugger上で実行している場合は、LFH化されません
*				  ★Debugモード/Releaseモードのことではなく、IDEで「デバッグ
*					開始」で実行している場合です
********************************************************************************/
BOOL SetLFH( HANDLE hHeap )
{
	ULONG	ulValue = 2 ;		// LFHに設定するためのフラグ値

	if( hHeap == NULL){
		hHeap = GetProcessHeap() ;
	}

	return ::HeapSetInformation(  hHeap
							   ,  HeapCompatibilityInformation
							   , &ulValue
							   ,  sizeof(ulValue)
							   ) ;
}


/*******************************************************************************
*	関　数　名：MemAlloc
*	用　　　途：64bit境界アライメント済みヒープ領域の割り当て
*	引　き　数：確保サイズ
*	戻　り　値：成功時(確保されたメモリへのポインタ)/失敗時(NULL)
*	備　　　考：
********************************************************************************/
void* MemAlloc( SIZE_T uiSize )
{
	if ( uiSize == 0 )	return NULL ;

	return _aligned_malloc( uiSize, MEM_ALIGNED ) ;
}


/*******************************************************************************
*	関　数　名：MemRealloc
*	用　　　途：64bit境界アライメント済みヒープ領域の割り当て・再割り当て
*	引　き　数：確保対象ポインタ、確保サイズ、初期化フラグ
*	戻　り　値：成功時(確保されたメモリへのポインタ)/失敗時(NULL)
*	備　　　考：ポインタがNULLの場合
*				初期化フラグ=TRUEの場合は、確保されたすべてのメモリ領域
*				が0リセットされます（追加分のみではありません)
********************************************************************************/
void* MemPrealloc( void** ppMemory, SIZE_T uiSize, BOOL bInit )
{
	ASSERT( ppMemory != NULL ) ;

	void*	pRes = NULL ;

	if ( uiSize==0 && *ppMemory!=NULL ) { 
		_aligned_free( *ppMemory ) ;
		*ppMemory = NULL ;
	}
	else if ( *ppMemory == NULL ) { 
		pRes = _aligned_malloc( uiSize, MEM_ALIGNED ) ;
		if ( pRes != NULL ) { 
			::ZeroMemory( pRes, uiSize ) ;
			*ppMemory = pRes ;
		}
	}
	else { 
		pRes = _aligned_realloc( *ppMemory, uiSize, MEM_ALIGNED ) ;
		if ( pRes != NULL ) { 
			::ZeroMemory( pRes, uiSize ) ;
			*ppMemory = pRes ;
		}
	}

	return pRes ;
}


/*******************************************************************************
*	関　数　名：MemFree
*	用　　　途：MemAllocで確保された領域の解放
*	引　き　数：解放対象メモリポインタ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL MemFree( void* pMem )
{
	if ( pMem == NULL )		return FALSE ;

	_aligned_free( pMem ) ;
	return TRUE ;
}


/*******************************************************************************
*	関　数　名：VAlloc
*	用　　　途：簡易VirtualAlloc
*	引　き　数：確保サイズ、初期化フラグ
*	戻　り　値：成功時(確保されたメモリへのポインタ)/失敗時(NULL)
*	備　　　考：仮想メモリからのメモリ確保とページロックを行う
********************************************************************************/
void* VAlloc( SIZE_T uiSizeBytes, BOOL bInit )
{
	BOOL	bResult ;
	void*	pMem = NULL ;

	do // once 
	{
		// メモリ領域の予約と確保
		pMem = VirtualAlloc( NULL, uiSizeBytes, MEM_COMMIT, PAGE_READWRITE ) ;
		if ( pMem == NULL ) { 
			break ;
		}

		// 確保した領域をページロックする
		bResult = VirtualLock( pMem, uiSizeBytes ) ;
		if ( !bResult ) { 
			TRACE( _T("Error(%d)\n"), GetLastError() ) ;
			::VirtualFree( pMem, 0, MEM_RELEASE ) ;
			pMem = NULL ;
			break ;
		}

		// 初期化
		if ( bInit ) { 
			::ZeroMemory( pMem, uiSizeBytes ) ;
		}
	} while ( FALSE ) ;

	return pMem ;
}


/*******************************************************************************
*	関　数　名：VFree
*	用　　　途：VAllocで確保された領域の解放
*	引　き　数：解放対象メモリポインタ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：ページロック解除とメモリ解放を行う
********************************************************************************/
BOOL VFree( void* pMem )
{
	if ( pMem == NULL )		return FALSE ;

	BOOL	bRet = FALSE ;
	SIZE_T	uiQueryBytes ;

	MEMORY_BASIC_INFORMATION tMemInfo ;

	do // once
	{
		// メモリ情報を問い合わせて、確保済みサイズを取得する
		uiQueryBytes = VirtualQuery( pMem, &tMemInfo, sizeof(tMemInfo) ) ;
		if ( uiQueryBytes < sizeof(tMemInfo) ) { 
			break ;
		}

		// サイズ分ロック解除
		bRet = VirtualUnlock( pMem, tMemInfo.RegionSize ) ;
		if ( !bRet ) { 
			break ;
		}

		// メモリを仮想メモリに返却
		bRet = VirtualFree( pMem, 0, MEM_RELEASE ) ;
	} while ( FALSE ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：CMemBuffer
*	用　　　途：コンストラクタ
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
CMemBuffer::CMemBuffer()
{
	// メモリ割り当て単位の初期設定
	SubInitAllocUnits() ;

	InitMember() ;
}


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


/*******************************************************************************
*	関　数　名：InitMember
*	用　　　途：メンバー変数の初期化を行う
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CMemBuffer::InitMember()
{
	m_uiBlockBytes		= 0 ;
	m_nBlockCount		= 0 ;
	m_uiAlignBlockBytes = 0 ;
	m_nSectorCount		= 0 ;
	m_nMaxCountInSector	= 0 ;
	m_paSectors			= NULL ;
}


/*******************************************************************************
*	関　数　名：SubInitAllocUnits
*	用　　　途：メモリ割り当て単位の初期化
*	引　き　数：
*	戻　り　値：
*	備　　　考：VirtualAlloc割り当て単位及びセクター割り当て基準単位をシステム
*				から取得する
********************************************************************************/
void CMemBuffer::SubInitAllocUnits()
{
	SYSTEM_INFO		tSysInfo ;

	::GetSystemInfo( &tSysInfo ) ;

	// VirtualAllocからのメモリ割り当て単位(現在64kB)
	m_uVMemUnitBytes	= tSysInfo.dwAllocationGranularity ;
	// セクター割り当て基準単位(現在、64k*64=4MB) 
	m_uStdSectorBytes	= m_uVMemUnitBytes * SECTOR_ALLOC_MULTIPLY ;
}


/*******************************************************************************
*	関　数　名：Alloc
*	用　　　途：複数ブロック領域を割り当てる
*	引　き　数：ブロックサイズ、ブロック数、初期化フラグ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CMemBuffer::Alloc( SIZE_T	uiBlockBytes
					  , INT32	nBlockCount
					  , BOOL	bInit
					  )
{
	// 事前条件
	if ( uiBlockBytes == 0    )				return FALSE ;
	if ( nBlockCount  <= 0    )				return FALSE ;
	if ( m_paSectors  != NULL )				return FALSE ;

	BOOL	bRet ;

	// 実際の割り当て処理
	bRet = DoAlloc( uiBlockBytes, nBlockCount, bInit ) ;
	if ( !bRet ) { 
		// 失敗時のロールバック
		DoFree() ;
		InitMember() ;
	}

	return bRet ;
}


/*******************************************************************************
*	関　数　名：Free
*	用　　　途：ブロックの解放
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CMemBuffer::Free()
{
	BOOL	bRet ;

	// 解放処理
	bRet = DoFree() ;
	if ( bRet ) { 
		InitMember() ;
	}

	return bRet ;
}


/*******************************************************************************
*	関　数　名：Setup
*	用　　　途：メモリのセットアップ(新規割り当て、再割り当て)
*	引　き　数：ブロックサイズ、ブロック数、初期化フラグ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：再割り当て後の状態は、初期化フラグを指定されていない場合
*				不定になる
********************************************************************************/
BOOL CMemBuffer::Setup( SIZE_T	uiBlockBytes
					  , INT32	nBlockCount
					  , BOOL	bInit
					  )
{
	// 事前条件
	if ( uiBlockBytes == 0    )				return FALSE ;
	if ( nBlockCount  <= 0    )				return FALSE ;

	BOOL	bRet ;

	do // once
	{
		// サイズ変更がない場合は初期化のみ
		if ( uiBlockBytes == m_uiBlockBytes && 
			 nBlockCount  == m_nBlockCount ) { 
			if ( bInit ) { 
				SubClearSectors() ;
			}
			bRet = TRUE ;
			break ;
		}

		// 既に割り当て済みならば一旦メモリ解放
		DoFree() ;

		// 再割り当て
		bRet = DoAlloc( uiBlockBytes, nBlockCount, bInit ) ;
		if ( !bRet ) { 
			DoFree() ;
			InitMember() ;
		}
	} while ( FALSE ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：DoCalcMaxSectorBytes
*	用　　　途：最大セクターサイズを計算する
*	引　き　数：ブロック数(8byteアライメント済み)、ブロック数
*	戻　り　値：最大セクターサイズ
*	備　　　考：
********************************************************************************/
SIZE_T CMemBuffer::DoCalcMaxSectorBytes( SIZE_T uiAlignedBlockBytes, INT32 nBlockCount )
{
	// アライメント済み総ブロックサイズ
	SIZE_T		uiTotalAlignedBlockBytes = uiAlignedBlockBytes * nBlockCount ;

	// 総ブロックサイズがVirtualAlloc割り当て単位以下の場合は、
	// １セクターですべて確保する
	// (後でヒープメモリに割り当てる)
	if ( uiTotalAlignedBlockBytes < m_uVMemUnitBytes ) { 
		return uiTotalAlignedBlockBytes ;
	}

	// VirtualAllocでのメモリ割り当て単位でアライメント
	SIZE_T		uiBlockBytesVMem	= IALIGN( uiAlignedBlockBytes, m_uVMemUnitBytes ) ;

	// １ブロックで既に基準セクター単位以上の量が必要な場合は
	// VirtualAllocでの割り当て単位でアライメントされたブロックサイズを
	// そのまま最大セクターサイズとする
	if ( uiBlockBytesVMem >= m_uStdSectorBytes ) { 
		return uiBlockBytesVMem ;
	}

	// 最終セクターインデックス計算
	// (セクター数が本来必要な値だが、１セクターしかいらないかどうか判断するだけ
	//  なので切り捨てして、セクターインデックスとして計算する)
	SIZE_T		uiLastSector	= uiTotalAlignedBlockBytes / m_uStdSectorBytes ;

	// 最終セクターインデックス=0 = セクターが１つしかない
	// 場合は、VirtualAllocでの割り当て単位でアライメントされた総ブロックサイズを
	// そのまま最大セクターサイズとする
	if ( uiLastSector == 0 ) { 
		return IALIGN( uiTotalAlignedBlockBytes, m_uVMemUnitBytes ) ;
	}

	// 総ブロックサイズから基準セクターサイズを割った余りが
	// 少しだけ(VirtualAlloc1割り当て未満)残った場合は、
	// 基準セクターサイズをそのまま最大セクターサイズとする
	SIZE_T		uiMod	= uiTotalAlignedBlockBytes % m_uStdSectorBytes ;

	if ( (m_uStdSectorBytes - uiMod) < m_uVMemUnitBytes ) { 
		return (SIZE_T)m_uStdSectorBytes ;
	}

	// 最後に基準セクターサイズ未満で、最大の連続ブロックサイズ
	// を最大セクターサイズとする
	SIZE_T		uiSectorInCount = m_uStdSectorBytes / uiAlignedBlockBytes ;

	return IALIGN( uiSectorInCount*uiAlignedBlockBytes, m_uVMemUnitBytes ) ; 
}


/*******************************************************************************
*	関　数　名：DoAlloc
*	用　　　途：ブロック領域の割り当てメイン処理
*	引　き　数：ブロックサイズ、ブロック数
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CMemBuffer::DoAlloc( SIZE_T uiBlockBytes
						, INT32	 nBlockCount 
						, BOOL	 bInit
						)
{
	INT32		nRemember ;				// 残り割り当てブロックカウント
	void*		pMem ;					// 割り当てメモリ領域
	INT32		i ;
	SIZE_T		uiMaxSectorBytes ;		// 最大セクターサイズ計算値
	SIZE_T		uiLastSectorBytes ;		// 残りセクターサイズ


	// 初期メモリ計算・設定
	m_uiBlockBytes		= uiBlockBytes ;
	m_nBlockCount		= nBlockCount ;
	// アライメント済みサイズの計算
	m_uiAlignBlockBytes	= IALIGN( m_uiBlockBytes, MEM_ALIGNED ) ;
	uiMaxSectorBytes	= DoCalcMaxSectorBytes( m_uiAlignBlockBytes, m_nBlockCount ) ;
	m_nMaxCountInSector	= (INT32)( uiMaxSectorBytes / m_uiAlignBlockBytes ) ;
	m_nSectorCount		= divc( m_nBlockCount, m_nMaxCountInSector ) ;

	// セクター情報をヒープに確保する
	m_paSectors			= (MEM_SECTOR*)MemAlloc( sizeof(MEM_SECTOR) * m_nSectorCount ) ;
	if ( m_paSectors == NULL ) { 
		return FALSE ;
	}
	ZeroMemory( m_paSectors, sizeof(MEM_SECTOR) * m_nSectorCount ) ;

	// 最後のブロックは、あまりのサイズを設定するため
	// それを除いて確保
	nRemember = m_nBlockCount ;
	for ( i=0 ; i<(m_nSectorCount - 1) ; i++ ) { 
		// 仮想メモリ領域よりセクター領域確保
		pMem = VAlloc( uiMaxSectorBytes ) ;
		if ( pMem == NULL ) { 
			return FALSE ;
		}

		// セクター情報設定
		m_paSectors[i].pMem			= (BYTE*)pMem ;
		m_paSectors[i].uiAllocBytes	= uiMaxSectorBytes ;
		m_paSectors[i].nAllocCount	= m_nMaxCountInSector ;
		nRemember				   -= m_nMaxCountInSector ;
		if ( bInit ) { 
			::ZeroMemory( pMem, uiMaxSectorBytes ) ;
		}
	}

	// 最後のセクターの割り当て
	// 総割り当てサイズがm_uVMemUnitBytesより小さい場合
	// (最後=最初の割り当てで、m_uVMemUnitBytes未満)
	// はヒープに割り当てる
	uiLastSectorBytes = nRemember * m_uiAlignBlockBytes ;
	if ( i!=0 || uiLastSectorBytes >= m_uVMemUnitBytes ) { 
		pMem		= VAlloc  ( uiLastSectorBytes ) ;
	}
	else { 
		pMem		= MemAlloc( uiLastSectorBytes ) ;
	}
	if ( pMem == NULL ) { 
		return FALSE ;
	}

	// 最後のセクターの情報設定
	m_paSectors[i].pMem			= (BYTE*)pMem ;
	m_paSectors[i].uiAllocBytes	= uiLastSectorBytes ;
	m_paSectors[i].nAllocCount	= nRemember ;
	if ( bInit ) { 
		::ZeroMemory( pMem, uiLastSectorBytes ) ;
	}

	return TRUE ;
}


/*******************************************************************************
*	関　数　名：DoFree
*	用　　　途：ブロック領域の解放メイン処理
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CMemBuffer::DoFree()
{
	if ( m_paSectors == NULL )	return FALSE ;

	BOOL		bRet = TRUE ;
	MEM_SECTOR*	pLastSector ;		// 最後のセクター
	INT32		i ;

	// 最後のセクターのみ処理が別
	pLastSector = &m_paSectors[m_nSectorCount-1] ;
	if ( pLastSector->pMem != NULL ) { 
		// 総割り当てサイズがm_uVMemUnitBytesより小さい場合
		// (最後=最初の割り当てで、m_uVMemUnitBytes未満)
		// はヒープに割り当てられているので、個別に解放
		if ( m_nSectorCount>1 || pLastSector->uiAllocBytes>=m_uVMemUnitBytes ) { 
			bRet &= VFree( pLastSector->pMem ) ;
		}
		else { 
			bRet &= MemFree( pLastSector->pMem ) ;
		}
	}

	// 最後のセクター以降の割り当て解放
	for ( i=m_nSectorCount-2 ; i>=0 ; i-- ) { 
		bRet   &= VFree( m_paSectors[i].pMem ) ;
	}

	// セクター配列の解放
	bRet &= MemFree( m_paSectors ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：SubClearSectors
*	用　　　途：セクター内データのクリア
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CMemBuffer::SubClearSectors()
{
	INT32		i ;
	MEM_SECTOR*	pSector ;

	for ( i=0 ; i<m_nSectorCount ; i++ ) { 
		pSector = &m_paSectors[i] ;
		::ZeroMemory( pSector->pMem, pSector->uiAllocBytes ) ;
	}
}


/*******************************************************************************
*	関　数　名：GetPtr
*	用　　　途：割り当て済みブロックへのポインタ取得
*	引　き　数：
*	戻　り　値：
*	備　　　考：ブロック番号が個数をオーバーする場合はNULLが返される
********************************************************************************/
BYTE* CMemBuffer::GetPtr( INT32 nBlockNo )
{
	ASSERT( nBlockNo >= 0 ) ;
	if ( nBlockNo >= m_nBlockCount ) return NULL ;

	// ブロック番号からセクター番号及び、セクター内ブロック番号
	// を逆算する
	INT32	nSectorNo		= nBlockNo / m_nMaxCountInSector ;
	INT32	nSectorInBlock	= nBlockNo % m_nMaxCountInSector ;

	// 
	return m_paSectors[nSectorNo].pMem + (nSectorInBlock * m_uiAlignBlockBytes) ;
}


/*******************************************************************************
*	関　数　名：operator[]
*	用　　　途：インデクサオペレータ
*	引　き　数：ブロック番号
*	戻　り　値：割り当て済みブロックへのポインタ
*	備　　　考：ブロック番号が間違っている場合はASSERTに失敗する
********************************************************************************/
BYTE* CMemBuffer::operator[]( INT32 nBlockNo )
{
	ASSERT( nBlockNo>=0 && nBlockNo<m_nBlockCount ) ;
	ASSERT( m_paSectors != NULL ) ;

	// ブロック番号からセクター番号及び、セクター内ブロック番号
	// を逆算する
	INT32	nSectorNo		= nBlockNo / m_nMaxCountInSector ;
	INT32	nSectorInBlock	= nBlockNo % m_nMaxCountInSector ;

	return m_paSectors[nSectorNo].pMem + (nSectorInBlock * m_uiAlignBlockBytes) ;
}


/*******************************************************************************
*	関　数　名：Clear
*	用　　　途：対象ブロックをクリアする
*	引　き　数：負数の場合はすべてのブロックをクリア
*	戻　り　値：
*	備　　　考：ブロック番号が個数をオーバーする場合はNULLが返される
********************************************************************************/
BOOL CMemBuffer::Clear( INT32 nBlockNo )
{
	if ( nBlockNo >= m_nBlockCount )	return FALSE ;
	if ( m_paSectors == NULL )			return FALSE ;

	if ( nBlockNo < 0 ) { 
		// 全ブロック・セクタークリア
		SubClearSectors() ;
	}
	else { 
		// ブロック番号からセクター番号及び、セクター内ブロック番号
		// を逆算し、対象ブロックをクリアする
		INT32	nSectorNo		= nBlockNo / m_nMaxCountInSector ;
		INT32	nSectorInBlock	= nBlockNo % m_nMaxCountInSector ;
		BYTE*	pMemory			= m_paSectors[nSectorNo].pMem
								+ (nSectorInBlock * m_uiAlignBlockBytes) ;

		::ZeroMemory( pMemory, m_uiAlignBlockBytes ) ;
	}

	return TRUE ;

}

/*******************************************************************************
*	関　数　名：BlockBytes
*	用　　　途：ブロックサイズを取得
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
SIZE_T CMemBuffer::BlockBytes() const 
{
	return m_uiBlockBytes ;
}


/*******************************************************************************
*	関　数　名：AlignBytes
*	用　　　途：アライメント済みブロックサイズを取得
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
SIZE_T CMemBuffer::AlignBytes() const 
{
	return m_uiAlignBlockBytes ;
}


/*******************************************************************************
*	関　数　名：BlockCount
*	用　　　途：ブロック数取得
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
INT32 CMemBuffer::BlockCount() const 
{
	return m_nBlockCount ;
}


/*******************************************************************************
*	関　数　名：IsValidBlock
*	用　　　途：対象ブロックポインタが有効かどうか確認する
*	引　き　数：
*	戻　り　値：有効(TRUE) / 異常(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CMemBuffer::IsValidBlock( BYTE* pbyBlock ) const 
{
	if ( m_paSectors == NULL )	return FALSE ;

	INT32		i, j, n ;
	BYTE*		pbyBlockInSector ;

	for ( i=0 ; i<m_nSectorCount ; i++ ) { 
		pbyBlockInSector	= m_paSectors[i].pMem ;
		n					= m_paSectors[i].nAllocCount ;
		for ( j=0 ; j<n ; j++ ) { 
			// 一致するブロック有り
			if ( pbyBlockInSector == pbyBlock ) { 
				return TRUE ;
			}
			pbyBlockInSector += m_uiAlignBlockBytes ;
		}
	}

	// ここまでループしていれば一致するブロックはなかった
	return FALSE ;
}





///-------------------------------------------------------------------------------------------------

/*******************************************************************************
*	関　数　名：CBlockedMemory
*	用　　　途：コンストラクタ
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
CBlockedMemory::CBlockedMemory()
: m_BlockBuffer()
{
	m_pSListHeader = NULL ;
	
	InitMember() ;
}


/*******************************************************************************
*	関　数　名：~CBlockedMemory
*	用　　　途：デストラクタ
*	引　き　数：
*	戻　り　値：
*	備　　　考：全メモリ解放
********************************************************************************/
CBlockedMemory::~CBlockedMemory()
{
	if ( m_pSListHeader != NULL ) { 
		SubFreeAll() ;
		_aligned_free( m_pSListHeader ) ;
	}
}


/*******************************************************************************
*	関　数　名：InitMember
*	用　　　途：メンバー変数の初期化
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CBlockedMemory::InitMember()
{
	m_uiBlockSize	= 0 ;
	m_usCount		= 0 ;
}


/*******************************************************************************
*	関　数　名：Setup
*	用　　　途：ブロックメモリの設定を行う
*	引　き　数：割り当てブロックサイズ、最大割り当て可能個数
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：初期設定・再割り当てを行う
*				※同期化していないので注意
********************************************************************************/
BOOL CBlockedMemory::Setup( SIZE_T uiBlockSize, USHORT usCount )
{
	// サイズ指定が0の場合は、解放する
	if ( uiBlockSize*usCount == 0 ) { 
		return Destroy( FALSE ) ;
	}

	BOOL	bRet = FALSE ;

	do // once
	{
		// シングルリンクリストヘッダ作成
		if ( m_pSListHeader == NULL ) { 
			m_pSListHeader = (SLIST_HEADER*)_aligned_malloc( sizeof(SLIST_HEADER)
														   , MEM_ALIGNED 
														   ) ;
			if ( m_pSListHeader == NULL ) { 
				break ;
			}
			
			::InitializeSListHead( m_pSListHeader ) ;
		}

		// リンクリスト内に使用個数がすべて残っていれば未使用
		if ( ::QueryDepthSList(m_pSListHeader) != m_usCount ) { 
			break ;
		}

		// サイズに変更がなければ初期化のみ
		if ( m_uiBlockSize==uiBlockSize && m_usCount==usCount ) { 
			SubInitBlocks() ;
			bRet = TRUE ;
			break ;
		}

		// 一旦全メモリを解放する
		if ( m_BlockBuffer.BlockCount() > 0 ) {
			bRet = SubFreeAll() ;
			if ( !bRet ) { 
				break ;
			}
		}

		// 新規割り当てサイズ設定
		m_uiBlockSize	= uiBlockSize ;
		m_usCount		= usCount ;

		// メモリブロックの割り当て
		bRet = SubAllocBlocks() ;
		if ( !bRet ) { 
			SubFreeAll() ;
			InitMember() ;
			break ;
		}

		// リスト内状態を初期化する
		SubInitBlocks() ;
	} while ( FALSE ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：Reset
*	用　　　途：メモリ使用状態の強制リセット
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：※同期化していないので注意
********************************************************************************/
BOOL CBlockedMemory::Reset()
{
	if ( m_BlockBuffer.BlockCount() == 0 ) return FALSE ;

	SubInitBlocks() ;

	return TRUE ;
}


/*******************************************************************************
*	関　数　名：Destroy
*	用　　　途：使用中メモリの全破棄
*	引　き　数：強制破棄フラグ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：※同期化していないので注意
********************************************************************************/
BOOL CBlockedMemory::Destroy( BOOL bForced )
{
	BOOL	bRet = FALSE ;

	do // once 
	{ 
		if ( m_pSListHeader == NULL ) { 
			break ;
		}
		
		if ( !bForced ) { 
			// リンクリスト内に使用個数がすべて残っていれば未使用
			if ( ::QueryDepthSList(m_pSListHeader) != m_usCount ) { 
				break ;
			}
		}

		// 全メモリの解放と初期化
		bRet = SubFreeAll() ;
		if ( !bRet ) { 
			break ;
		}

		// メンバーの初期化
		InitMember() ;
	} while ( FALSE ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：SubFreeAll
*	用　　　途：メモリの全解放
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CBlockedMemory::SubFreeAll()
{
	// リンクリスト内のデータをクリア
	::InterlockedFlushSList( m_pSListHeader ) ;

	return m_BlockBuffer.Free() ;
}


/*******************************************************************************
*	関　数　名：SubAllocBuffer
*	用　　　途：メモリブロックを割り当てる
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL CBlockedMemory::SubAllocBlocks()
{
	SIZE_T	uiContainerSize ;

	// コンテナサイズを計算
	uiContainerSize = offsetof(Container, aBuffer) + m_uiBlockSize ;

	// コンテナの割り当て
	return m_BlockBuffer.Alloc( uiContainerSize, m_usCount ) ;
}


/*******************************************************************************
*	関　数　名：SubInitBlocks
*	用　　　途：メモリブロックの初期化
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CBlockedMemory::SubInitBlocks()
{
	// 一旦リンクリスト内の要素をクリア
	::InterlockedFlushSList( m_pSListHeader ) ;

	// リンクリスト(スタック)にコンテナを積む
	for ( INT32 i=m_usCount-1 ; i>=0 ; i-- ) { 
		::InterlockedPushEntrySList( m_pSListHeader
								   , (SLIST_ENTRY*)m_BlockBuffer[i] 
								   ) ;
	}
}


/*******************************************************************************
*	関　数　名：Alloc
*	用　　　途：メモリブロックの割り当て
*	引　き　数：初期化フラグ
*	戻　り　値：成功(割り当てたメモリへのポインタ) / 失敗(NULL)
*	備　　　考：
********************************************************************************/
BYTE* CBlockedMemory::Alloc( BOOL bInit )
{
	if ( m_BlockBuffer.BlockCount() == 0 )	return NULL ;

	BYTE*		pbyRet = NULL ;
	Container*	pContainer ;

	do // once 
	{
		// リンクリスト(スタック)に詰まれているコンテナへのポインタを取り出して
		// その中のデータへのポインタを返却する
		pContainer = (Container*)::InterlockedPopEntrySList( m_pSListHeader ) ;
		if ( pContainer == NULL ) {
			break ;
		}

		pbyRet = (BYTE*)pContainer->aBuffer ;
		if ( !bInit ) { 
			break ;
		}
		::ZeroMemory( pbyRet, m_uiBlockSize ) ;
	} while ( FALSE ) ;

	return pbyRet ;
}


/*******************************************************************************
*	関　数　名：Free
*	用　　　途：使用メモリブロックを解放
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：※Release時はポインタのチェックは行わないので、注意して扱うこと
********************************************************************************/
BOOL CBlockedMemory::Free( void* p )
{
	if ( p == NULL )						return FALSE ;
	if ( m_BlockBuffer.BlockCount() == 0 )	return FALSE ;

	Container*	pContainer ;

	// データバッファへのポインタからコンテナへのポインタを逆算する
	pContainer = (Container*)( (BYTE*)p - offsetof(Container, aBuffer) ) ;

#ifdef _DEBUG
	BOOL	bRet ;

	do // once
	{
		// ポインタチェック
		bRet = m_BlockBuffer.IsValidBlock( (BYTE*)pContainer ) ;
		if ( !bRet ) { 
			break ;
		}

		// リンクリスト(スタック)に使用済みポインタを返却する
		::InterlockedPushEntrySList( m_pSListHeader, (SLIST_ENTRY*)pContainer ) ;
	} while ( FALSE ) ;

	return bRet ;
#else
	// リンクリスト(スタック)に使用済みポインタを返却する
	::InterlockedPushEntrySList( m_pSListHeader, (SLIST_ENTRY*)pContainer ) ;

	return TRUE ;
#endif
}


/*******************************************************************************
*	関　数　名：IsUsed
*	用　　　途：メモリ使用中確認
*	引　き　数：
*	戻　り　値：使用していればTRUE 
*	備　　　考：
********************************************************************************/
BOOL CBlockedMemory::IsUsed()
{
	if ( m_BlockBuffer.BlockCount() == 0 )	return FALSE ;

	BOOL	bRet = FALSE ;

	// リンクリスト内に使用個数がすべて残っていれば未使用
	if ( ::QueryDepthSList(m_pSListHeader) != m_usCount ) { 
		bRet = TRUE ;
	}

	return bRet ;
}


///-------------------------------------------------------------------------------------------------

/*******************************************************************************
*	関　数　名：CMemories
*	用　　　途：コンストラクタ
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
CMemories::CMemories()
: m_aMemories()
{
}


/*******************************************************************************
*	関　数　名：~CMemories
*	用　　　途：デストラクタ
*	引　き　数：
*	戻　り　値：
*	備　　　考：全メモリ解放
********************************************************************************/
CMemories::~CMemories()
{
	DestroyMemories() ;
}


/*******************************************************************************
*	関　数　名：SetupMemories
*	用　　　途：メモリ配列の作成
*	引　き　数：配列個数
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：全メモリ解放
********************************************************************************/
BOOL CMemories::SetupMemories( UINT uQuantity )
{
	BOOL	bRet = FALSE ;
	INT32	i ;

	do // once 
	{
		// 個数に変更がなければ使用状態を解除するのみ
		if ( m_aMemories.GetCount() == (INT_PTR)uQuantity ) { 
			bRet = ResetAll() ;
			break ;
		}

		// 既に割り当て済みであれば解放する
		if ( m_aMemories.GetCount() > 0 ) { 
			bRet = DestroyMemories() ;
			if ( !bRet ) { 
				break ;
			}
		}

		// 成功で初期設定
		bRet	= TRUE ;

		// メモリ配列作成とポインタの初期化
		m_aMemories.SetSize( (INT_PTR)uQuantity ) ;
		for ( i=0 ; i<(INT32)uQuantity ; i++ ) { 
			m_aMemories[i] = NULL ;
		}

		// メモリオブジェクトの作成
		for ( i=0 ; i<(INT32)uQuantity ; i++ ) { 
			m_aMemories[i] = new CBlockedMemory() ;
			if ( m_aMemories[i] == NULL ) { 
				bRet = FALSE ;
				DestroyMemories() ;
				break ;
			}
		}
	} while ( FALSE ) ;

	return bRet ;
}


/*******************************************************************************
*	関　数　名：DestroyMemories
*	用　　　途：全メモリ配列の解放
*	引　き　数：
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：全メモリ解放
********************************************************************************/
BOOL CMemories::DestroyMemories()
{
	if ( m_aMemories.GetCount() <= 0 )	return FALSE ;

	INT32	i, n ;

	n = (INT32)m_aMemories.GetCount() ;
	for ( i=0 ; i<n ; i++ ) { 
		if ( m_aMemories[i] != NULL ) { 
			delete m_aMemories[i] ;
		}
	}

	m_aMemories.RemoveAll() ;
	return TRUE ;
}


/*******************************************************************************
*	関　数　名：RefAt
*	用　　　途：メモリオブジェクトの参照
*	引　き　数：オブジェクトインデックス
*	戻　り　値：失敗時はNULLが返却される
*	備　　　考：
********************************************************************************/
CBlockedMemory* CMemories::RefAt( INT32 nIndex )
{
	if ( nIndex<0 || nIndex>=m_aMemories.GetCount() )		return NULL ;

	return m_aMemories.GetAt( nIndex ) ;
}


/*******************************************************************************
*	関　数　名：operator[]
*	用　　　途：インデクサオペレータ
*	引　き　数：オブジェクトインデックス
*	戻　り　値：失敗時はNULLが返却される
*	備　　　考：インデックスが異常の場合は、デバッグ時にアサートに失敗する
********************************************************************************/
CBlockedMemory& CMemories::operator[]( INT32 nIndex )
{
	ASSERT( nIndex>=0 && nIndex<m_aMemories.GetCount() ) ;

	return *( m_aMemories.GetAt(nIndex) ) ;
}


/*******************************************************************************
*	関　数　名：Quantity
*	用　　　途：メモリオブジェクト数を取得する
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
UINT CMemories::Quantity() const
{
	return (UINT)m_aMemories.GetCount() ;
}


/*******************************************************************************
*	関　数　名：ResetAll
*	用　　　途：全メモリオブジェクトのメモリ使用状況をリセットする
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
BOOL CMemories::ResetAll()
{
	if ( m_aMemories.GetCount() <= 0 )	return FALSE ;

	BOOL	bRet = TRUE ;
	INT32	i, n ; 

	n = (INT32)m_aMemories.GetCount() ;
	for ( i=0 ; i<n ; i++ ) { 
		bRet &= m_aMemories[i]->Reset() ;
	}

	return bRet ;
}


/*******************************************************************************
*	関　数　名：DestroyAll
*	用　　　途：全メモリオブジェクトのメモリを解放する
*	引　き　数：矯正解放フラグ
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：bForced=FALSEの場合は、使用中メモリが１つでもあれば
*				そのメモリオブジェクトの解放は失敗する
********************************************************************************/
BOOL CMemories::DestroyAll( BOOL bForced )
{
	if ( m_aMemories.GetCount() <= 0 )	return FALSE ;

	BOOL	bRet = TRUE ;
	INT32	i, n ; 

	n = (INT32)m_aMemories.GetCount() ;

	if ( !bForced ) { 
		// 使用中チェック
		// １つでも使用中のものがあれば解放不可
		for ( i=0 ; i<n ; i++ ) { 
			if ( m_aMemories[i]->IsUsed() ) { 
				bRet = FALSE ;
				goto breakout ;
			}
		}
	}

	// 全メモリ解放
	for ( i=0 ; i<n ; i++ ) { 
		bRet &= m_aMemories[i]->Destroy( bForced ) ;
	}

breakout:;
	return bRet ;
}
