/********************************************************************************
*	ファイル名：RingQueue.hpp													*
*	用　　　途：リングバッファを使用した高速な固定長FIFOバッファ				*
*	備　　　考：テンプレート													*
*				RingQueue.h内よりinclude										*
*	履　　　歴：																*
********************************************************************************/
/****************************
*	 コンパイル環境設定		*
****************************/
#pragma once
/****************************
*	インクルードファイル	*
****************************/
#include "ctor.hpp"

/****************************
*		 マクロ定数			*
****************************/
/****************************
*		 構造体定義			*
****************************/
/****************************
*		 クラス定義			*
****************************/
/****************************
*		 関数　宣言			*
****************************/
/****************************
*		 変数　宣言			*
****************************/

/********************************************************************************
*	関　数　名：PushT															*
*	用　　　途：待ち行列にデータを追加											*
*	引　き　数：キューデータ(要素)												*
*	戻　り　数：追加成功でＴＲＵＥ、失敗でＦＡＬＳＥ							*
*	備　　　考：																*
********************************************************************************/
template<typename T>
BOOL CRingQueue::PushT( T& rSrc )
{
	ASSERT( sizeof(T) <= m_nDataSize ) ;

	INT32	nIndex ;		/* リングインデックス		 */
	Packet*	pPacket ;		/* パケットバッファへの参照	 */

	/***** 初期化チェック *****/
	if( m_ppvQueue == NULL   )						return FALSE ;
	if( sizeof(T) > m_nDataSize )					return FALSE ;

	/***** マルチスレッドロック *****/
	if( m_plkWrite != NULL )
	{
		m_plkWrite->Lock() ;

		if( ATOMIC_TEST(m_lbShutDown) )
		{
			m_plkWrite->Unlock() ;
													return FALSE ;
		}

		nIndex = m_nWriteIndex ;

		/***** 満杯チェック *****/
		if( ATOMIC_TEST_PTR(m_ppvQueue[nIndex]) )
		{	
			m_plkWrite->Unlock() ;
													return FALSE ;
		}

		RingIncrement( &m_nWriteIndex ) ;
	}
	else
	/***** シングルスレッド *****/
	{
		/***** 送受信停止チェック *****/
		if( ATOMIC_TEST(m_lbShutDown) )				return FALSE ;

		/***** 満杯チェック *****/
		nIndex = m_nWriteIndex ;
		if( ATOMIC_TEST_PTR(m_ppvQueue[nIndex]) )	return FALSE ;

		RingIncrement( &m_nWriteIndex ) ;
	}

	// パケットにデータセット
	pPacket = (Packet*)( m_pbyPackets + (nIndex * m_nPacketSize) ) ;
	pPacket->iSize = (INT64)m_nDataSize ;

	*( (T*)pPacket->aData ) = rSrc ;

	// キューに追加
	ATOMIC_SET_PTR( m_ppvQueue[nIndex], pPacket ) ;

	/***** リーダーロック用セマフォ１個インクリメント ****/
	if( m_hsmRead != NULL )
	{
		::ReleaseSemaphore( m_hsmRead, 1, NULL ) ;
	}

	if ( m_plkWrite != NULL )
	{
		m_plkWrite->Unlock() ;
	}

	return TRUE ;
}


