// String.cpp: implementation of the CXMLNode class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "string.h"
#include <tchar.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#endif
//////////////////////////////////////////////////////////////////////
String::String()
{
InitValues();
}
String::String(const String& string)
{
InitValues();
Assign(string.m_pBuffer, string.GetLength());
}
String::String(LPCTSTR lpsz)
{
InitValues();
*this += lpsz;
}
String::String(char ch, int nRepeat /*= 1*/)
{
InitValues();
ReleaseBuffer(nRepeat);
memset(m_pBuffer, ch, nRepeat);
}
String::String(LPCTSTR lpch, int nLength)
{
InitValues();
memcpy(GetBuffer(nLength), lpch, nLength);
}
String::String(const unsigned char* lpsz)
{
InitValues();
*this = (LPCSTR)lpsz;
}
int StrLen(const char* p)
{
return p ? (int)strlen(p) : 0;
}
void String::InitValues()
{
m_pBuffer = NULL;
m_nAllocLength = 0;
m_nLength = 0;
m_nGrowBytes = 100;
}
String::~String()
{
Empty();
}
void String::SetGrowBytes(int nGrowBytes)
{
m_nGrowBytes = nGrowBytes;
}
int String::GetGrowBytes()
{
return m_nGrowBytes;
}
void String::TrimRight(TCHAR chTarget)
{
if(m_pBuffer)
while(m_nLength && chTarget == m_pBuffer[m_nLength-1])
m_pBuffer[--m_nLength] = 0;
}
void String::TrimRight()
{
TrimRight(" \r\n\t");
}
void String::TrimRight(LPCTSTR lpszTargets)
{
if(m_pBuffer)
while(m_nLength && strchr(lpszTargets, m_pBuffer[m_nLength-1]) != NULL)
m_pBuffer[--m_nLength] = 0;
}
void String::TrimLeft(TCHAR chTarget)
{
if(m_pBuffer)
while(m_nLength && chTarget == m_pBuffer[0])
Delete(0);
}
void String::TrimLeft()
{
TrimLeft(" \r\n\t");
}
void String::TrimLeft(LPCTSTR lpszTargets)
{
if(m_pBuffer)
while(m_nLength && strchr(lpszTargets, m_pBuffer[0]) != NULL)
Delete(0);
}
#define TCHAR_ARG TCHAR
#define WCHAR_ARG WCHAR
#define CHAR_ARG char
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
#define FORCE_INT64 0x40000
void String::Format(LPCTSTR lpszFormat, ...)
{
va_list argList;
va_start(argList, lpszFormat);
va_list argListSave = argList;
// make a guess at the maximum length of the resulting string
int nMaxLen = 0;
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
{
nMaxLen += (int)_tclen(lpsz);
continue;
}
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0;
for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// check for valid flags
if (*lpsz == '#')
nMaxLen += 2; // for '0x'
else if (*lpsz == '*')
nWidth = va_arg(argList, int);
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
*lpsz == ' ')
;
else // hit non-flag character
break;
}
// get width and skip it
if (nWidth == 0)
{
// width indicated by
nWidth = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
;
}
// ASSERT(nWidth >= 0);
int nPrecision = 0;
if (*lpsz == '.')
{
// skip past '.' separator (width.precision)
lpsz = _tcsinc(lpsz);
// get precision and skip it
if (*lpsz == '*')
{
nPrecision = va_arg(argList, int);
lpsz = _tcsinc(lpsz);
}
else
{
nPrecision = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
;
}
// ASSERT(nPrecision >= 0);
}
// should be on type modifier or specifier
int nModifier = 0;
if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
{
lpsz += 3;
nModifier = FORCE_INT64;
#if !defined(_X86_) && !defined(_ALPHA_)
// __int64 is only available on X86 and ALPHA platforms
// ASSERT(FALSE);
#endif
}
else
{
switch (*lpsz)
{
// modifiers that affect size
case 'h':
nModifier = FORCE_ANSI;
lpsz = _tcsinc(lpsz);
break;
case 'l':
nModifier = FORCE_UNICODE;
lpsz = _tcsinc(lpsz);
break;
// modifiers that do not affect size
case 'F':
case 'N':
case 'L':
lpsz = _tcsinc(lpsz);
break;
}
}
// now should be on specifier
switch (*lpsz | nModifier)
{
// single characters
case 'c':
case 'C':
nItemLen = 2;
va_arg(argList, TCHAR_ARG);
break;
case 'c'|FORCE_ANSI:
case 'C'|FORCE_ANSI:
nItemLen = 2;
va_arg(argList, CHAR_ARG);
break;
case 'c'|FORCE_UNICODE:
case 'C'|FORCE_UNICODE:
nItemLen = 2;
va_arg(argList, WCHAR_ARG);
break;
// strings
case 's':
{
LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
case 'S':
{
#ifndef _UNICODE
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = (int)wcslen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
#else
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlenA(pstrNextArg);
nItemLen = max(1, nItemLen);
}
#endif
}
break;
case 's'|FORCE_ANSI:
case 'S'|FORCE_ANSI:
{
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = lstrlenA(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
case 's'|FORCE_UNICODE:
case 'S'|FORCE_UNICODE:
{
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = (int)wcslen(pstrNextArg);
nItemLen = max(1, nItemLen);
}
}
break;
}
// adjust nItemLen for strings
if (nItemLen != 0)
{
if (nPrecision != 0)
nItemLen = min(nItemLen, nPrecision);
nItemLen = max(nItemLen, nWidth);
}
else
{
switch (*lpsz)
{
// integers
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
if (nModifier & FORCE_INT64)
va_arg(argList, __int64);
else
va_arg(argList, int);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'e':
case 'g':
case 'G':
va_arg(argList, double/*DOUBLE_ARG*/);
nItemLen = 128;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'f':
{
double f;
LPTSTR pszTemp;
// 312 == strlen("-1+(309 zeroes).")
// 309 zeroes == max precision of a double
// 6 == adjustment in case precision is not specified,
// which means that the precision defaults to 6
pszTemp = (LPTSTR)malloc(max(nWidth, 312+nPrecision+6));
f = va_arg(argList, double);
_stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
nItemLen = (int)_tcslen(pszTemp);
free(pszTemp);
}
break;
case 'p':
va_arg(argList, void*);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
// no output
case 'n':
va_arg(argList, int*);
break;
// default:
// ASSERT(FALSE); // unknown formatting option
}
}
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen;
}
_vstprintf(GetBuffer(nMaxLen), lpszFormat, argListSave);
ReleaseBuffer();
va_end(argListSave);
va_end(argList);
}
LPTSTR String::GetBuffer(int nMinBufLength)
{
if(nMinBufLength > (int)m_nLength)
ReleaseBuffer(nMinBufLength);
return (LPTSTR)m_pBuffer;
}
void String::AllocBuffer(int nAllocLength)
{
if(nAllocLength <= m_nAllocLength)
return;
m_nAllocLength = nAllocLength;
if(m_pBuffer == NULL)
m_pBuffer = (char*)AllocPtr(m_nAllocLength);
else
m_pBuffer = (char*)ReAllocPtr(m_pBuffer, m_nAllocLength);
}
void String::ReleaseBuffer(int nNewLength /*= -1*/)
{
if(nNewLength == -1)
nNewLength = StrLen(m_pBuffer);
if(nNewLength == 0)
Empty();
else if(nNewLength >= m_nAllocLength)
AllocBuffer(nNewLength+m_nGrowBytes+1);
m_nLength = nNewLength;
if(m_pBuffer)
m_pBuffer[m_nLength] = 0;
}
void String::Empty()
{
if(m_pBuffer)
{
FreePtr(m_pBuffer);
m_pBuffer = NULL;
}
m_nAllocLength = 0;
m_nLength = 0;
}
const String& String::Assign(char* pBuffer, int nLength)
{
if(pBuffer && nLength > 0)
{
ReleaseBuffer(nLength);
memcpy(m_pBuffer, pBuffer, nLength);
}
else
Empty();
return *this;
}
void String::Attach(char* pBuffer, int nLength)
{
Empty();
m_pBuffer = pBuffer;
m_nLength = nLength;
m_nAllocLength = nLength;
}
char *String::Detach()
{
char *pBuffer = m_pBuffer;
m_pBuffer = NULL;
Empty();
return pBuffer;
}
//////////////////////////////////////////////////////////////////////////////
// Assignment String::operators
const String& String::operator=(const String& string)
{
LPCSTR lpcs = string;
return Assign((char*)lpcs, string.GetLength());
}
const String& String::operator=(LPCTSTR lpsz)
{
return Assign((char*)lpsz, StrLen(lpsz));
}
const String& String::operator=(LPSTR lpsz)
{
return Assign(lpsz, StrLen(lpsz));
}
const String& String::operator=(int n)
{
return Assign<int>(n);
}
const String& String::operator=(short s)
{
return Assign<short>(s);
}
const String& String::operator=(double d)
{
return Assign<double>(d);
}
const String& String::operator=(float f)
{
return Assign<float>(f);
}
const String& String::operator=(char c)
{
return Assign<char>(c);
}
//////////////////////////////////////////////////////////////////////////////
// Concatenation String::operators
const String& String::Append(char* pBuffer, int nLength)
{
int nCurLength = m_nLength;
ReleaseBuffer(m_nLength+nLength);
memcpy(m_pBuffer+nCurLength, pBuffer, nLength);
return *this;
}
const String& String::operator+=(const String& string)
{
LPCSTR lpcs = string;
return Append((char*)lpcs, string.GetLength());
}
const String& String::operator+=(LPCTSTR lpsz)
{
return Append((char*)lpsz, StrLen(lpsz));
}
const String& String::operator+=(LPTSTR lpsz)
{
return Append((char*)lpsz, StrLen(lpsz));
}
const String& String::operator+=(const unsigned char* lpsz)
{
return Append((char*)lpsz, StrLen((char*)lpsz));
}
const String& String::operator+=(int n)
{
return Append<int>(n);
}
const String& String::operator+=(short s)
{
return Append<short>(s);
}
const String& String::operator+=(double d)
{
return Append<double>(d);
}
const String& String::operator+=(float f)
{
return Append<float>(f);
}
const String& String::operator+=(char c)
{
return Append<char>(c);
}
///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access
LPTSTR String::GetBufferSetLength(int nNewLength)
{
ReleaseBuffer(nNewLength);
return (LPTSTR)m_pBuffer;
}
void String::FreeExtra()
{
}
///////////////////////////////////////////////////////////////////////////////
// Commonly used routines (rarely used routines in STREX.CPP)
int String::Find(TCHAR ch) const
{
return Find(ch, 0);
}
int String::Find(TCHAR ch, int nStart) const
{
if(m_pBuffer == NULL)
return -1;
LPTSTR lpsz = strchr(m_pBuffer+nStart, ch);
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}
int String::FindOneOf(LPCTSTR lpszCharSet) const
{
if(m_pBuffer == NULL)
return -1;
LPTSTR lpsz = _tcspbrk(m_pBuffer, lpszCharSet);
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}
void String::MakeUpper()
{
if(m_pBuffer == NULL)
return;
_tcsupr((LPTSTR)m_pBuffer);
}
void String::MakeLower()
{
if(m_pBuffer == NULL)
return;
_tcslwr((LPTSTR)m_pBuffer);
}
void String::MakeReverse()
{
if(m_pBuffer == NULL)
return;
_tcsrev((LPTSTR)m_pBuffer);
}
void String::SetAt(int nIndex, TCHAR ch)
{
if(nIndex >= m_nLength)
GetBuffer(nIndex+1);
m_pBuffer[nIndex] = ch;
}
//////////////////////////////////////////////////////////////////////////////
// Advanced manipulation
int String::Delete(int nIndex, int nCount /* = 1 */)
{
if (nIndex < 0)
nIndex = 0;
int nNewLength = m_nLength;
if (nCount > 0 && nIndex < nNewLength)
{
int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
memcpy((char*)m_pBuffer + nIndex,
m_pBuffer + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
ReleaseBuffer(nNewLength - nCount);
}
return nNewLength;
}
int String::Insert(int nIndex, char* pBuffer, int nLength)
{
if(nIndex > m_nLength || nLength <= 0)
return m_nLength;
int nCurLength = m_nLength;
ReleaseBuffer(m_nLength+nLength);
memmove(m_pBuffer+nIndex+nLength, m_pBuffer+nIndex, nCurLength-nIndex);
memcpy(m_pBuffer+nIndex, pBuffer, nLength);
return m_nLength;
}
int String::Replace(TCHAR chOld, TCHAR chNew)
{
int nCount = 0;
// short-circuit the nop case
if (chOld != chNew)
{
// otherwise modify each character that matches in the string
LPTSTR psz = (char*)m_pBuffer;
LPTSTR pszEnd = psz + m_nLength;
while (psz < pszEnd)
{
// replace instances of the specified character only
if (*psz == chOld)
{
*psz = chNew;
nCount++;
}
psz = _tcsinc(psz);
}
}
return nCount;
}
int String::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
{
int nPos, nCount = 0;
char *p;
while((p = strstr(m_pBuffer, lpszOld)) != NULL)
{
nPos = (int)(p-m_pBuffer);
Delete(nPos, (int)strlen(lpszOld));
Insert(nPos, lpszNew);
nCount++;
}
return nCount;
}
int String::Remove(TCHAR chRemove)
{
int nLength = m_nLength;
LPTSTR pstrSource = (char*)m_pBuffer;
LPTSTR pstrDest = (char*)m_pBuffer;
LPTSTR pstrEnd = (char*)m_pBuffer + nLength;
while (pstrSource < pstrEnd)
{
if (*pstrSource != chRemove)
{
*pstrDest = *pstrSource;
pstrDest = _tcsinc(pstrDest);
}
pstrSource = _tcsinc(pstrSource);
}
*pstrDest = '\0';
int nCount = (int)(pstrSource - pstrDest);
ReleaseBuffer(nLength-nCount);
return nCount;
}
//////////////////////////////////////////////////////////////////////////////
// Very simple sub-string extraction
String String::Mid(int nFirst) const
{
return Mid(nFirst, m_nLength - nFirst);
}
String String::Mid(int nFirst, int nCount) const
{
String s;
memcpy(s.GetBuffer(nCount), m_pBuffer+nFirst, nCount);
s.ReleaseBuffer(nCount);
return s;
}
String String::Right(int nCount) const
{
String s;
memcpy(s.GetBuffer(nCount), m_pBuffer+m_nLength-nCount, nCount);
s.ReleaseBuffer(nCount);
return s;
}
String String::Left(int nCount) const
{
String s;
memcpy(s.GetBuffer(nCount), m_pBuffer, nCount);
s.ReleaseBuffer(nCount);
return s;
}
// strspn equivalent
String String::SpanIncluding(LPCTSTR lpszCharSet) const
{
return Left((int)_tcsspn(m_pBuffer, lpszCharSet));
}
// strcspn equivalent
String String::SpanExcluding(LPCTSTR lpszCharSet) const
{
return Left((int)_tcscspn(m_pBuffer, lpszCharSet));
}
//////////////////////////////////////////////////////////////////////////////
// Finding
int String::ReverseFind(TCHAR ch) const
{
if(m_pBuffer == NULL)
return -1;
const BYTE* p = (const BYTE*)m_pBuffer;
BYTE* lpsz = _mbsrchr(p, ch);
return (lpsz == NULL) ? -1 : (int)(lpsz - p);
}
// find a sub-string (like strstr)
int String::Find(LPCTSTR lpszSub) const
{
return Find(lpszSub, 0);
}
int String::Find(LPCTSTR lpszSub, int nStart) const
{
if(m_pBuffer == NULL)
return -1;
LPTSTR lpsz = strstr(m_pBuffer+nStart, lpszSub);
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pBuffer);
}
// String
const String& String::operator=(const unsigned char* lpsz)
{ *this = (LPCSTR)lpsz; return *this; }
int String::GetLength() const
{ return m_nLength; }
int String::GetAllocLength() const
{ return m_nAllocLength; }
BOOL String::IsEmpty() const
{ return m_nLength == 0; }
String::operator LPCTSTR() const
{ return (LPCTSTR)m_pBuffer; }
// String support (windows specific)
int String::Compare(LPCTSTR lpsz) const
{ return _tcscmp(m_pBuffer, lpsz); } // MBCS/Unicode aware
int String::CompareNoCase(LPCTSTR lpsz) const
{ return _tcsicmp(m_pBuffer, lpsz); } // MBCS/Unicode aware
int String::Compare(BYTE *pBuffer, int nLength)
{
int nResult = memcmp(m_pBuffer, pBuffer, min(m_nLength, nLength));
if(nResult != 0 || m_nLength == nLength)
return nResult;
return m_nLength > nLength ? 1 : -1;
}
// String::Collate is often slower than Compare but is MBSC/Unicode
// aware as well as locale-sensitive with respect to sort order.
int String::Collate(LPCTSTR lpsz) const
{ return _tcscoll(m_pBuffer, lpsz); } // locale sensitive
int String::CollateNoCase(LPCTSTR lpsz) const
{ return _tcsicoll(m_pBuffer, lpsz); } // locale sensitive
TCHAR String::GetAt(int nIndex) const
{
return m_pBuffer[nIndex];
}
TCHAR String::operator[](int nIndex) const
{
return m_pBuffer[nIndex];
}
String operator+(const String& string1, const String& string2)
{
String s = string1;
s += string2;
return s;
}
String operator+(const String& string, char ch)
{
String s = string;
s += ch;
return s;
}
String operator+(char ch, const String& string)
{
String s = ch;
s += string;
return s;
}
String operator+(const String& string, short s)
{
String str = string;
str += s;
return str;
}
String operator+(short s, const String& string)
{
String str;
str = s;
str += string;
return str;
}
String operator+(const String& string, LPCTSTR lpsz)
{
String s = string;
s += lpsz;
return s;
}
String operator+(LPCTSTR lpsz, const String& string)
{
String s = (LPCSTR)lpsz;
s += string;
return s;
}
bool operator==(const String& s1, const String& s2)
{
return s1.GetLength() == s2.GetLength() && memcmp(s1, s2, s1.GetLength()) == 0;
}
bool operator==(const String& s1, LPCTSTR s2)
{
return s1.GetLength() == StrLen(s2) && memcmp(s1, s2, s1.GetLength()) == 0;
}
bool operator==(LPCTSTR s1, const String& s2)
{
return s2.GetLength() == StrLen(s1) && memcmp(s2, s1, s2.GetLength()) == 0;
}
bool operator!=(const String& s1, const String& s2)
{
return s1.GetLength() != s2.GetLength() || memcmp(s1, s2, s1.GetLength()) != 0;
}
bool operator!=(const String& s1, LPCTSTR s2)
{
return s1.GetLength() != StrLen(s2) || memcmp(s1, s2, s1.GetLength()) != 0;
}
bool operator!=(LPCTSTR s1, const String& s2)
{
return s2.GetLength() != StrLen(s1) || memcmp(s2, s1, s2.GetLength()) != 0;
}
bool operator<(const String& s1, const String& s2)
{
return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) < 0;
}
bool operator<(const String& s1, LPCTSTR s2)
{
return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) < 0;
}
bool operator<(LPCTSTR s1, const String& s2)
{
return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) < 0;
}
bool operator>(const String& s1, const String& s2)
{
return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) > 0;
}
bool operator>(const String& s1, LPCTSTR s2)
{
return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) > 0;
}
bool operator>(LPCTSTR s1, const String& s2)
{
return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) > 0;
}
bool operator<=(const String& s1, const String& s2)
{
return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) <= 0;
}
bool operator<=(const String& s1, LPCTSTR s2)
{
return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) <= 0;
}
bool operator<=(LPCTSTR s1, const String& s2)
{
return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) <= 0;
}
bool operator>=(const String& s1, const String& s2)
{
return memcmp(s1, s2, min(s1.GetLength(), s2.GetLength())) >= 0;
}
bool operator>=(const String& s1, LPCTSTR s2)
{
return memcmp(s1, s2, min(s1.GetLength(), (int)StrLen(s2))) >= 0;
}
bool operator>=(LPCTSTR s1, const String& s2)
{
return memcmp(s1, s2, min((int)StrLen(s1), s2.GetLength())) >= 0;
}