/*<<<***************************************************************************
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: xbData
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbData.h"

void xbDataTrace(const char *pszMessage, const xbData &sData)
{
	xbString msg;
	msg += "\n<";
	msg += pszMessage;
	msg += ">\n";
	int cCnt = 0;
	for (int i=0;i<sData.GetLength();i++)
	{
		if (++cCnt > 32)
		{
			xbRelOutput(msg);
			msg = xbString();
			cCnt = 1;
		}
		xbString    cCha;
		cCha.Format(" %02hX",int(sData.unsafeGetAt(i)));
		msg = msg + cCha;
	}
	xbRelOutput(msg);
}


int   Conv_Hex(BYTE bByte )
//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
{
	int ret;

	if (bByte >=48 && bByte <=57)  ret= bByte - 48;
	else if (bByte >=65 && bByte <=70)  ret= bByte - 65 + 10;
	else 
	{
		xbAssert(!"Conv_Hex() : bad arg");
		ret = 0;
	}

	return ret;
}


TEBCDICmap::TEBCDICmap()
{
	m_Inited = false;
}

void	TEBCDICmap::DefaultCodePage()
{
	NullCodePage();
	InitEBCDIC();
	InitASCII();
	CorrectCP();
	DbgDump("Code page : Default");
}

void	TEBCDICmap::NullCodePage()
{
	for (int i = 0; i < 256; i ++) 
		m_EBCDIC_ASCII[i] = m_ASCII_EBCDIC[i] = 0;
	m_Inited = true;
}

//void CUserControl:: INI_ASCII()
void	TEBCDICmap::InitASCII()
{
	for (int i=32;i<256;i++)
	{
		for (int j=0;j<256;j++) //MMM: err index
		{
			if ( m_EBCDIC_ASCII[j]  ==  (BYTE)(i) ) 
			{
				m_ASCII_EBCDIC[i] = (BYTE)(j);
				break;
			}
		}
	}
}

void	TEBCDICmap::CorrectCP()
{
    m_EBCDIC_ASCII[0] = (BYTE)(32);
	for(int i = 1; i <= 63; i++) 
		m_EBCDIC_ASCII[i] = (BYTE)(1);
}

//void CUserControl:: INI_EBCDIC()
void	TEBCDICmap::InitEBCDIC()
{
    m_EBCDIC_ASCII[64] = (BYTE)(32);
    m_EBCDIC_ASCII[65] = (BYTE)(32);
    m_EBCDIC_ASCII[66] = (BYTE)(226);
    m_EBCDIC_ASCII[67] = (BYTE)(228);
    m_EBCDIC_ASCII[68] = (BYTE)(64);
    m_EBCDIC_ASCII[69] = (BYTE)(225);
    m_EBCDIC_ASCII[70] = (BYTE)(227);
    m_EBCDIC_ASCII[71] = (BYTE)(229);
    m_EBCDIC_ASCII[72] = (BYTE)(92);
    m_EBCDIC_ASCII[73]= (BYTE)(241);
    m_EBCDIC_ASCII[74] = (BYTE)(176);
    m_EBCDIC_ASCII[75] = (BYTE)(46);
    m_EBCDIC_ASCII[76] = (BYTE)(60);
    m_EBCDIC_ASCII[77] = (BYTE)(40);
    m_EBCDIC_ASCII[78] = (BYTE)(43);
    m_EBCDIC_ASCII[79] = (BYTE)(33);
    m_EBCDIC_ASCII[80] = (BYTE)(38);
    m_EBCDIC_ASCII[81] = (BYTE)(123);
    m_EBCDIC_ASCII[82] = (BYTE)(234);
    m_EBCDIC_ASCII[83] = (BYTE)(235);
    m_EBCDIC_ASCII[84] = (BYTE)(125);
    m_EBCDIC_ASCII[85] = (BYTE)(237);
    m_EBCDIC_ASCII[86] = (BYTE)(238);
    m_EBCDIC_ASCII[87] = (BYTE)(239);
    m_EBCDIC_ASCII[88] = (BYTE)(236);
    m_EBCDIC_ASCII[89] = (BYTE)(223);
    m_EBCDIC_ASCII[90] = (BYTE)(167);
    m_EBCDIC_ASCII[91] = (BYTE)(36);
    m_EBCDIC_ASCII[92] = (BYTE)(42);
    m_EBCDIC_ASCII[93] = (BYTE)(41);
    m_EBCDIC_ASCII[94] = (BYTE)(59);
    m_EBCDIC_ASCII[95] = (BYTE)(94);
    m_EBCDIC_ASCII[96] = (BYTE)(45);
    m_EBCDIC_ASCII[97] = (BYTE)(47);
    m_EBCDIC_ASCII[98] = (BYTE)(194);
    m_EBCDIC_ASCII[99] = (BYTE)(196);
    m_EBCDIC_ASCII[100] = (BYTE)(192);
    m_EBCDIC_ASCII[101] = (BYTE)(193);
    m_EBCDIC_ASCII[102] = (BYTE)(195);
    m_EBCDIC_ASCII[103] = (BYTE)(197);
    m_EBCDIC_ASCII[104] = (BYTE)(199);
    m_EBCDIC_ASCII[105] = (BYTE)(209);
    m_EBCDIC_ASCII[106] = (BYTE)(249);
    m_EBCDIC_ASCII[107] = (BYTE)(44);
    m_EBCDIC_ASCII[108] = (BYTE)(37);
    m_EBCDIC_ASCII[109] = (BYTE)(95);
    m_EBCDIC_ASCII[110] = (BYTE)(62);
    m_EBCDIC_ASCII[111] = (BYTE)(63);
    m_EBCDIC_ASCII[112] = (BYTE)(248);
    m_EBCDIC_ASCII[113] = (BYTE)(201);
    m_EBCDIC_ASCII[114] = (BYTE)(202);
    m_EBCDIC_ASCII[115] = (BYTE)(203);
    m_EBCDIC_ASCII[116] = (BYTE)(200);
    m_EBCDIC_ASCII[117] = (BYTE)(205);
    m_EBCDIC_ASCII[118] = (BYTE)(206);
    m_EBCDIC_ASCII[119] = (BYTE)(207);
    m_EBCDIC_ASCII[120] = (BYTE)(204);
    m_EBCDIC_ASCII[121] = (BYTE)(181);
    m_EBCDIC_ASCII[122] = (BYTE)(58);
    m_EBCDIC_ASCII[123] = (BYTE)(163);
    m_EBCDIC_ASCII[124] = (BYTE)(224);
    m_EBCDIC_ASCII[125] = (BYTE)(39);
    m_EBCDIC_ASCII[126] = (BYTE)(61);
    m_EBCDIC_ASCII[127] = (BYTE)(34);
    m_EBCDIC_ASCII[128] = (BYTE)(216);
    m_EBCDIC_ASCII[129] = (BYTE)(97);
    m_EBCDIC_ASCII[130] = (BYTE)(98);
    m_EBCDIC_ASCII[131] = (BYTE)(99);
    m_EBCDIC_ASCII[132] = (BYTE)(100);
    m_EBCDIC_ASCII[133] = (BYTE)(101);
    m_EBCDIC_ASCII[134] = (BYTE)(102);
    m_EBCDIC_ASCII[135] = (BYTE)(103);
    m_EBCDIC_ASCII[136] = (BYTE)(104);
    m_EBCDIC_ASCII[137] = (BYTE)(105);
    m_EBCDIC_ASCII[138] = (BYTE)(171);
    m_EBCDIC_ASCII[139] = (BYTE)(187);
    m_EBCDIC_ASCII[140] = (BYTE)(240);
    m_EBCDIC_ASCII[141] = (BYTE)(253);
    m_EBCDIC_ASCII[142] = (BYTE)(254);
    m_EBCDIC_ASCII[143] = (BYTE)(177);
    m_EBCDIC_ASCII[144] = (BYTE)(91);
    m_EBCDIC_ASCII[145] = (BYTE)(106);
    m_EBCDIC_ASCII[146] = (BYTE)(107);
    m_EBCDIC_ASCII[147] = (BYTE)(108);
    m_EBCDIC_ASCII[148] = (BYTE)(109);
    m_EBCDIC_ASCII[149] = (BYTE)(110);
    m_EBCDIC_ASCII[150] = (BYTE)(111);
    m_EBCDIC_ASCII[151] = (BYTE)(112);
    m_EBCDIC_ASCII[152] = (BYTE)(113);
    m_EBCDIC_ASCII[153] = (BYTE)(114);
    m_EBCDIC_ASCII[154] = (BYTE)(170);
    m_EBCDIC_ASCII[155] = (BYTE)(186);
    m_EBCDIC_ASCII[156] = (BYTE)(230);
    m_EBCDIC_ASCII[157] = (BYTE)(145);
    m_EBCDIC_ASCII[158] = (BYTE)(198);
    m_EBCDIC_ASCII[159] = (BYTE)(164);
    m_EBCDIC_ASCII[160] = (BYTE)(96);
    m_EBCDIC_ASCII[161] = (BYTE)(168);
    m_EBCDIC_ASCII[162] = (BYTE)(115);
    m_EBCDIC_ASCII[163] = (BYTE)(116);
    m_EBCDIC_ASCII[164] = (BYTE)(117);
    m_EBCDIC_ASCII[165] = (BYTE)(118);
    m_EBCDIC_ASCII[166] = (BYTE)(119);
    m_EBCDIC_ASCII[167] = (BYTE)(120);
    m_EBCDIC_ASCII[168] = (BYTE)(121);
    m_EBCDIC_ASCII[169] = (BYTE)(122);
    m_EBCDIC_ASCII[170] = (BYTE)(161);
    m_EBCDIC_ASCII[171] = (BYTE)(184);
    m_EBCDIC_ASCII[172] = (BYTE)(208);
    m_EBCDIC_ASCII[173] = (BYTE)(221);
    m_EBCDIC_ASCII[174] = (BYTE)(222);
    m_EBCDIC_ASCII[175] = (BYTE)(174);
    m_EBCDIC_ASCII[176] = (BYTE)(162);
    m_EBCDIC_ASCII[177] = (BYTE)(35);
    m_EBCDIC_ASCII[178] = (BYTE)(165);
    m_EBCDIC_ASCII[179] = (BYTE)(183);
    m_EBCDIC_ASCII[180] = (BYTE)(169);
    m_EBCDIC_ASCII[181] = (BYTE)(93);
    m_EBCDIC_ASCII[182] = (BYTE)(182);
    m_EBCDIC_ASCII[183] = (BYTE)(188);
    m_EBCDIC_ASCII[184] = (BYTE)(189);
    m_EBCDIC_ASCII[185] = (BYTE)(190);
    m_EBCDIC_ASCII[186] = (BYTE)(172);
    m_EBCDIC_ASCII[187] = (BYTE)(92);
    m_EBCDIC_ASCII[188] = (BYTE)(175);
    m_EBCDIC_ASCII[189] = (BYTE)(126);
    m_EBCDIC_ASCII[190] = (BYTE)(180);
    m_EBCDIC_ASCII[191] = (BYTE)(215);
    m_EBCDIC_ASCII[192] = (BYTE)(233);
    m_EBCDIC_ASCII[193] = (BYTE)(65);
    m_EBCDIC_ASCII[194] = (BYTE)(66);
    m_EBCDIC_ASCII[195] = (BYTE)(67);
    m_EBCDIC_ASCII[196] = (BYTE)(68);
    m_EBCDIC_ASCII[197] = (BYTE)(69);
    m_EBCDIC_ASCII[198] = (BYTE)(70);
    m_EBCDIC_ASCII[199] = (BYTE)(71);
    m_EBCDIC_ASCII[200] = (BYTE)(72);
    m_EBCDIC_ASCII[201]= (BYTE)(73);
    m_EBCDIC_ASCII[202] = (BYTE)(173);
    m_EBCDIC_ASCII[203] = (BYTE)(244);
    m_EBCDIC_ASCII[204] = (BYTE)(246);
    m_EBCDIC_ASCII[205] = (BYTE)(242);
    m_EBCDIC_ASCII[206] = (BYTE)(243);
    m_EBCDIC_ASCII[207] = (BYTE)(245);
    m_EBCDIC_ASCII[208] = (BYTE)(232);
    m_EBCDIC_ASCII[209] = (BYTE)(74);
    m_EBCDIC_ASCII[210] = (BYTE)(75);
    m_EBCDIC_ASCII[211] = (BYTE)(76);
    m_EBCDIC_ASCII[212] = (BYTE)(77);
    m_EBCDIC_ASCII[213] = (BYTE)(78);
    m_EBCDIC_ASCII[214] = (BYTE)(79);
    m_EBCDIC_ASCII[215] = (BYTE)(80);
    m_EBCDIC_ASCII[216] = (BYTE)(81);
    m_EBCDIC_ASCII[217]= (BYTE)(82);
    m_EBCDIC_ASCII[218] = (BYTE)(185);
    m_EBCDIC_ASCII[219] = (BYTE)(251);
    m_EBCDIC_ASCII[220] = (BYTE)(252);
    m_EBCDIC_ASCII[221] = (BYTE)(166);
    m_EBCDIC_ASCII[222] = (BYTE)(250);
    m_EBCDIC_ASCII[223] = (BYTE)(255);
    m_EBCDIC_ASCII[224] = (BYTE)(231);
    m_EBCDIC_ASCII[225] = (BYTE)(247);
    m_EBCDIC_ASCII[226] = (BYTE)(83);
    m_EBCDIC_ASCII[227] = (BYTE)(84);
    m_EBCDIC_ASCII[228] = (BYTE)(85);
    m_EBCDIC_ASCII[229] = (BYTE)(86);
    m_EBCDIC_ASCII[230] = (BYTE)(87);
    m_EBCDIC_ASCII[231] = (BYTE)(88);
    m_EBCDIC_ASCII[232] = (BYTE)(89);
    m_EBCDIC_ASCII[233] = (BYTE)(90);
    m_EBCDIC_ASCII[234] = (BYTE)(178);
    m_EBCDIC_ASCII[235] = (BYTE)(212);
    m_EBCDIC_ASCII[236] = (BYTE)(214);
    m_EBCDIC_ASCII[237] = (BYTE)(210);
    m_EBCDIC_ASCII[238] = (BYTE)(211);
    m_EBCDIC_ASCII[239] = (BYTE)(213);
    m_EBCDIC_ASCII[240] = (BYTE)(48);
    m_EBCDIC_ASCII[241] = (BYTE)(49);
    m_EBCDIC_ASCII[242] = (BYTE)(50);
    m_EBCDIC_ASCII[243] = (BYTE)(51);
    m_EBCDIC_ASCII[244] = (BYTE)(52);
    m_EBCDIC_ASCII[245] = (BYTE)(53);
    m_EBCDIC_ASCII[246] = (BYTE)(54);
    m_EBCDIC_ASCII[247] = (BYTE)(55);
    m_EBCDIC_ASCII[248] = (BYTE)(56);
    m_EBCDIC_ASCII[249] = (BYTE)(57);
    m_EBCDIC_ASCII[250] = (BYTE)(179);
    m_EBCDIC_ASCII[251] = (BYTE)(219);
    m_EBCDIC_ASCII[252] = (BYTE)(220);
    m_EBCDIC_ASCII[253]= (BYTE)(217);
    m_EBCDIC_ASCII[254] = (BYTE)(218);
    m_EBCDIC_ASCII[255] = (BYTE)(255);
}

//void  CUserControl::WINPROCESS_CodePage(char * sFileCodePage)
void  TEBCDICmap::LoadCodePage(const char *pszFileCodePage)
{
	FILE *fp = xbFopen (pszFileCodePage, "r");
	if (!fp) 
		xbThrowEsysFmt("Can't open code page file '%s' !",pszFileCodePage);

	NullCodePage();

	try
	{
		int iIdxEBCDIC = 0;
		int iIdxASCII = 0; //MMM: not init in original code
		
		for (int iP=1;iP<=2000;iP++)
		{
			fseek (fp, ((iP - 1)) * 2, SEEK_SET);
			BYTE bByte1 = fgetc (fp);
			fseek (fp, ((iP - 1)) * 2 + 1, SEEK_SET);
			BYTE bByte2 = fgetc (fp);
			if ( bByte1 > 47  &&  bByte1 < 71  &&  bByte2 > 47  &&  bByte2 < 71 ) 
			{
				int iChrCP = Conv_Hex(bByte1) * 16 + Conv_Hex(bByte2);
				if ( iIdxEBCDIC < 256 ) 
				{
					m_EBCDIC_ASCII[iIdxEBCDIC] = (BYTE)(iChrCP);
					iIdxEBCDIC = iIdxEBCDIC + 1;
				} 
				else  
				{
					m_ASCII_EBCDIC[iIdxASCII] = (BYTE)(iChrCP);
					iIdxASCII = iIdxASCII + 1;
				}
				if ( iIdxEBCDIC  ==  256  &&  iIdxASCII  ==  256 )
					break;
			}
		}
		CorrectCP();
		fclose (fp);
		m_Inited = true;
		DbgDump("Code page : "+xbString(pszFileCodePage));
	}
	catch(...) 
	{
		fclose (fp);
		m_Inited = false;
		throw;
	}
}

void	TEBCDICmap::DbgDump(const xbString &smsg)
{
/*
#ifdef _DEBUG
	xbDbgOutput("\n<<"+smsg+">>\n- Ascii");
	BYTE *ptr = m_EBCDIC_ASCII;
	xbString msg;
	int cCnt = 0;
	for (int i=0; i < 256; i++,ptr++)
	{
		if (++cCnt > 32)
		{
			xbRelOutput(msg);
			msg = xbString();
			cCnt = 1;
		}
		xbString    cCha;
		cCha.Format(" %02hX",int(*ptr));
		msg = msg + cCha;
	}
	xbRelOutput(msg+"\n- Ebcdic");

	ptr = m_ASCII_EBCDIC;
	msg = xbString();
	cCnt = 0;
	for (i=0; i < 256; i++,ptr++)
	{
		if (++cCnt > 32)
		{
			xbRelOutput(msg);
			msg = xbString();
			cCnt = 1;
		}
		xbString    cCha;
		cCha.Format(" %02hX",int(*ptr));
		msg = msg + cCha;
	}
	xbRelOutput(msg);
#endif
*/
}