/********************************************************************************
*	関　数　名：PopT															*
*	用　　　途：待ち行列からデータを取得										*
*	引　き　数：キューデータ格納先、タイムアウト時間[ms]						*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：アクセスモードがRQ_MT_BLOCKING_READERの場合は、					*
*				タイムアウト値が有効											*
*				そうでない場合は、タイムアウト値は無効							*
********************************************************************************/
template<typename T>
INT32 CRingQueue::PopT( T*		pDst,		/* : 取り出し先				    */
						UINT32	nTimeout	/* : 受け取り待ちタイムリミット */ )
{
	ASSERT( pDst!= NULL ) ;
	ASSERT( sizeof(T) <= m_nDataSize ) ;

	Packet*		pPacket ;		/* パケットバッファ		 */
	INT32		nSize ;			/* 取得サイズ			 */

	/***** 初期化チェック *****/
	if( m_ppvQueue == NULL )					return -1 ;

	/***** リーダーブロッキング(マルチスレッド) *****/
	if( m_hsmRead != NULL )
	{
		/***** バッファ待ちと送受信停止確認及び
			   リーダーロック用セマフォ１個デクリメント ****/
		DWORD	dwWait = ::WaitForSingleObject( m_hsmRead, (DWORD)nTimeout ) ;

			 if( dwWait == WAIT_TIMEOUT	 )		return  0 ;
		else if( dwWait != WAIT_OBJECT_0 )		return -1 ;
	}
	/***** シングルスレッド *****/
	//else
	//{
	//}

	/***** パケット取得 *****/
	pPacket = (Packet*)ATOMIC_GET_PTR( m_ppvQueue[m_nReadIndex] ) ;
	if ( pPacket == NULL ) 
	{ 
		if ( ATOMIC_TEST(m_lbShutDown) )
		{
			TRACE( _T("■■■■■■■■■■ キューはシャットダウン(読み書き不可)しました!  ")
			       _T("Pop()の返値には-1が返されます。  ")
			       _T("TRUE / FALSEでないことに注意してください!\n") ) ;
												return -1 ;
		}
												return 0 ;
	}

	nSize = (INT32)pPacket->iSize ;
	ASSERT( nSize>0 && nSize<=(INT32)m_nDataSize ) ;

	*pDst = *( (T*)pPacket->aData ) ;

	ATOMIC_SET_PTR( m_ppvQueue[m_nReadIndex], NULL ) ;

	RingIncrement( &m_nReadIndex ) ;

	return nSize ;
}


/********************************************************************************
*	関　数　名：PeekT															*
*	用　　　途：待ち行列からデータを取得										*
*	引　き　数：キューデータ格納先												*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：覗き見機能なのでPopと異なりキューからは外されない				*
*				引数の格納先が０の場合は、データの有無・サイズのみの確認になる	*
********************************************************************************/
template<typename T>
INT32 CRingQueue::PeekT( T* pDst /* : 取り出し先 */ )
{
	Packet*		pPacket ;		/* パケットバッファ		 */
	INT32		nSize ;		 	/* 取得サイズ			 */

	/***** 初期化チェック *****/
	if( m_ppvQueue == NULL )							return -1 ;

	/***** 送受信停止確認 *****/
	//if( ATOMIC_TEST(m_lbShutDown) )					return -1 ;

	/***** パケット取得 *****/
	pPacket = (Packet*)ATOMIC_GET_PTR( m_ppvQueue[m_nReadIndex] ) ;
	if ( pPacket == NULL )								return 0 ; 

	nSize = (INT32)pPacket->iSize ;
	ASSERT( nSize>0 && nSize<=(INT32)m_nDataSize ) ;

	if( pDst != NULL )	
	{
		*pDst = *( (T*)pPacket->aData ) ;
	}

	return nSize ;
}

/********************************************************************************
*	関　数　名：Peek															*
*	用　　　途：待ち行列の先頭から任意位置のデータを取得						*
*	引　き　数：キューデータ格納先												*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：覗き見機能なのでPopと異なりキューからは外されない				*
*				引数の格納先が０の場合は、データの有無・サイズのみの確認になる	*
*				対象データがキューに格納されていない場合は失敗(-1)する			*
********************************************************************************/
template<typename T>
INT32 CRingQueue::PeekT( INT32 nPeekIndexNum, T* pvDst )
{
	Packet*		pPacket ;		/* パケットバッファ		 */
	INT32		nSize ;		 	/* 取得サイズ			 */
	INT32		nPeekIndex ;	/* 参照先インデックス		 */

	/***** 初期化チェック *****/
	if( m_ppvQueue == NULL )									return -1 ;

	/***** キューに格納されているかチェック *****/
	if( abs( m_nWriteIndex - m_nReadIndex ) < nPeekIndexNum )	return -1 ;

	/***** 参照インデックス取得 *****/
	nPeekIndex = m_nReadIndex;
	RingDecrement( nPeekIndexNum, &nPeekIndex );

	/***** パケット取得 *****/
	pPacket = (Packet*)ATOMIC_GET_PTR( m_ppvQueue[nPeekIndex] ) ;
	if ( pPacket == NULL )										return 0 ; 

	nSize = (INT32)pPacket->iSize ;
	ASSERT( nSize>0 && nSize<=(INT32)m_nDataSize ) ;

	if( pvDst != NULL )	
	{
		::CopyMemory( pvDst, pPacket->aData, (SIZE_T)nSize ) ;
	}

	return nSize ;
}


