/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbStreamItf
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbStreamItf.h"
#include "xbVariant.h"

#pragma warning(push)
#pragma warning(disable: 4311) //troncation de pointeur de 'wchar *' à 'xbNptr'

xbEimpl(xbEstream,xbEadv);

xbEstream::xbEstream(const IxbStreamInfo *pStm,xbHR hr,const xbString &msg,xbEctorMode ctorMode)
:xbEadv(hr,msg,pStm?pStm->GetName():NULL,xbEctor_base)
{
	traceExcept(ctorMode);
}

xbEstream::xbEstream(const IxbStreamInfo *pStm,const xbString &msg,xbEctorMode ctorMode)
:xbEadv(msg,pStm?pStm->GetName():NULL,xbEctor_base) 
{
	traceExcept(ctorMode);
}

xbEstream::xbEstream(const xbString &strm,xbHR hr,const xbString &msg,xbEctorMode ctorMode)
:xbEadv(hr,msg,strm,xbEctor_base)
{
	traceExcept(ctorMode);
}

/*xbEstream::xbEstream(const xbString &strm,const xbString &msg,xbEctorMode ctorMode)
:xbEadv(msg,strm) 
{
	traceExcept(ctorMode);
}*/

IxbInputStream	&IxbInputStream::operator>>(sIxbStreamable *piObj)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	piObj->ReadFromStream(pmode,this);
	return *this;
}

IxbInputStream	&IxbInputStream::operator>>(xbStringA *pStr)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_ReadStringA(xbFormatBin,this,pmode,NULL,pLck,pStr,NULL);
	return *this;
}

IxbInputStream	&IxbInputStream::operator>>(xbStringW *pStr)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_ReadStringW(xbFormatBin,this,pmode,NULL,pLck,pStr,NULL);
	return *this;
}

IxbInputStream	&IxbInputStream::operator>>(xbBSTR *pStr)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_ReadStringW(xbFormatBin,this,pmode,NULL,pLck,pStr,NULL);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const sIxbStreamable &iObj)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	iObj.WriteToStream(pmode,this);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const xbStringA &str)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_WriteStringA(xbFormatBin,this,pmode,pLck,str,str.Length,false);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const xbStringW &str)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_WriteStringW(xbFormatBin,this,pmode,pLck,str,str.Length,false);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const xbBSTR &str)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_WriteStringW(xbFormatBin,this,pmode,pLck,str,str.Length,false);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const char *pStr)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_WriteStringA(xbFormatBin,this,pmode,pLck,pStr,(ulong)(pStr?strlen(pStr):0),false);
	return *this;
}

IxbOutputStream	&IxbOutputStream::operator<<(const wchar *pStr)
{
	xbRefQIPtr<IxbStreamMode> pmode(this,xbOp_Optional);
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbStream_WriteStringW(xbFormatBin,this,pmode,pLck,pStr,(ulong)(pStr?wcslen(pStr):0),false);
	return *this;
}

ulong		IxbInputStream::ReadStringA(xbStringA *pStr,xbStringA *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_ReadStringA(xbFormatText,this,pMode,NULL,pLck,pStr,pStopStr);
/*
	xbRefQIPtr<IxbSeekStream> prSkStm(this);

	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	xbLock lck;
	if (pLck)
		lck.Lock(pLck);

	return xbStream_ReadString(this,prSkStm,&lck,pStr,pStopStr,pMode);*/
}

ulong		IxbInputStream::ReadStringW(xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_ReadStringW(xbFormatText,this,pMode,NULL,pLck,pStr,pStopStr);
}

ulong			IxbInputStream::ReadStringW(xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_ReadStringW(xbFormatText,this,pMode,NULL,pLck,pStr,pStopStr);
}

ulong			IxbOutputStream::WriteStringA(const xbStringA &str,bool bAddLf,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_WriteStringA(xbFormatText,this,pMode,pLck,str,str.Length,bAddLf);
}
ulong			IxbOutputStream::WriteStringA(const char *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_WriteStringA(xbFormatText,this,pMode,pLck,pStr,lgStr,bAddLf);
}

ulong			IxbOutputStream::WriteStringW(const xbStringW &str,bool bAddLf,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_WriteStringW(xbFormatText,this,pMode,pLck,str,str.Length,bAddLf);
}

