/*<<<***************************************************************************
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: xbString.h
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<29/12/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#ifdef xbSTRING_DEFW
#define _xbSD(name)		name ## W
#define _xbCHAR			wchar
#define _xbUCHAR		wchar
#define _xbSL(str)		L ## str
#define _xbBYTEtoCHAR(cByte) (cByte/sizeof(wchar))
#define _xbPCSTR		const wchar *
#define _xbPSTR			wchar *
#define xbSTRING_TRIMCHARSW	L" \t\r\n"
#else
//xbSTRING_DEFA
#define _xbSD(name)		name ## A
#define _xbCHAR			char
#define _xbUCHAR		uchar
#define _xbSL(str)		str
#define _xbBYTEtoCHAR(cByte) (cByte)
#define _xbPCSTR		const char *
#define _xbPSTR			char *
#define xbSTRING_TRIMCHARSA	" \t\r\n"
#endif

#pragma warning(push)
#pragma warning(disable: 4200) //extension non standard utilisée : tableau de taille zéro dans un struct/union

// comment found in olestd.h !! -> BUGBUG32: isspace function does not seem to work properly
inline bool _xbSD(xbIsSpace)(_xbCHAR ch) 
{
	return (ch==_xbSL(' ') || ch==_xbSL('\t') || ch==_xbSL('\n') || ch==_xbSL('\r'));
}
/*inline bool xbIsWspace(wchar ch)
{
	return ch <= 32 || iswspace(ch);
}*/

#define _xbSTR _xbSD(xbString)
#define _xbSTRDATA _xbSD(xbStringData)

struct xbEXPORT _xbSTRDATA
{
	long			m_cUse;
	long			m_cChar;
#ifdef _xbSTRINGDEBUG
	mutable ulong	m_thid;
#endif
	_xbCHAR m_str[0];

	static _xbSTRDATA *dataNew(_xbPCSTR lpsz)
	{
		return dataNew(lpsz,(int)(lpsz?_xbSD(xbStrlen)(lpsz):0));
	}
	static _xbSTRDATA *dataNew(_xbPCSTR lpsz,int len)
	{
		_xbSTRDATA *pData = dataNew(len);
		if (lpsz)
			memcpy(pData->m_str,lpsz,sizeof(_xbCHAR) * len); 
		return pData;
	}
	static _xbSTRDATA *dataNew(int len)
	{
		_xbSTRDATA *pData = (_xbSTRDATA*)g_xbEngineCenter.m_heap.xbAlloc(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (len+1));
		pData->m_cChar = len;
		pData->m_str[len] = 0;
		pData->m_cUse = 1;
#ifdef _xbSTRINGDEBUG
		pData->m_thid = xbThreadId();
#endif
		return pData;
	}
	_xbSTRDATA *dataRealloc(int len)
	{
#ifdef _xbSTRINGDEBUG
		xbRdbgAssert(m_thid == xbThreadId());
#endif
		if (len == m_cChar)
			return this;
		size_t newSize=g_xbEngineCenter.m_heap.SizeAlign(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (len+1));
		if (newSize == g_xbEngineCenter.m_heap.SizeAlign(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (m_cChar+1)))
		{
			m_cChar = len;
			m_str[len] = 0;
			return this;
		}
		_xbSTRDATA *pData =  (_xbSTRDATA*)g_xbEngineCenter.m_heap.xbReAlloc(this,newSize);
		pData->m_cChar = len;
		pData->m_str[len] = 0;
		return pData;
	}
	static _xbSTRDATA *dataNew(int len,_xbCHAR chInit)
	{
		_xbSTRDATA *pData = (_xbSTRDATA*)g_xbEngineCenter.m_heap.xbAlloc(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (len+1));
		pData->m_cChar = len;
		pData->m_str[len] = 0;
		pData->m_cUse = 1;
#ifdef _xbSTRINGDEBUG
		pData->m_thid = xbThreadId();
#endif
		dataInit(pData->m_str,chInit,len);
		return pData;
	}
	_xbSTRDATA *dataRealloc(int len,_xbCHAR chInit)
	{
#ifdef _xbSTRINGDEBUG
		xbRdbgAssert(m_thid == xbThreadId());
#endif
		if (len == m_cChar)
			return this;
		size_t newSize=g_xbEngineCenter.m_heap.SizeAlign(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (len+1));
		if (newSize == g_xbEngineCenter.m_heap.SizeAlign(sizeof(_xbSTRDATA)+sizeof(_xbCHAR) * (m_cChar+1)))
		{
			if (len > m_cChar)
				dataInit(m_str + m_cChar,chInit,len - m_cChar);
			m_cChar = len;
			m_str[len] = 0;
			return this;
		}
		_xbSTRDATA *pData =  (_xbSTRDATA*)g_xbEngineCenter.m_heap.xbReAlloc(this,newSize);
			if (len > m_cChar)
				dataInit(pData->m_str + m_cChar,chInit,len - m_cChar);
		pData->m_cChar = len;
		pData->m_str[len] = 0;
		return pData;
	}
	void addRef()
	{
#ifdef _xbSTRINGDEBUG
		m_thid = 0;
#endif
		::xbInterlockIncrement32(&m_cUse);
	}
	void release()
	{
		xbRdbgAssert(m_cUse>0);
		if (::xbInterlockDecrement32(&m_cUse) > 0)
			return;

		g_xbEngineCenter.m_heap.Free(this);
	}
	bool isShared() const
	{
		if (m_cUse > 1)
			return true;
#ifdef _xbSTRINGDEBUG
		m_thid = xbThreadId();
#endif
		return false;
	}

