//
//	cliprinb.cs
//
//	無駄を減らしたリングバッファ
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Thu Mar 23 23:57:19 JST 2017 Naoyuki Sawa
//	- 1stリリース。
//	- C言語版では、要素の実体がリングバッファのメモリ内に有ったので、参照中の位置に書き込む事は出来ませんでした。
//	  C#では、要素の実体がリングバッファのメモリ外に有るので、参照中の位置に書き込む事も可能ではあるのですが、
//	  今回は、なるべくC言語版の動作をそのまま移植しようと思ったので、敢えてそのままのアルゴリズムで移植しました。
//	  従って、C#版でもC言語版と同じく、読み出さずに書き込める要素数は(nElmCnt-1)です。
//	  尚、当モジュール内のコメントは、C言語版のコメントをほぼそのまま残しているので、C#の実態とは合っていません。
//	  当モジュール内のコメントは、あくまでもC言語版のコメントである事に注意して下さい。
//
using System;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	
		//*****************************************************************************
		//リングバッファ
		public class ST_RinBuf {
			public int		iMaxPos { get { return TBL_Buf.Length - 1; } }	//iAddPos,及び,iGetPosの最大値	1～INT_MAX	(=TBL_Buf[]の要素数-1)	少なくとも書き込み用に1要素と参照用に1要素の2要素が必要。従ってiMaxPosの下限は2-1=1である。
			public int		iAddPos;					//要素を書き込む位置		0～iMaxPos
			public int		iGetPos;					//要素を読み出す位置		0～iMaxPos
			public int		nOvrCnt;					//オーバーランが発生した回数	0～INT_MAX	オーバーランが発生した場合、既にバッファ内に有る要素は維持し、書き込もうとした要素が失われる。
			public object[]		TBL_Buf/*[(iMaxPos+1)]*/;			//
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		//リングバッファを動的に作成する。
		public static ST_RinBuf RinBuf_New(int nElmCnt/*2～*/) {
			ST_RinBuf pRinBuf;
			if(nElmCnt < 2) { throw new ApplicationException(); }
			//メモリを確保する。
			pRinBuf = new ST_RinBuf();
			//リングバッファを静的に初期化する。
			RinBuf_Init(pRinBuf, nElmCnt);
			return pRinBuf;
		}
		//-----------------------------------------------------------------------------
		//リングバッファを静的に初期化する。
		public static void RinBuf_Init(ST_RinBuf pRinBuf, int nElmCnt/*2～*/) {
			if(nElmCnt < 2) { throw new ApplicationException(); }
			pRinBuf.iAddPos = 0;
			pRinBuf.iGetPos = 0;
			pRinBuf.nOvrCnt = 0;
			pRinBuf.TBL_Buf = new object[nElmCnt];
		}
		//-----------------------------------------------------------------------------
		//要素を追加する。
		public static void RinBuf_Add(ST_RinBuf pRinBuf, object pElm) {
/*ENTER_CS;*/		lock(pRinBuf) {
				//今回の要素を書き込む位置を取得する。
				int iAddPos = pRinBuf.iAddPos;
				//今回の要素を書き込む位置へのインデクスを求める。
				int iBuf = iAddPos;
				//次回の要素を書き込む位置を進める。
				if(++iAddPos > pRinBuf.iMaxPos) { iAddPos = 0; }
				//次回の要素を書き込む位置が、要素を読み出す位置に一致しなければ…
				// - 即ち、今回の要素を書き込む位置が、現在アプリケーションが参照中(かも知れない)の位置(=前回RinBuf_Get()が返した位置)よりも前ならば…
				if(iAddPos != pRinBuf.iGetPos) {
					//次回の要素を書き込む位置を格納する。
					pRinBuf.iAddPos = iAddPos;
					//今回の要素を書き込む。
					pRinBuf.TBL_Buf[iBuf] = pElm;
				//次回の要素を書き込む位置が、要素を読み出す位置に一致したら…
				// - 即ち、今回要素を書き込む位置が、現在アプリケーションが参照中(かも知れない)の位置(=前回RinBuf_Get()が返した位置)ならば…
				} else {
					//今回の要素を書き込む事は出来ない。オーバーランが発生した回数を増やす。
					if(pRinBuf.nOvrCnt < int.MaxValue) { pRinBuf.nOvrCnt++; }
				}
/*LEAVE_CS;*/		}
		}
		//-----------------------------------------------------------------------------
		//要素を取得する。
		public static object RinBuf_Get(ST_RinBuf pRinBuf) {
			object pBuf;
/*ENTER_CS;*/		lock(pRinBuf) {
				//今回の要素を読み出す位置を取得する。
				int iGetPos = pRinBuf.iGetPos;
				//バッファが空でなければ…
				if(iGetPos != pRinBuf.iAddPos) {
					//今回の要素を読み出す位置のオブジェクトを取得する。
					pBuf = pRinBuf.TBL_Buf[iGetPos];
					//次回の要素を読み出す位置を進める。
					if(++iGetPos > pRinBuf.iMaxPos) { iGetPos = 0; }
					//次回の要素を読み出す位置を格納する。
					pRinBuf.iGetPos = iGetPos;
				//バッファが空ならば…
				} else {
					//nullポインタを返す。
					pBuf = null;
				}
/*LEAVE_CS;*/		}
			return pBuf;
		}
		//-----------------------------------------------------------------------------
		//オーバーランが発生した回数を取得する。
		public static int RinBuf_Ovr(ST_RinBuf pRinBuf) {
			int nOvrCnt;
/*ENTER_CS;*/		lock(pRinBuf) {
				//オーバーランが発生した回数を取得する。
				nOvrCnt = pRinBuf.nOvrCnt;
				//オーバーランが発生した回数を確実にクリアする。
				pRinBuf.nOvrCnt = 0;
/*LEAVE_CS;*/		}
			return nOvrCnt;
		}
	}
}