ulong			IxbOutputStream::WriteStringW(const xbBSTR &str,bool bAddLf,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_WriteStringW(xbFormatText,this,pMode,pLck,str,str.Length,bAddLf);
}

ulong			IxbOutputStream::WriteStringW(const wchar *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbStreamMode> pmode;
	if (!pMode)
	{
		pmode = this;
		pMode = pmode;
	}
	sIxbLock *pLck;
	xbCastIn<sIxbLock>(this,&pLck);
	return xbStream_WriteStringW(xbFormatText,this,pMode,pLck,pStr,lgStr,bAddLf);
}

uint64			IxbOutputStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm)
{
	sIxbLock *pLckStm = NULL;
	xbCastIn(pstm,&pLckStm);
	IxbSeekStream *pSkStm = NULL;
	xbCastIn(pstm,&pSkStm);
	return xbStream_WriteStream(this,pstm,pLckStm,pSkStm,pCb);
}

ulong			IxbOutputStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm,ulong DataLg,bool DataLg_Required)
{
	return xbStream_WriteStream(this,pstm,DataLg,DataLg_Required,pCb);
}

ulong xbStream_WriteStream(IxbOutputStream *pstmDst,IxbInputStream *pstmSrc,ulong DataLg,bool DataLg_Required, sIxbProgress *pCb)
{
	if (!pstmSrc->CanRead()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmSrc,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, read permission denied on source stream");
	if (!pstmDst->CanWrite()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmDst,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, write permission denied on destination stream");

	if (DataLg<=0) return 0;

	ulong data_towr = DataLg;
	char	buf[xbSTREAM_BUFSIZE];

	while(data_towr>0)
	{
		ulong	data_wr = data_towr>xbSTREAM_BUFSIZE?xbSTREAM_BUFSIZE:data_towr;
		if ((data_wr = pstmSrc->Read(buf,data_wr,false)) <= 0) break;
		pstmDst->Write(buf,data_wr);
		if (pCb)
			pCb->stageStep(NULL,data_wr,"bytes",true);
		data_towr -= data_wr;
	}
	if (DataLg_Required && data_towr != 0)
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmDst,xbOp_Optional),STG_E_WRITEFAULT,"WriteStream failed");

	return DataLg - data_towr;
}