	_xbSTRDATA *clone()
	{
		return dataNew(m_str,m_cChar);
	}

	static void dataInit(_xbCHAR *pStr,_xbCHAR chInit,int cChar)
	{
		_xbCHAR *pStrEnd = pStr + cChar;
		while(pStr < pStrEnd)
			*(pStr++) = chInit;
	}
private:
	xbDclNoCopyForObject(_xbSTRDATA);
	xbDclNoNewForObject(_xbSTRDATA);
};

class _xbSTR;
int xbEXPORT _xbSD(xbStrToInt64)(const _xbCHAR *ptr,int64 *pNb,int base,const _xbCHAR **ppStop); /* Renvoie le nbre de digits lus, 0 -> 0 digits, < 0 -> overflow + abs(nb digits lus, -10 -> overflow, 10 digits lus) */
int xbEXPORT _xbSD(xbStrToUint64)(const _xbCHAR *ptr,uint64 *pNb,int base,const _xbCHAR **ppStop);
int64 xbEXPORT _xbSD(xbStrToInt64)(const _xbCHAR *ptr,int base = 10);
uint64 xbEXPORT _xbSD(xbStrToUint64)(const _xbCHAR *ptr,int base = 10);

int xbEXPORT _xbSD(xbStrToLong)(const _xbCHAR *ptr,long *pNb,int base,const _xbCHAR **ppStop);
int xbEXPORT _xbSD(xbStrToUlong)(const _xbCHAR *ptr,ulong *pNb,int base,const _xbCHAR **ppStop);
long xbEXPORT _xbSD(xbStrToLong)(const _xbCHAR *ptr,int base = 10);
ulong xbEXPORT _xbSD(xbStrToUlong)(const _xbCHAR *ptr,int base = 10);

int xbEXPORT _xbSD(xbStrToDouble)(const _xbCHAR *ptr,double *pNb,const _xbCHAR **ppStop);
double xbEXPORT _xbSD(xbStrToDouble)(const _xbCHAR *ptr);
bool xbEXPORT _xbSD(xbStrToGuid)(const _xbCHAR *ptr,GUID *pId,const _xbCHAR **ppStop);
bool xbEXPORT _xbSD(xbStrToBool)(const _xbCHAR *ptr,bool *pBool,const _xbCHAR **ppStop);
bool xbEXPORT _xbSD(xbStrToBool)(const _xbCHAR *ptr);

_xbSTR xbEXPORT _xbSD(xbStrFromInt64)(int64 val, int base = 0, bool bUpperCase = true);
_xbSTR xbEXPORT _xbSD(xbStrFromUint64)(uint64 val,int base = 0, bool bUpperCase = true);
_xbSTR xbEXPORT _xbSD(xbStrFromLong)(long val, int base = 0, bool bUpperCase = true);
_xbSTR xbEXPORT _xbSD(xbStrFromUlong)(ulong val,int base = 0, bool bUpperCase = true);
_xbSTR xbEXPORT _xbSD(xbStrFromDouble)(double val,xbStringNumPrec prec = xbStringNumPrec_full);
_xbSTR xbEXPORT _xbSD(xbStrFromGuid)(const GUID &val,bool bAccolades = true);
_xbSTR xbEXPORT _xbSD(xbStrFromBool)(bool val);
_xbSTR xbEXPORT _xbSD(xbStrFromNptr)(xbNptr val);
_xbSTR xbEXPORT _xbSD(xbStrFromPtr)(const void *pVal);

inline int _xbSD(xbStrToInt)(const _xbCHAR *ptr,int *pNb,int base,const _xbCHAR **ppStop)
{
	return _xbSD(xbStrToLong)(ptr,(long*)pNb,base,ppStop);
}
inline int _xbSD(xbStrToUint)(const _xbCHAR *ptr,uint *pNb,int base,const _xbCHAR **ppStop)
{
	return _xbSD(xbStrToUlong)(ptr,(ulong*)pNb,base,ppStop);
}
inline int _xbSD(xbStrToInt)(const _xbCHAR *ptr,int base = 10)
{
	return (int)_xbSD(xbStrToLong)(ptr,base);
}
inline uint _xbSD(xbStrToUint)(const _xbCHAR *ptr,int base = 10)
{
	return (uint)_xbSD(xbStrToUlong)(ptr,base);
}

class xbEXPORT _xbSTR
{
public:
	_xbSTR()
	{
		m_pData = NULL;
	}