/********************************************************************************
*	関数名：TRingQueue															*
*	用　途：デフォルトコンストラクタ											*
*	引き数：																	*
*	戻り値：																	*
*	備　考：																	*
********************************************************************************/
template<typename T>
TRingQueue<T>::TRingQueue()
	: CRingQueue()
{
}


/********************************************************************************
*	関数名：TRingQueue															*
*	用　途：待ち行列に格納する数量設定コンストラクタ							*
*	引き数：数量、マルチスレッドモードフラグ									*
*	戻り値：																	*
*	備　考：																	*
********************************************************************************/
template<typename T>
TRingQueue<T>::TRingQueue( UINT32 nQuantity,	/* : 数量                     */ 
						   UINT32 nMTMode		/* : マルチスレッド対応モード */ )
	: CRingQueue( sizeof(T), nQuantity, UINT32 nMTMode )
{
	if( nQuantity != 0 )
	{
		PBYTE	pbyData = CRingQueue::m_pbyPackets + offsetof( CRingQueue::Packet, aData ) ;
		INT32	i ;

		//***** コンストラクタを呼び出す
		for( i=0 ; i< CRingQueue::m_nQuantity ; i++ )
		{
			new(pbyData) T() ;
			pbyData += CRingQueue::m_nPacketSize ;
		}
	}
}


/********************************************************************************
*	関数名：~TRingQueue															*
*	用　途：デストラクタ														*
*	引き数：																	*
*	戻り値：																	*
*	備　考：																	*
********************************************************************************/
template<typename T>
TRingQueue<T>::~TRingQueue()
{
	RemoveAll() ;
}


/********************************************************************************
*	関　数　名：SetDataSize														*
*	用　　　途：キューに保持する数量を指定										*
*	引　き　数：データサイズ、個数												*
*	戻　り　数：設定成功でＴＲＵＥ、失敗でＦＡＬＳＥ							*
*	備　　　考：すでにキューが作成済みの場合は、失敗する						*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::SetDataSize( UINT32 nQuantity,	/* : 数量                     */  
					 		     UINT32 nMTMode		/* : マルチスレッド対応モード */ )
{
	BOOL	bRet ;

	bRet = CRingQueue::SetDataSize( sizeof(T), nQuantity, nMTMode ) ;
	if( !bRet )
	{
		return FALSE ;
	}

	if( nQuantity == 0 )
	{
		return TRUE ;
	}

	PBYTE	pbyData = CRingQueue::m_pbyPackets + offsetof( CRingQueue::Packet, aData ) ;
	UINT32	i ;

	//***** 各要素のコンストラクタを呼び出す
	for( i=0 ; i<CRingQueue::m_nQuantity ; i++ )
	{
		new(pbyData) T() ;
		pbyData += CRingQueue::m_nPacketSize ;
	}

	return TRUE ;
}


/********************************************************************************
*	関　数　名：GetQuantity														*
*	用　　　途：キューに保持するデータの最大個数を取得							*
*	引　き　数：																*
*	戻　り　数：個数															*
*	備　　　考：																*
********************************************************************************/
template<typename T>
UINT32 TRingQueue<T>::GetQuantity() const 
{
	return CRingQueue::GetQuantity() ;
}