void	TEBCDICmap::ToEBCDIC(const BYTE *pSrce,BYTE *pDest,int lg) const
{
	xbAssert(m_Inited);
	while(lg > 0)
	{
		*(pDest++) = m_ASCII_EBCDIC[*(pSrce++)];
		lg--;
	}
}

void	TEBCDICmap::ToASCII(const BYTE *pSrce,BYTE *pDest,int lg) const
{
	xbAssert(m_Inited);
	while(lg > 0)
	{
		*(pDest++) = m_EBCDIC_ASCII[*(pSrce++)];
		lg--;
	}
}

xbData &xbData::AddEBCDIC(const TEBCDICmap &map,const void *ptr,int len,int *pNbDel)
{
	if (len <= 0) return *this;

	BYTE	*pIn = (BYTE*)ptr;
	int		iPos = GetLength();
	int		lgOut = len + iPos;
	BYTE	*pOut = (BYTE*)GetWritePtr(lgOut)+iPos;
	
	for (int i=0;i<len;i++,iPos++,pIn++)
	{
		BYTE	cCharEBCDIC = map.ToEBCDIC(*pIn);
        if ( cCharEBCDIC == xbC_IAC ) 
		{
			pOut = (BYTE*)GetWritePtr(++lgOut) + iPos++;
			*(pOut++) = xbC_IAC;
			if (pNbDel) (*pNbDel)++;
		}
		*(pOut++) = cCharEBCDIC;
	}

	return *this;
}