	_xbSTR(const _xbSTR &str)
	{
		m_pData = str.dataAddRef();
	}

	// from an ANSI string
#ifdef xbSTRING_DEFW
	explicit _xbSTR(LPCSTR lpsz);
#else
	_xbSTR(LPCSTR lpsz)
	{
		m_pData = _xbSTRDATA::dataNew(lpsz);
	}
#endif

	// from a UNICODE string
#ifdef xbSTRING_DEFA
	explicit _xbSTR(LPCWSTR lpsz);
#else
	_xbSTR(LPCWSTR lpsz)
	{
		m_pData = _xbSTRDATA::dataNew(lpsz);
	}
#endif

	// subset of characters from an ANSI string
#ifdef xbSTRING_DEFW
	explicit _xbSTR(LPCSTR lpch, int nLength);
#else
	explicit _xbSTR(LPCSTR lpch, int nLength)
	{
		m_pData = _xbSTRDATA::dataNew(lpch,nLength);
	}
#endif

	// subset of characters from a UNICODE string
#ifdef xbSTRING_DEFA
	explicit _xbSTR(LPCWSTR lpch, int nLength);
#else
	explicit _xbSTR(LPCWSTR lpch, int nLength)
	{
		m_pData = _xbSTRDATA::dataNew(lpch,nLength);
	}
#endif
	

#ifdef xbSTRING_DEFW
	// subset of characters from an MBCS string of codepage cp to UNICODE string
	explicit _xbSTR(ulong cp, LPCSTR lpsz, int nLength);
	explicit _xbSTR(ulong cp, LPCSTR lpsz);
	explicit _xbSTR(ulong cp, const xbStringA &str);
	explicit _xbSTR(const xbStringA &str);
	_xbSTR(const xbBSTR &str);
#else
	// subset of characters from a UNICODE string to MBCS codepage cp
	explicit _xbSTR(ulong cp, LPCWSTR lpsz, int nLength);
	explicit _xbSTR(ulong cp, LPCWSTR lpsz);
	explicit _xbSTR(ulong cp, const xbStringW &str);
	explicit _xbSTR(const xbStringW &str);
	explicit _xbSTR(ulong cp, const xbBSTR &str);
	explicit _xbSTR(const xbBSTR &str);
#endif

	explicit _xbSTR(int val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromLong)(val, (int)fmt);
	}
	explicit _xbSTR(unsigned int val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromUlong)(val, (int)fmt);
	}
	explicit _xbSTR(long val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromLong)(val, (int)fmt);
	}
	explicit _xbSTR(unsigned long val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromUlong)(val, (int)fmt);
	}
	explicit _xbSTR(int64 val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromInt64)(val, (int)fmt);
	}
	explicit _xbSTR(uint64 val,xbStringNumFmt fmt = xbStringNum_base10)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromUint64)(val, (int)fmt);
	}
	explicit _xbSTR(bool val)
	{
		m_pData = NULL;
		*this = val?_xbSL("True"):_xbSL("False");
	}
	explicit _xbSTR(double val,xbStringNumPrec prec = xbStringNumPrec_full)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromDouble)(val, prec);
	}
	explicit _xbSTR(const GUID &val,bool bAccolades = true)
	{
		m_pData = NULL;
		*this = _xbSD(xbStrFromGuid)(val, bAccolades);
	}

#ifndef xbINDICE_0BASEDCHECK
	_xbCHAR operator [] (int i) const
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbString.operator[](%d) failure, bad index (len=%d)",i,GetLength()); 
		return m_pData->m_str[i];
	}
#else
private:
	_xbCHAR operator [] (int i) const
	{
		xbThrowEargFmt("xbString.operator[](%d) disabled, xbINDICE_0BASEDCHECK mode"); 
		return 0;
	}