/********************************************************************************
*	関　数　名：SetMTMode														*
*	用　　　途：マルチスレッド対応モードの設定									*
*	引　き　数：モード															*
*	戻　り　数：設定成功でＴＲＵＥ、失敗でＦＡＬＳＥ							*
*	備　　　考：マルチスレッド対応モードの場合、待機(ブロッキング中)のスレッド	*
*				がいない状態で使用すること										*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::SetMTMode( UINT32 nMTMode )
{
	return CRingQueue::SetMTMode( nMTMode ) ;
}


/********************************************************************************
*	関　数　名：RemoveAll														*
*	用　　　途：キューをリセットしてバッファを解放する							*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：																*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::RemoveAll()
{
	if( CRingQueue::m_nQuantity != 0 )
	{
		PBYTE	pbyData = CRingQueue::m_pbyPackets + offsetof( CRingQueue::Packet, aData ) ;
		UINT32	i ;

		//***** 各要素のデストラクタを呼び出す
		for( i=0 ; i<CRingQueue::m_nQuantity ; i++ )
		{
			((T*)pbyData)->~T() ;
			pbyData += CRingQueue::m_nPacketSize ;
		}
	}

	return CRingQueue::RemoveAll() ;
}


/********************************************************************************
*	関　数　名：Reset															*
*	用　　　途：キューをリセットする（バッファを解放しない）					*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：マルチスレッド対応モードの場合、待機(ブロッキング中)のスレッド	*
*				がいない状態で使用すること										*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::Reset()
{
	BOOL	bRet ;

	bRet = CRingQueue::Reset() ;
	if( !bRet )
	{
		return FALSE ;
	}

	if( CRingQueue::m_nQuantity != 0 )
	{
		PBYTE	pbyData = CRingQueue::m_pbyPackets + offsetof( CRingQueue::Packet, aData ) ;
		UINT32	i ;

		//***** 各要素を初期化する
		for( i=0 ; i<CRingQueue::m_nQuantity ; i++ )
		{
			*((T*)pbyData) = T() ;
			pbyData += CRingQueue::m_nPacketSize ;
		}
	}

	return TRUE ;
}


/********************************************************************************
*	関　数　名：ShutDown														*
*	用　　　途：キューのアクセスを停止する										*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：RemoveAllもしくはResetされるまで状態が保持される				*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::ShutDown()
{
	return CRingQueue::ShutDown() ;
}


/********************************************************************************
*	関　数　名：IsShutDown														*
*	用　　　途：キューのアクセス停止状態の確認									*
*	引　き　数：																*
*	戻　り　数：																*
*	備　　　考：																*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::IsShutDown()
{
	return CRingQueue::IsShutDown() ;
}


/********************************************************************************
*	関　数　名：Push															*
*	用　　　途：待ち行列にデータを追加											*
*	引　き　数：キューデータ(要素)												*
*	戻　り　数：追加成功でＴＲＵＥ、失敗でＦＡＬＳＥ							*
*	備　　　考：																*
********************************************************************************/
template<typename T>
BOOL TRingQueue<T>::Push( T& rSrc )
{
	return CRingQueue::PushT( rSrc ) ;
}


/********************************************************************************
*	関　数　名：Pop																*
*	用　　　途：待ち行列からデータを取得										*
*	引　き　数：キューデータ格納先、タイムアウト時間[ms]						*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：アクセスモードがRQ_MT_BLOCKING_READERの場合は、					*
*				タイムアウト値が有効											*
*				そうでない場合は、タイムアウト値は無効							*
********************************************************************************/
template<typename T>
INT32 TRingQueue<T>::Pop( T*		pDst,		/* : 取り出し先				    */
						  UINT32	nTimeout	/* : 受け取り待ちタイムリミット */ )
{
	return CRingQueue::PopT( pDst, nTimeout ) ;
}


/********************************************************************************
*	関　数　名：Peek															*
*	用　　　途：待ち行列からデータを取得										*
*	引　き　数：キューデータ格納先												*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：覗き見機能なのでPopと異なりキューからは外されない				*
*				引数の格納先が０の場合は、データの有無・サイズのみの確認になる	*
********************************************************************************/
template<typename T>
INT32 TRingQueue<T>::Peek( T* pDst /* : 取り出し先 */ )
{
	return CRingQueue::PeekT( pDst ) ;
}

/********************************************************************************
*	関　数　名：Peek															*
*	用　　　途：待ち行列の先頭から任意位置のデータを取得						*
*	引　き　数：キューデータ格納先												*
*	戻　り　数：取得成功でデータサイズ、失敗で-1								*
*	備　　　考：覗き見機能なのでPopと異なりキューからは外されない				*
*				引数の格納先が０の場合は、データの有無・サイズのみの確認になる	*
*				対象データがキューに格納されていない場合は失敗(-1)する			*
********************************************************************************/
template<typename T>
INT32 TRingQueue<T>::Peek( INT32 nPeekIndexNum, T* pvDst )
{
	return CRingQueue::PeekT( nPeekIndexNum, pDst ) ;
}