xbData &xbData::AddEBCDICnc(const void *ptr,int len,int *pNbDel)
{
	if (len <= 0) return *this;

	BYTE	*pIn = (BYTE*)ptr;
	int		iPos = GetLength();
	int		lgOut = len + iPos;
	BYTE	*pOut = (BYTE*)GetWritePtr(lgOut)+iPos;
	
	for (int i=0;i<len;i++,iPos++)
	{
        if ( *pIn == xbC_IAC ) 
		{
			pOut = (BYTE*)GetWritePtr(++lgOut) + iPos++;
			*(pOut++) = xbC_IAC;
			if (pNbDel) (*pNbDel)++;
		}
		*(pOut++) = *(pIn++);
	}

	return *this;
}

int	xbData::GetEBCDIClength() const
{
	int		len = 0;
	BYTE	*ptr = (BYTE*)m_pVal;
	BYTE	*pend = ptr + GetLength();

	while(ptr < pend)
	{
		if (*ptr == xbC_IAC && ptr < pend - 1 && *(ptr+1) == xbC_IAC)
			ptr++;
		else
			len++;
		ptr++;
	}

	return len;
}

void	xbData::ParseStr(const char *pStr)
{
	if (!pStr) return;

	for(;;)
	{
		while((*pStr < '0' || *pStr > '9')
			&& (*pStr < 'A' || *pStr > 'F')) 
		{
			if (*pStr == 0) return;
			pStr++;
		}

		int ch;
		if (*pStr >= '0' && *pStr <= '9') ch = int(*pStr) - int('0');
		else ch = int(*pStr) - int('A') + 10;
		pStr++;
		if (*pStr >= '0' && *pStr <= '9') {ch = (ch<<4) + int(*pStr) - int('0'); pStr++;}
		else if (*pStr >= 'A' && *pStr <= 'F') {ch = (ch<<4) + int(*pStr) - int('A') + 10; pStr++;}

		operator<<(ch);
	}
}