public:
#endif
	_xbCHAR GetAt(int i) const
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbString.operator[](%d) failure, bad index (len=%d)",i,GetLength()); 
		return m_pData->m_str[i];
	}
	void SetAt(int i, _xbCHAR ch)
	{
		if (i < 0 || i >= GetLength())
			xbThrowEargFmt("xbString.SetAt(%d, ...) failure, bad index (len=%d)",i,GetLength()); 
		OwnData();
		m_pData->m_str[i] = ch;
	}
	_xbCHAR unsafeGetAt(int i) const
	{
		return m_pData->m_str[i];
	}
	void unsafeSetAt(int i, _xbCHAR ch)
	{
		OwnData();
		m_pData->m_str[i] = ch;
	}
	_xbCHAR First(int i = 0) const
	{
		if (i < 0 || i >= Length)
			xbThrowEargFmt("xbString.First(%d) const failure, bad index (len=%d)",i,Length); 
		return m_pData->m_str[i];
	}
	_xbCHAR Last(int i = 0) const
	{
		int j = Length - i - 1;
		if (j < 0 || j >= Length)
			xbThrowEargFmt("xbArray.Last(%d) const failure, bad index (len=%d)",i,Length); 
		return m_pData->m_str[j];
	}
	_xbCHAR *Begin()
	{
		return m_pData?m_pData->m_str:NULL;
	}
	_xbCHAR *End()
	{
		return m_pData?m_pData->m_str + GetLength():NULL;
	}
	const _xbCHAR *Begin() const
	{
		return m_pData?m_pData->m_str:NULL;
	}
	const _xbCHAR *End() const
	{
		return m_pData?m_pData->m_str + GetLength():NULL;
	}
	_xbCHAR	*GetWritePtr(int len)
	{
		SetLength(len);
		xbRdbgAssert(m_pData);
		return m_pData->m_str;
	}
	_xbCHAR	*GetWritePtr()
	{
		xbRdbgAssert(m_pData);
		return m_pData->m_str;
	}
	void WritePtrEnd(int len)
	{
		if (len < 0)
			len = (int)_xbSD(xbStrlen)(m_pData->m_str);
		setLength(len);
	}
	bool	IsNull() const
	{
		return !m_pData;
	}
	bool	IsEmpty() const
	{
		return !m_pData || !m_pData->m_cChar;
	}
	bool	IsValid() const
	{
		return m_pData && m_pData->m_cChar;
	}
	void Empty()
	{
		if (!m_pData)
			return;
		m_pData->release();
		m_pData = NULL;
	}
	~_xbSTR()
	{
		eProtectTrace(Empty());
	}

	operator _xbPCSTR() const
	{
		return m_pData?m_pData->m_str:NULL;
	}

	_xbPCSTR Ptr() const
	{
		if (!m_pData)
			xbThrowEargFmt("xbString.Ptr() failure, string is null"); 
		return m_pData->m_str;
	}
	_xbPCSTR ForcePtr() const
	{
		return m_pData?m_pData->m_str:_xbSL("");
	}


	// simple sub-string extraction

	_xbSTR Left(int len) const
	{
		if (len < 0) 
			xbThrowEargFmt("xbString::Left(%d) : bad arg",len);
		if (len >= Length)
			return *this;
		return _xbSTR(*this,len);
	}
	_xbSTR Right(int len) const
	{
		if (len < 0) 
			xbThrowEargFmt("xbString::Left(%d) : bad arg",len);
		if (len >= Length)
			return *this;
		return _xbSTR((_xbPCSTR)*this + Length - len,len);
	}

	// return len characters starting at 0-based start position
	_xbSTR Mid(int start,int len) const
	{
		if (start < 0 || len < 0) 
			xbThrowEargFmt("xbString::Mid(%d,%d) : bad arg",start,len);
		int dtaLen = GetLength();
		if (start >= dtaLen) 
			return _xbSTR();

		len = min(len, dtaLen-start);
		if (len == 0) 
			return _xbSTR();
		
		return _xbSTR((_xbPCSTR)*this+start,len);
	}
	// return all characters starting at one-based start position
	_xbSTR Mid(int start) const
	{
		if (start < 0) 
			xbThrowEargFmt("xbString::Mid(%d) : bad arg",start);
		int dtaLen = GetLength();
		if (start >= dtaLen) 
			return _xbSTR();

		return _xbSTR((_xbPCSTR)*this + start,dtaLen-start);
	}
	void Insert(int pos, _xbCHAR ch)
	{
		Insert(pos,&ch,1);
	}
	// insert substring at 0-based index; concatenates
	// if index is past end of string
	void Insert(int pos, _xbPCSTR pstr)
	{
		Insert(pos,pstr,(int)(pstr?_xbSD(xbStrlen)(pstr):0));
	}
	void Insert(int pos, const _xbSTR &str)
	{
		Insert(pos,str,str.GetLength());
	}
	void Insert(int pos, _xbPCSTR pstr, int len)
	{
		if (pos < 0 || pos > GetLength()) 
			xbThrowEargFmt("xbString::Insert(%d) : bad index",pos);
		if (len <= 0)
			return;
		setLength(GetLength()+len);

		if (GetLength() > pos + len)
			::memmove(m_pData->m_str + pos + len, m_pData->m_str + pos,sizeof(_xbCHAR) * (GetLength() - pos - len));

		if (pstr)
			memcpy(m_pData->m_str + pos,pstr,sizeof(_xbCHAR) * len);
		else
			memset(m_pData->m_str + pos,0,sizeof(_xbCHAR) * len);
	}
	void Insert(int pos, _xbCHAR ch, int len)
	{
		if (pos < 0 || pos > GetLength()) 
			xbThrowEargFmt("xbString::Insert(%d) : bad index",pos);
		if (len <= 0)
			return;
		setLength(GetLength()+len);

		if (GetLength() > pos + len)
			::memmove(m_pData->m_str + pos + len, m_pData->m_str + pos,sizeof(_xbCHAR) * (GetLength() - pos - len));

		_xbSTRDATA::dataInit(m_pData->m_str + pos,ch,len);
	}
	// delete nCount characters starting at 0-based index
	void Delete(int pos, int cnt = 1)
	{
		if (cnt <= 0) 
			return;

		if (pos < 0
			 || pos+cnt > GetLength()) 
			xbThrowEargFmt("xbString::Delete(%d,%d) : bad arg",pos,cnt);

		if (m_pData->m_cChar <= cnt)
		{
			setLength(0);
			return;
		}

		if (m_pData->m_cChar > pos + cnt) 
			::memmove(m_pData->m_str+pos,m_pData->m_str+pos+cnt,sizeof(_xbCHAR) * (m_pData->m_cChar - pos - cnt));
		
		setLength(m_pData->m_cChar - cnt);
	}
	void SetAt(int pos, _xbPCSTR strNew, int newLg = -1,int oldLg = -1)
	{
		if (pos < 0 || pos >= GetLength())
			xbThrowEargFmt("xbString.SetAt(%d, ...) failure, bad index (len=%d)",pos,GetLength()); 
		OwnData();
		if (newLg < 0)
			newLg = (int)(strNew?_xbSD(xbStrlen)(strNew):0);
		if (newLg <= 0)
		{
			if (oldLg > 0)
				Delete(pos,oldLg);
			return;
		}
		if (oldLg >= 0)
		{
			if (oldLg > newLg)
				Delete(pos,oldLg - newLg);
			else
			{
				if (oldLg < newLg)
					Insert(pos,(_xbPCSTR)NULL,newLg - oldLg);
			}
		}
		else
		{
			if (GetLength() < pos + newLg)
				setLength(pos+newLg);
		}

		memcpy(m_pData->m_str + pos,strNew,sizeof(_xbCHAR) * newLg);
	}
	void setLength(int len)
	{
		if (len < 0)
		{
			Empty();
			return;
		}
		if (!m_pData)
		{
			m_pData = _xbSTRDATA::dataNew(len);
			return;
		}
		OwnData();
		if (m_pData->m_cChar == len)
			return;

		m_pData = m_pData->dataRealloc(len);
	}
	void setLength(int len,_xbCHAR chInit)
	{
		if (len < 0)
		{
			Empty();
			return;
		}
		if (!m_pData)
		{
			m_pData = _xbSTRDATA::dataNew(len,chInit);
			return;
		}
		OwnData();
		if (m_pData->m_cChar == len)
			return;

		m_pData = m_pData->dataRealloc(len,chInit);
	}

	void Add(const _xbSTR &str)
	{
		Add(str,str.Length);
	}
	void Add(_xbPCSTR str)
	{
		if (!str)
			return;
		Add(str,(int)_xbSD(xbStrlen)(str));
	}
	void Add(_xbPCSTR pStr,int len)
	{
		if (len <= 0) 
			return;
		int curlen = GetLength();
		setLength(curlen + len);

		memcpy(m_pData->m_str + curlen, pStr, len * sizeof(_xbCHAR));
	}
	void Add(_xbCHAR ch)
	{
		Add(&ch,1);
	}
	void Set(const _xbSTR &str)
	{
		Empty();
		m_pData = str.dataAddRef();
	}
	void Set(_xbPCSTR str)
	{
		if (!str)
			return;
		Set(str,(int)_xbSD(xbStrlen)(str));
	}
	void Set(_xbPCSTR pStr,int len)
	{
		if (len <= 0) 
		{
			Empty();
			return;
		}
		setLength(len);
		memcpy(m_pData->m_str, pStr, len * sizeof(_xbCHAR));
	}

	int GetLength() const
	{
		return m_pData?m_pData->m_cChar:0;
	}
	void SetLength(int lg)
	{
		setLength(lg,0);
	}
	xbPropertyRW(int,Length);

	_xbSTR &operator=(const _xbSTR &str)
	{
		Empty();
		m_pData = str.dataAddRef();
		return *this;
	}
	_xbSTR &operator=(_xbPCSTR str)
	{
		Set(str);
		return *this;
	}

	// searching

	// find character starting at left, -1 if not found
	int IndexOf(_xbCHAR ch) const; //Reverse

	// find character starting at right
	int LastIndexOf(_xbCHAR ch) const;

	// find character starting at 0-based index and going left
	int IndexOf(_xbCHAR ch, int start) const;

	// find character starting at 0-based index and going right
	int LastIndexOf(_xbCHAR ch, int start) const;

	// find first instance of any character in passed string
	int IndexOfChars(_xbPCSTR lpszCharSet, int start = 0) const;

	// find last instance of any character in passed string
	int LastIndexOfChars(_xbPCSTR lpszCharSet, int start = xbSIZE_LIMIT) const;

	// find first instance of substring starting at zero-based index
	int IndexOf(_xbPCSTR lpszSub, int nStart = 0) const
	{
		return IndexOf(lpszSub,-1,nStart);
	}
	int IndexOf(const _xbSTR &sSub, int nStart = 0) const
	{
		return IndexOf(sSub,sSub.Length,nStart);
	}
	int IndexOf(_xbPCSTR lpszSub, int cChSub, int nStart) const;

	// find first instance of substring starting at zero-based index
	int LastIndexOf(_xbPCSTR lpszSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOf(lpszSub,-1,nStart);
	}
	int LastIndexOf(const _xbSTR &sSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOf(sSub,sSub.Length,nStart);
	}
	int LastIndexOf(_xbPCSTR lpszSub, int cChSub, int nStart) const;

	//searching ignoring case (no case)
	// find first instance of substring starting at zero-based index
	int IndexOfNC(_xbPCSTR lpszSub, int nStart = 0) const
	{
		return IndexOfNC(lpszSub,-1,nStart);
	}
	int IndexOfNC(const _xbSTR &sSub, int nStart = 0) const
	{
		return IndexOfNC(sSub,sSub.Length,nStart);
	}
	int IndexOfNC(_xbPCSTR lpszSub, int cChSub, int nStart) const;

	// find first instance of substring starting at zero-based index
	int LastIndexOfNC(_xbPCSTR lpszSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOfNC(lpszSub,-1,nStart);
	}
	int LastIndexOfNC(const _xbSTR &sSub, int nStart = xbSIZE_LIMIT) const
	{
		return LastIndexOfNC(sSub,sSub.Length,nStart);
	}
	int LastIndexOfNC(_xbPCSTR lpszSub, int cChSub, int nStart) const;

	//check start/end of string
	bool StartWith(const _xbSTR &sSub) const;
	bool StartWith(_xbPCSTR sSub) const;
	bool StartWith(_xbPCSTR sSub,int len) const;

	bool EndWith(const _xbSTR &sSub) const;
	bool EndWith(_xbPCSTR sSub) const;
	bool EndWith(_xbPCSTR sSub,int len) const;

	bool StartWithNC(const _xbSTR &sSub) const;
	bool StartWithNC(_xbPCSTR sSub) const;
	bool StartWithNC(_xbPCSTR sSub,int len) const;

	bool EndWithNC(const _xbSTR &sSub) const;
	bool EndWithNC(_xbPCSTR sSub) const;
	bool EndWithNC(_xbPCSTR sSub,int len) const;

	//Make the string the unique owner of pointed buffer
	void OwnData() 
	{
		_xbSTRDATA *pData =m_pData;
		if (!pData)
			return;
		if (!pData->isShared())
			return;
		m_pData = pData->clone();
		pData->release();
	}

	// trimming whitespace (either side)
	void TrimLeft(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS))
	{
		trim(pch,true,false);
	}
	void TrimRight(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS))
	{
		trim(pch,false,true);
	}
	void Trim(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS))
	{
		trim(pch,true,true);
	}

	_xbSTR RetTrimLeft(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS)) const
	{
		_xbSTR ret(*this);
		ret.TrimLeft(pch);
		return ret;
	}
	_xbSTR RetTrimRight(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS)) const
	{
		_xbSTR ret(*this);
		ret.TrimRight(pch);
		return ret;
	}
	_xbSTR RetTrim(_xbPCSTR pch = _xbSD(xbSTRING_TRIMCHARS)) const
	{
		_xbSTR ret(*this);
		ret.Trim(pch);
		return ret;
	}

	// string padding
	void Pad(int cnt,_xbCHAR ch = ' ');
	void PadLeft(int cnt,_xbCHAR ch = ' ');

	_xbSTR RetPad(int cnt,_xbCHAR ch = ' ') const;
	_xbSTR RetPadLeft(int cnt,_xbCHAR ch = ' ') const;

	//	_xbSTR &Mask(const _xbSTR &mask,sIxbStringMask *pMask,int maxCnt = -1);

	// concatenate a xbString
	const _xbSTR& operator+=(const _xbSTR& string);
	// concatenate a zt string
	const _xbSTR& operator+=(_xbPCSTR lpsz);
	// concatenate a single character
	const _xbSTR& operator+=(_xbCHAR ch);

	friend _xbSTR  xbEXPORT operator+(const _xbSTR& string1,const _xbSTR& string2);
	friend _xbSTR  xbEXPORT operator+(const _xbSTR& string, _xbPCSTR lpsz);
	friend _xbSTR  xbEXPORT operator+(_xbPCSTR lpsz, const _xbSTR& string);

	// string comparison

	// straight character comparison
	int Compare(_xbPCSTR lpsz) const;
	int Compare(const _xbSTR &str) const;
	// compare ignoring case
	int CompareNC(_xbPCSTR lpsz) const;
	int CompareNC(const _xbSTR &str) const;
	int CompareNoCase(_xbPCSTR lpsz) const
	{
		return CompareNC(lpsz);
	}