ulong	xbStream_WriteStream(IxbOutputStream *pstmDst,IxbInputStream *pstmSrc,sIxbLock *pLckSrc,IxbSeekStream *pSkSrc,sIxbProgress *pCb)
{
	if (!pstmSrc->CanRead()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmSrc,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, read permission denied on source stream");
	if (!pstmDst->CanWrite()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmDst,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, write permission denied on destination stream");

	ulong	data_wr = 0,stmPos = 0;
	char	buf[xbSTREAM_BUFSIZE];

	if (pSkSrc && !pLckSrc)
		pSkSrc->SetPosition(0);

	for(;;)
	{
		ulong	data_rd;
		if (pLckSrc && pSkSrc)
		{
			xbLock lck(pLckSrc);
			pSkSrc->SetPosition(stmPos);
			if ((data_rd = pstmSrc->Read(buf,xbSTREAM_BUFSIZE,false)) <= 0) 
				break;
			stmPos += data_rd;
		}
		else
		{
			if ((data_rd = pstmSrc->Read(buf,xbSTREAM_BUFSIZE,false)) <= 0) 
				break;
		}
		pstmDst->Write(buf,data_rd);
		if (pCb)
			pCb->stageStep(NULL,data_rd,"bytes",true);

		data_wr += data_rd;
	}

	return data_wr;
}

uint64	xbStream_WriteLongStream(IxbOutputStream *pstmDst,IxbInputStream *pstmSrc,sIxbLock *pLckSrc,IxbLongSeekStream *pSkSrc,sIxbProgress *pCb)
{
	if (!pstmSrc->CanRead()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmSrc,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, read permission denied on source stream");
	if (!pstmDst->CanWrite()) 
		xbThrow xbEstream(xbRefQIPtr<IxbStreamInfo>(pstmDst,xbOp_Optional),STG_E_ACCESSDENIED,"WriteStream failed, write permission denied on destination stream");

	uint64	data_wr = 0,stmPos = 0;
	char	buf[xbSTREAM_BUFSIZE];

	if (pSkSrc && !pLckSrc)
		pSkSrc->SetLongPosition(0);

	for(;;)
	{
		ulong	data_rd;
		if (pLckSrc && pSkSrc)
		{
			xbLock lck(pLckSrc);
			pSkSrc->SetLongPosition(stmPos);
			if ((data_rd = pstmSrc->Read(buf,xbSTREAM_BUFSIZE,false)) <= 0) 
				break;
			stmPos += data_rd;
		}
		else
		{
			if ((data_rd = pstmSrc->Read(buf,xbSTREAM_BUFSIZE,false)) <= 0) 
				break;
		}
		pstmDst->Write(buf,data_rd);
		if (pCb)
			pCb->stageStep(NULL,data_rd,"bytes",true);

		data_wr += data_rd;
	}

	return data_wr;
}

ulong xbStream_FindLineSepA(ulong startpos,const char *pStr,ulong StrLg,long *pSepLg)
{
	xbAssert(startpos >= 1);
	if (startpos < 1)
		startpos = 1;
	register const char *ptr = pStr + startpos - 1;
	register const char *pend = pStr + StrLg;

	while(ptr < pend)
	{
		if (*ptr == '\n')
		{
			if (ptr > pStr && *(ptr-1) == '\r')
			{
				if (pSepLg) 
					*pSepLg = 2;
				ptr--;
			}
			else 
			{
				if (pSepLg) 
					*pSepLg = 1;
			}
			return ulong(xbNptr(ptr) - xbNptr(pStr) + 1);
		}
		ptr++;
	}
	ptr = pStr + startpos - 1;
	if (ptr<pend && *(pend-1) == '\r')
	{
		if (pSepLg) 
			*pSepLg = -1;
		return ulong(xbNptr(pend) - xbNptr(pStr));
	}
	return 0;
}

ulong	xbStream_ReadStringTextA(IxbInputStream *pstmSrc,IxbSeekStream *pSkSrc,
								 xbStringA *pStr,xbStringA *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbSeekStream> prSkSrc;
	if (!pSkSrc)
	{
		prSkSrc = pstmSrc;
		pSkSrc = prSkSrc;
	}

	if (pStr)
		pStr->Empty();

	ulong cbrd = 0;
	char buf[xbSTREAM_LINEBUFSIZE];
	for(;;)
	{
		ulong cb = pstmSrc->Read(buf,sizeof(buf),false);
		if (cb == 0)
		{
			if (pStopStr) 
				pStopStr->Empty();
			return cbrd;
		}

		long seplg = 0;
		ulong seppos;
		if (pMode)
			seppos = pMode->FindLineSepA(1,buf,cb,&seplg);
		else
			seppos = xbStream_FindLineSepA(1,buf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
			{
				pSkSrc->Seek(seplg,xbSeekCur);
				cb = seppos-1;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(buf,seppos-1);
			cbrd += seppos - 1;
			int step = cb - seppos + 1 - seplg;
			if (step > 0) 
				pSkSrc->Seek(-step,xbSeekCur);
			
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && buf[seppos-1] == '\r'
					&& buf[seppos] == '\n') 
					pStopStr->Set("\n",1);
				else 
					pStopStr->Set(buf+seppos-1,seplg);
			}
			
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(buf,cb);
		cbrd += cb;
	}

	if (pStopStr) 
		pStopStr->Empty();
	return cbrd;
}

ulong	xbStream_WriteStringTextA(IxbOutputStream *pstmSrc,const char *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	if (!lgStr && !bAddLf)
		return 0;
	if (!bAddLf && (!pMode || !pMode->CRLFasLF))
		return pstmSrc->Write(pStr,lgStr);

	xbStringA str(pStr,lgStr);
	if (bAddLf) 
		str += '\n';
	if (pMode && pMode->CRLFasLF)
	{
		int pos = -1;
		while((pos = str.IndexOf('\n',pos+1)) >= 0)
		{
			if (pos > 0 
				&& str.GetAt(pos-1) == '\r') 
				continue;

			str.Insert(pos++,'\r');
		}
	}

	return pstmSrc->Write((LPCSTR)str,str.Length);
}

ulong xbStream_FindLineSepW(ulong startpos,const wchar *pStr,ulong StrLg,long *pSepLg)
{
	xbAssert(startpos >= 1);
	if (startpos < 1)
		startpos = 1;
	register const wchar *ptr = pStr + startpos - 1;
	register const wchar *pend = pStr + StrLg;

	while(ptr < pend)
	{
		if (*ptr == L'\n')
		{
			if (ptr > pStr && *(ptr-1) == L'\r')
			{
				if (pSepLg) 
					*pSepLg = 2;
				ptr--;
			}
			else 
			{
				if (pSepLg) 
					*pSepLg = 1;
			}
			return ulong((xbNptr(ptr) - xbNptr(pStr))/sizeof(wchar) + 1);
		}
		ptr++;
	}
	ptr = pStr + startpos - 1;
	if (ptr<pend && *(pend-1) == L'\r')
	{
		if (pSepLg) 
			*pSepLg = -1;
		return ulong((xbNptr(pend) - xbNptr(ptr))/sizeof(wchar));
	}
	return 0;
}

ulong	xbStream_ReadStringTextW(IxbInputStream *pstmSrc,IxbSeekStream *pSkSrc,xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbSeekStream> prSkSrc;
	if (!pSkSrc)
	{
		prSkSrc = pstmSrc;
		pSkSrc = prSkSrc;
	}

	if (pStr)
		pStr->Empty();

	bool bUtf16revert = pMode && xbTextBOMutf16ToRevert(pMode->GetTextBOMtype());
	ulong cbrd = 0;
	wchar buf[xbSTREAM_LINEBUFSIZE];
	for(;;)
	{
		ulong cb = pstmSrc->Read(buf,sizeof(buf),false);
		if ((cb % 2) == 1) 
		{
			cb--;
			pSkSrc->Seek(-1,xbSeekCur);
		}
		if (cb == 0)
		{
			if (pStopStr) 
				pStopStr->Empty();
			return cbrd;
		}
		cb /= sizeof(wchar);
		if (bUtf16revert)
			xbTextBOMrevert(buf,cb);

		long seplg = 0;
		ulong seppos;
		if (pMode)
			seppos = pMode->FindLineSepW(1,buf,cb,&seplg);
		else
			seppos = xbStream_FindLineSepW(1,buf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
			{
				pSkSrc->Seek(seplg*long(sizeof(wchar)),xbSeekCur);
				cb = seppos-1;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(buf,seppos-1);
			cbrd += seppos - 1;
			int step = cb - seppos + 1 - seplg;
			if (step > 0) 
				pSkSrc->Seek(-int(step*sizeof(wchar)),xbSeekCur);
			
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && buf[seppos-1] == L'\r'
					&& buf[seppos] == L'\n') 
					pStopStr->Set(L"\n",1);
				else 
					pStopStr->Set(buf+seppos-1,seplg);
			}
			
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(buf,cb);
		cbrd += cb;
	}

	if (pStopStr) 
		pStopStr->Empty();
	return cbrd;
}

ulong	xbStream_ReadStringTextW(IxbInputStream *pstmSrc,IxbSeekStream *pSkSrc,xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode)
{
	xbRefQIPtr<IxbSeekStream> prSkSrc;
	if (!pSkSrc)
	{
		prSkSrc = pstmSrc;
		pSkSrc = prSkSrc;
	}

	if (pStr)
		pStr->Empty();

	bool bUtf16revert = pMode && xbTextBOMutf16ToRevert(pMode->GetTextBOMtype());
	ulong cbrd = 0;
	wchar buf[xbSTREAM_LINEBUFSIZE];
	for(;;)
	{
		ulong cb = pstmSrc->Read(buf,sizeof(buf),false);
		if ((cb % 2) == 1) 
		{
			cb--;
			pSkSrc->Seek(-1,xbSeekCur);
		}
		if (cb == 0)
		{
			if (pStopStr) 
				pStopStr->Empty();
			return cbrd;
		}
		cb /= sizeof(wchar);
		if (bUtf16revert)
			xbTextBOMrevert(buf,cb);

		long seplg = 0;
		ulong seppos;
		if (pMode)
			seppos = pMode->FindLineSepW(1,buf,cb,&seplg);
		else
			seppos = xbStream_FindLineSepW(1,buf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
			{
				pSkSrc->Seek(seplg*long(sizeof(wchar)),xbSeekCur);
				cb = seppos-1;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(buf,seppos-1);
			cbrd += seppos - 1;
			int step = cb - seppos + 1 - seplg;
			if (step > 0) 
				pSkSrc->Seek(-int(step*sizeof(wchar)),xbSeekCur);
			
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && buf[seppos-1] == L'\r'
					&& buf[seppos] == L'\n') 
					pStopStr->Set(L"\n",1);
				else 
					pStopStr->Set(buf+seppos-1,seplg);
			}
			
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(buf,cb);
		cbrd += cb;
	}

	if (pStopStr) 
		pStopStr->Empty();
	return cbrd;
}

ulong	xbStream_WriteStringTextW(IxbOutputStream *pstmSrc,const wchar *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	if (!lgStr && !bAddLf)
		return 0;
	bool bUtf16revert = pMode && xbTextBOMutf16ToRevert(pMode->GetTextBOMtype());
	if (!bUtf16revert && !bAddLf && (!pMode || !pMode->CRLFasLF))
		return pstmSrc->Write(pStr,lgStr*sizeof(wchar));

	xbStringW str(pStr,lgStr);
	if (bAddLf) 
		str += L'\n';
	if (pMode && pMode->CRLFasLF)
	{
		int pos = -1;
		while((pos = str.IndexOf(L'\n',pos+1)) >= 0)
		{
			if (pos > 0 
				&& str.GetAt(pos-1) == L'\r') 
				continue;
			str.Insert(pos++,L'\r');
		}
	}

	if (bUtf16revert)
		xbTextBOMrevert(str.GetWritePtr(),str.Length);

	return pstmSrc->Write((LPCWSTR)str,str.Length*sizeof(wchar));
}

ulong xbStream_ReadStringA(xbFormatMode fmtDefault,IxbInputStream *pstmSrc,IxbStreamMode *pMode,IxbSeekStream *pSkSrc,sIxbLock *pStmLck,xbStringA *pStr,xbStringA *pStopStr)
{
	xbLock lck(pStmLck);
	xbFormatMode fmt(pMode?pMode->GetFormat():fmtDefault);
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;
	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = pstmSrc->ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + pstmSrc->Read((char*)xbStrExpA(pStr,lg,false),lg * sizeof(char));
		}
	case xbFormatText :
		if (pMode && xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringW str,strStop;
			ulong ret = xbStream_ReadStringTextW(pstmSrc,pSkSrc,&str,pStopStr?&strStop:NULL,pMode);
			if (pStopStr)
				*pStopStr = xbStringA(pMode->GetAppTextCP(),strStop);
			*pStr = xbStringA(pMode->GetAppTextCP(),str);
			return ret;
		}
		if (pMode && pMode->GetAppTextCP() != pMode->GetStreamTextCP())
		{
			xbStringA str,strStop;
			ulong ret = xbStream_ReadStringTextA(pstmSrc,pSkSrc,&str,pStopStr?&strStop:NULL,pMode);
			if (pStopStr)
				::xbCPconvert(pMode->GetStreamTextCP(),strStop,pMode->GetAppTextCP(),pStopStr);
			::xbCPconvert(pMode->GetStreamTextCP(),str,pMode->GetAppTextCP(),pStr);
			return ret;
		}
		return xbStream_ReadStringTextA(pstmSrc,pSkSrc,pStr,pStopStr,pMode);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStream_ReadStringA() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStream_ReadStringW(xbFormatMode fmtDefault,IxbInputStream *pstmSrc,IxbStreamMode *pMode,IxbSeekStream *pSkSrc,sIxbLock *pStmLck,
						   xbStringW *pStr,xbStringW *pStopStr)
{
	xbLock lck(pStmLck);
	xbFormatMode fmt(pMode?pMode->GetFormat():fmtDefault);
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;
	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = pstmSrc->ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + pstmSrc->Read((wchar*)xbStrExpW(pStr,lg,false),lg * sizeof(wchar));
		}
	case xbFormatText :
		if (!pMode || !xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringA str,strStop;
			ulong ret = xbStream_ReadStringTextA(pstmSrc,pSkSrc,&str,pStopStr?&strStop:NULL,pMode);
			if (pStopStr)
				*pStopStr = xbStringW(pMode->GetStreamTextCP(),strStop);
			*pStr = xbStringW(pMode->GetStreamTextCP(),strStop);
			return ret;
		}
		return xbStream_ReadStringTextW(pstmSrc,pSkSrc,pStr,pStopStr,pMode);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStream_ReadStringW() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStream_ReadStringW(xbFormatMode fmtDefault,IxbInputStream *pstmSrc,IxbStreamMode *pMode,IxbSeekStream *pSkSrc,sIxbLock *pStmLck,
						   xbBSTR *pStr,xbBSTR *pStopStr)
{
	xbLock lck(pStmLck);
	xbFormatMode fmt(pMode?pMode->GetFormat():fmtDefault);
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;
	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = pstmSrc->ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + pstmSrc->Read(pStr->GetWritePtr(lg),lg* sizeof(wchar));
		}
	case xbFormatText :
		if (!pMode || !xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringA str,strStop;
			ulong ret = xbStream_ReadStringTextA(pstmSrc,pSkSrc,&str,pStopStr?&strStop:NULL,pMode);
			if (pStopStr)
				*pStopStr = xbBSTR(xbAllocLib,pMode->GetStreamTextCP(),strStop);
			*pStr = xbBSTR(xbAllocLib,pMode->GetStreamTextCP(),strStop);
			return ret;
		}
		return xbStream_ReadStringTextW(pstmSrc,pSkSrc,pStr,pStopStr,pMode);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStream_ReadStringW() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStream_WriteStringA(xbFormatMode fmtDefault,IxbOutputStream *pstmSrc,IxbStreamMode *pMode,sIxbLock *pStmLck,const char *pStr,ulong lgStr,bool bAddLf)
{
	xbLock lck(pStmLck);
	xbFormatMode fmt(pMode?pMode->GetFormat():fmtDefault);
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;
	switch(fmt)
	{
	case xbFormatBin :
		{
			pstmSrc->WriteLong(lgStr);
			if (lgStr <= 0)
				return sizeof(long);
			return sizeof(long) + pstmSrc->Write(pStr,lgStr * sizeof(char));
		}
	case xbFormatText :
		if (pMode && xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringW str(pMode->GetAppTextCP(),pStr,lgStr);
			return xbStream_WriteStringTextW(pstmSrc,str,str.Length,bAddLf,pMode);
		}
		if (pMode && pMode->GetAppTextCP() != pMode->GetStreamTextCP())
		{
			xbStringA str;
			::xbCPconvert(pMode->GetAppTextCP(),pStr,lgStr,pMode->GetStreamTextCP(),&str);
			return xbStream_WriteStringTextA(pstmSrc,str,str.Length,bAddLf,pMode);
		}
		return xbStream_WriteStringTextA(pstmSrc,pStr,lgStr,bAddLf,pMode);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStream_WriteStringA() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStream_WriteStringW(xbFormatMode fmtDefault,IxbOutputStream *pstmSrc,IxbStreamMode *pMode,sIxbLock *pStmLck,const wchar *pStr,ulong lgStr,bool bAddLf)
{
	xbLock lck(pStmLck);
	xbFormatMode fmt(pMode?pMode->GetFormat():fmtDefault);
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;
	switch(fmt)
	{
	case xbFormatBin :
		{
			pstmSrc->WriteLong(lgStr);
			if (lgStr <= 0)
				return sizeof(long);
			return sizeof(long) + pstmSrc->Write(pStr,lgStr * sizeof(wchar));
		}
	case xbFormatText :
		if (pMode && xbTextBOMisUtf16(pMode->GetTextBOMtype()))
			return xbStream_WriteStringTextW(pstmSrc,pStr,lgStr,bAddLf,pMode);

		{
			xbStringA str;
			if (pMode)
				::xbCPconvert(pStr,lgStr,pMode->GetStreamTextCP(),&str);
			else
				::xbCPconvert(pStr,lgStr,CP_ACP,&str);
			return xbStream_WriteStringTextA(pstmSrc,str,str.Length,bAddLf,pMode);
		}
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStream_WriteStringA() :  format not supported");
	}
	return -1;//Never executed
}


void xbTextBOMrevert(wchar *pstr,int cLen)
{ //little endian <-> big endian
	byte	*ptr = (byte*)pstr;
	byte	*ptrEnd = ptr + cLen * sizeof(wchar);
	byte	rev;
	while(ptr < ptrEnd)
	{
		rev = *ptr;
		*ptr = *(ptr+1);
		*(ptr+1) = rev;
		ptr+=2;
	}
}
/*	xbBOM_none = 0,		//no BOM
	xbBOM_utf8 = 1,		//utf 8 : EF BB BF
	xbBOM_utf16le = 2,	//utf 16 little endian: FF FE 
	xbBOM_utf16be = 3,	//utf 16 big endian: FE FF
	xbBOM_utf16leNoBOM = 4,
	xbBOM_utf16beNoBOM = 5,
	xbBOM_utf16 = xbBOM_utf16le,
	xbBOM_utf16noBOM = xbBOM_utf16leNoBOM,
	xbTextBOM_default = xbBOM_utf8
*/
byte s_textBOMbytes_utf8[3] = {0xEF, 0xBB, 0xBF};
byte s_textBOMbytes_utf16le[2] = {0xFE, 0xFF};
byte s_textBOMbytes_utf16be[2] = {0xFF, 0xFE};

void xbTextBOMread(IxbInputStream *pstmSrc,IxbStreamMode *pMode,IxbSeekStream *pSkSrc,sIxbLock *pStmLck,xbTextBOMtype *pbBOMtype)
{
	xbRefQIPtr<IxbSeekStream> prSkSrc;
	if (!pSkSrc)
	{
		prSkSrc = pstmSrc;
		pSkSrc = prSkSrc;
	}
	xbLock lck(pStmLck);

	byte	bom[xbTextBOM_maxLength];
	long	lgrd = pstmSrc->Read(bom,xbTextBOM_maxLength,false);
	if (lgrd < xbTextBOM_minLength)
	{
		pSkSrc->Seek(-lgrd,xbSeekCur);
		if (pbBOMtype)
			*pbBOMtype = xbBOM_none;
		return;
	}
	if (lgrd >= sizeof(s_textBOMbytes_utf8)
		&& memcmp(s_textBOMbytes_utf8,bom,sizeof(s_textBOMbytes_utf8)) == 0)
	{
		lgrd -= sizeof(s_textBOMbytes_utf8);
		if (lgrd>0)
			pSkSrc->Seek(-lgrd,xbSeekCur);
		if (pbBOMtype)
			*pbBOMtype = xbBOM_utf8;
		pMode->SetTextBOMtype(xbBOM_utf8);
		pMode->SetStreamTextCP(CP_UTF8);
		return;
	}
	if (lgrd >= sizeof(s_textBOMbytes_utf16le)
		&& memcmp(s_textBOMbytes_utf16le,bom,sizeof(s_textBOMbytes_utf16le)) == 0)
	{
		lgrd -= sizeof(s_textBOMbytes_utf16le);
		if (lgrd>0)
			pSkSrc->Seek(-lgrd,xbSeekCur);
		if (pbBOMtype)
			*pbBOMtype = xbBOM_utf16le;
		return;
	}
	if (lgrd >= sizeof(s_textBOMbytes_utf16be)
		&& memcmp(s_textBOMbytes_utf16be,bom,sizeof(s_textBOMbytes_utf16be)) == 0)
	{
		lgrd -= sizeof(s_textBOMbytes_utf16be);
		if (lgrd>0)
			pSkSrc->Seek(-lgrd,xbSeekCur);
		if (pbBOMtype)
			*pbBOMtype = xbBOM_utf16be;
		return;
	}
	pSkSrc->Seek(-lgrd,xbSeekCur);
	if (pbBOMtype)
		*pbBOMtype = xbBOM_none;
	pMode->SetStreamTextCP(CP_ACP);
}

void xbTextBOMwrite(IxbOutputStream *pstmSrc,IxbStreamMode *pMode,sIxbLock *pStmLck,xbTextBOMtype BOMtype)
{
	switch(BOMtype)
	{
	case xbBOM_utf8:
		pstmSrc->Write(s_textBOMbytes_utf8,sizeof(s_textBOMbytes_utf8));
		break;
	case xbBOM_utf16le:
		pstmSrc->Write(s_textBOMbytes_utf16le,sizeof(s_textBOMbytes_utf16le));
		break;
	case xbBOM_utf16be:
		pstmSrc->Write(s_textBOMbytes_utf16be,sizeof(s_textBOMbytes_utf16be));
		break;
	}
}

#pragma warning(pop)