int xbData::IndexOf(char ch) const
{
	if (!m_pVal)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pChEnd = pChBegin + GetLength(); 
	LPCSTR pCh = pChBegin; 
	while(pCh < pChEnd)
	{
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
		pCh++;
	}
	return -1;
}
// find character starting at right
int xbData::LastIndexOf(char ch) const
{
	if (!m_pVal)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pCh = pChBegin + GetLength(); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
	}
	return -1;
}

int xbData::IndexOf(char ch,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::Find(start=%d) : bad index",start);
	if (!m_pVal)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pChEnd = pChBegin + GetLength(); 
	LPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
		pCh++;
	}
	return -1;
}
// find character starting at right
int xbData::LastIndexOf(char ch,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::ReverseFind(start=%d) : bad index",start);
	if (!m_pVal)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pCh = pChBegin + min(GetLength(),start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
	}
	return -1;
}

// find first instance of any character in passed string
int xbData::IndexOfChars(LPCSTR lpszCharSet,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::IndexOfChars(start=%d) : bad index",start);
	if (!m_pVal || !lpszCharSet)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pChEnd = pChBegin + GetLength(); 
	LPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		LPCSTR pSep = lpszCharSet; 
		while(*pSep)
		{
			if (*pCh == *pSep)
				return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
			pSep++;
		}
		pCh++;
	}
	return -1;
}
int xbData::LastIndexOfChars(LPCSTR lpszCharSet,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::LastIndexOfChars(start=%d) : bad index",start);
	if (!m_pVal || !lpszCharSet)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pCh = pChBegin + min(GetLength(),start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		LPCSTR pSep = lpszCharSet; 
		while(*pSep)
		{
			if (*pCh == *pSep)
				return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
			pSep++;
		}
	}
	return -1;
}

// find first instance of substring starting at zero-based index
int xbData::IndexOf(LPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::IndexOf(start=%d) : bad index",start);
	if (!m_pVal)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?xbStrlen(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pChEnd = pChBegin + GetLength() - cChSub + 1;
	LPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		if (xbStrncmp(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
		pCh++;
	}
	return -1;
}

int xbData::LastIndexOf(LPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::LastIndexOf(start=%d) : bad index",start);
	if (!m_pVal)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?xbStrlen(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	LPCSTR pChBegin = (LPCSTR)m_pVal; 
	LPCSTR pCh = pChBegin + min(GetLength()-cChSub+1,start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (xbStrncmp(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(char));
	}
	return -1;
}