/*	// NLS aware comparison, case sensitive
	int Collate(_xbPCSTR lpsz) const;
	// NLS aware comparison, case insensitive
	int CollateNoCase(_xbPCSTR lpsz) const;
*/
	// upper/lower conversion

	// NLS aware conversion to uppercase
	void Upper();
	_xbSTR RetUpper() const;
	// NLS aware conversion to lowercase
	void Lower();
	_xbSTR RetLower() const;

	// advanced manipulation

	// replace occurrences of chOld with chNew
	int Replace(_xbCHAR chOld, _xbCHAR chNew);
	// replace occurrences of substring lpszOld with lpszNew;
	// empty lpszNew removes instances of lpszOld
	int Replace(_xbPCSTR lpszOld, _xbPCSTR lpszNew);
	int Replace(const _xbSTR &strOld, const _xbSTR &strNew);
	// replace occurrences of substring lpszOld with lpszNew (NC);
	int ReplaceNC(_xbPCSTR lpszOld, _xbPCSTR lpszNew);
	int ReplaceNC(const _xbSTR &strOld, const _xbSTR &strNew);

	_xbSTR RetReplace(_xbCHAR chOld, _xbCHAR chNew) const
	{
		_xbSTR ret(*this);
		ret.Replace(chOld,chNew);
		return ret;
	}
	_xbSTR RetReplace(_xbPCSTR lpszOld, _xbPCSTR lpszNew) const
	{
		_xbSTR ret(*this);
		ret.Replace(lpszOld,lpszNew);
		return ret;
	}
	_xbSTR RetReplace(const _xbSTR &strOld, const _xbSTR &strNew) const
	{
		_xbSTR ret(*this);
		ret.Replace(strOld,strNew);
		return ret;
	}
	_xbSTR RetReplaceNC(_xbPCSTR lpszOld, _xbPCSTR lpszNew) const
	{
		_xbSTR ret(*this);
		ret.ReplaceNC(lpszOld,lpszNew);
		return ret;
	}
	_xbSTR RetReplaceNC(const _xbSTR &strOld, const _xbSTR &strNew) const
	{
		_xbSTR ret(*this);
		ret.ReplaceNC(strOld,strNew);
		return ret;
	}

	// simple formatting

	// printf-like formatting using passed string
	void  Format(_xbPCSTR lpszFormat, ...);
	// printf-like formatting using variable arguments parameter
	void FormatV(_xbPCSTR lpszFormat, va_list ap);

	int	Split(const _xbSTR &strToFind, xbArray<_xbSTR> *pArr,bool bTrim = true) const;
	int	SplitNC(const _xbSTR &strToFind, xbArray<_xbSTR> *pArr,bool bTrim = true) const;
	int	Split(_xbCHAR ch, xbArray<_xbSTR> *pArr,bool bTrim = true) const;
/*
GetToken functions returns:
		return 0; //parse terminated
		return -1; //string not terminated
		return -2; //delimited block not terminated
		else <= input string lg+1
*/
	int	GetToken(int start,_xbPCSTR pSeps,_xbSTR *pStr,_xbSTR *pStrStaying = NULL,_xbCHAR *pChSep = NULL,
		bool bSpcNet = true) const;
	int	GetToken(int start,_xbPCSTR pSeps,_xbSTR *pStr,_xbSTR *pStrStaying,_xbCHAR *pChSep,
		_xbPCSTR pStringDelimiter,_xbPCSTR pDelimiterStart = NULL,_xbPCSTR pDelimiterEnd = NULL,bool bSpcNet = true) const;

	int GetTokenString(int start,_xbPCSTR pStringDelimiter,_xbSTR *pStr,_xbCHAR *pchDelimiter = NULL,
		bool *pbMixedDelimiter = NULL) const;

//	/*virtual*/ void ReadFromStream(IxbStreamMode *pMode,IxbInputStream *pStream);
//	/*virtual*/ void WriteToStream(IxbStreamMode *pMode,IxbOutputStream *pStream) const;

/*
consoleGetArg functions returns:
		return -1; //string not terminated (if bReq = false else exception)
		return -2; //delimited block not terminated (if bReq = false else exception)
		else number of argument
*/
	int consoleGetArg(ulong cp,const _xbSTR &cmd,xbArray<_xbSTR> *pArrArg,xbOpMode opm =xbOp_Required,_xbPCSTR pSeps = NULL) const;
	int consoleGetArg(const _xbSTR &cmd,xbArray<_xbSTR> *pArrArg,xbOpMode opm =xbOp_Required,_xbPCSTR pSeps = NULL) const
	{
		return consoleGetArg(CP_ACP,cmd,pArrArg,opm,pSeps);
	}

#ifdef xbSTRING_DEFW
	BSTR AllocSysString() const;
	BSTR SetSysString(BSTR* pbstr) const;
#else
	BSTR AllocSysString(ulong cp = CP_ACP) const;
	BSTR SetSysString(BSTR* pbstr,ulong cp = CP_ACP) const;
#endif
	_xbPCSTR StrAlloc() const
	{
		_xbSTRDATA *data = dataAddRef();
		if (!data)
			return NULL;
		return data->m_str;
	}
	static void StrFree(_xbPCSTR pStr)
	{
		if (!pStr)
			return;
		_xbSTRDATA *data = (_xbSTRDATA*)(pStr - offsetof(_xbSTRDATA,m_str));
		data->release();
	}
protected:
	void trim(_xbPCSTR pTrimChars,bool bLeft,bool bRight);

	_xbSTRDATA	*m_pData;	
	_xbSTRDATA	*dataAddRef() const
	{
		_xbSTRDATA	*pData = m_pData;
		if (pData)
			pData->addRef();
		return pData;
	}
private:
	xbDclNoNewForObject(_xbSTR);
};

inline  bool  operator==(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) == 0; }
inline  bool  operator==(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) == 0; }
inline  bool  operator==(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) == 0; }

inline  bool  operator!=(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) != 0; }
inline  bool  operator!=(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) != 0; }
inline  bool  operator!=(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) != 0; }

inline  bool  operator<(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) < 0; }
inline  bool  operator<(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) < 0; }
inline  bool  operator<(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) > 0; }

inline  bool  operator>(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) > 0; }
inline  bool  operator>(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) > 0; }
inline  bool  operator>(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) < 0; }

inline  bool  operator<=(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) <= 0; }
inline  bool  operator<=(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) <= 0; }
inline  bool  operator<=(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) >= 0; }

inline  bool  operator>=(const _xbSTR& s1, const _xbSTR& s2)
	{ return s1.Compare(s2) >= 0; }
inline  bool  operator>=(const _xbSTR& s1, _xbPCSTR s2)
	{ return s1.Compare(s2) >= 0; }
inline  bool  operator>=(_xbPCSTR s1, const _xbSTR& s2)
	{ return s2.Compare(s1) <= 0; }

_xbSTR  xbEXPORT operator+(const _xbSTR& string1,const _xbSTR& string2);
_xbSTR  xbEXPORT operator+(const _xbSTR& string, _xbPCSTR lpsz);
_xbSTR  xbEXPORT operator+(_xbPCSTR lpsz, const _xbSTR& string);
_xbSTR  xbEXPORT operator+(const _xbSTR& string, _xbCHAR ch);
_xbSTR  xbEXPORT operator+(_xbCHAR ch, const _xbSTR& string);

class xbEXPORT _xbSD(xbStrExp)
{
public:
	_xbSD(xbStrExp)(_xbSTR *pStr,int lg,bool bZt = true)
	{
		m_ptr = pStr->GetWritePtr(lg);
		m_pStr = pStr;
		m_lg = bZt?-1:lg;
	}
	_xbSD(xbStrExp)()
	{
		m_pStr = NULL;
		m_ptr = NULL;
		m_lg = 0;
	}
	_xbCHAR *Lock(_xbSTR *pStr,int lg,bool bZt = true)
	{
		if (m_pStr) Unlock();
		m_ptr = pStr->GetWritePtr(lg);
		m_pStr = pStr;
		m_lg = bZt?-1:lg;
		return m_ptr;
	}
	void Unlock()
	{
		_xbSTR *pStr = m_pStr;
		if (!pStr) 
			return;
		m_ptr = NULL;
		m_pStr = NULL;
		pStr->WritePtrEnd(m_lg);
	}
	void Unlock(int lg)
	{
		_xbSTR *pStr = m_pStr;
		if (!pStr) 
			return;
		m_ptr = NULL;
		m_pStr = NULL;
		pStr->WritePtrEnd(lg);
	}
	operator _xbCHAR *() 
	{
		if (!m_ptr) 
			xbThrowEsys("xbStrExp() : no string associated");
		return m_ptr;
	}
	/*virtual*/ ~_xbSD(xbStrExp)()
	{
		eProtectTrace(Unlock());
	}
protected:
	_xbSTR		*m_pStr;
	_xbCHAR		*m_ptr;
	int			m_lg;
private:
	xbDclNoNewForObject(_xbSD(xbStrExp));
	xbDclNoCopyForObject(_xbSD(xbStrExp));
};

// printf-like formatting using passed string
_xbSTR xbEXPORT _xbSD(xbFormat)(_xbPCSTR lpszFormat, ...);

// printf-like formatting using variable arguments parameter
_xbSTR xbEXPORT _xbSD(xbFormatV)(_xbPCSTR lpszFormat, va_list ap);

int	xbEXPORT _xbSD(xbSortStringNC)(const _xbSTR &str1,const _xbSTR &str2);
int	xbEXPORT _xbSD(xbFindStringNC)(const _xbSTR &str1,const void *pStr2);
int	xbEXPORT _xbSD(xbSortString)(const _xbSTR &str1,const _xbSTR &str2);
int	xbEXPORT _xbSD(xbFindString)(const _xbSTR &str1,const void *pStr2);

inline _xbSTR _xbSD(xbStringPad)(int lg,_xbCHAR ch = ' ')
{
	_xbSTR ret;
	ret.Pad(lg,ch);
	return ret;
}
#pragma warning(pop)

#undef _xbSD
#undef _xbCHAR
#undef _xbUCHAR
#undef _xbSTR
#undef _xbSL
#undef _xbBYTEtoCHAR
#undef _xbPCSTR
#undef _xbPSTR
