Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1613 lines
42 KiB

//***************************************************************************
//
// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved
//
// CHSTRING.CPP
//
// Purpose: utility library version of MFC CString
//
//***************************************************************************
#include "precomp.h"
#pragma warning( disable : 4290 )
#include <chstring.h>
#include <stdio.h>
#include <comdef.h>
#include <AssertBreak.h>
#include <ScopeGuard.h>
#define _wcsinc(_pc) ((_pc)+1)
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
#define DEPRECATED 0
const CHString& afxGetEmptyCHString();
#define afxEmptyCHString afxGetEmptyCHString()
// Global data used for LoadString.
#if 0
HINSTANCE g_hModule = GetModuleHandle(NULL); // Default to use the process module.
#endif
#ifdef FRAMEWORK_ALLOW_DEPRECATED
void WINAPI SetCHStringResourceHandle(HINSTANCE handle)
{
ASSERT_BREAK(DEPRECATED);
#if 0
g_hModule = handle;
#endif
}
#endif
/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines
/////////////////////////////////////////////////////////////////////////////
WCHAR afxChNil = '\0';
static DWORD GetPlatformID(void)
{
OSVERSIONINFOA version;
version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA) ;
GetVersionExA(&version);
return version.dwPlatformId;
}
static DWORD s_dwPlatformID = GetPlatformID();
/////////////////////////////////////////////////////////////////////////////
// For an empty string, m_pchData will point here
// (note: avoids special case of checking for NULL m_pchData)
// empty string data (and locked)
/////////////////////////////////////////////////////////////////////////////
static int rgInitData[] = { -1, 0, 0, 0 };
static CHStringData* afxDataNil = (CHStringData*)&rgInitData;
LPCWSTR afxPchNil = (LPCWSTR)(((BYTE*)&rgInitData)+sizeof(CHStringData));
/////////////////////////////////////////////////////////////////////////////
// special function to make EmptyString work even during initialization
/////////////////////////////////////////////////////////////////////////////
// special function to make afxEmptyString work even during initialization
const CHString& afxGetEmptyCHString()
{
return *(CHString*)&afxPchNil;
}
///////////////////////////////////////////////////////////////////////////////
// CHString conversion helpers (these use the current system locale)
///////////////////////////////////////////////////////////////////////////////
int _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
{
if (count == 0 && mbstr != NULL)
{
return 0;
}
int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, count, NULL, NULL);
ASSERT_BREAK(mbstr != NULL || result <= (int)count);
if (result > 0)
{
mbstr[result-1] = 0;
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
int _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
{
if (count == 0 && wcstr != NULL)
{
return 0;
}
int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,wcstr, count);
ASSERT_BREAK(wcstr != NULL || result <= (int)count);
if (result > 0)
{
wcstr[result-1] = 0;
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
//*************************************************************************
//
// THE CHSTRING CLASS: PROTECTED MEMBER FUNCTIONS
//
//*************************************************************************
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// implementation helpers
///////////////////////////////////////////////////////////////////////////////
CHStringData* CHString::GetData() const
{
if( m_pchData == (WCHAR*)*(&afxPchNil))
{
return (CHStringData *)afxDataNil;
}
return ((CHStringData*)m_pchData)-1;
}
//////////////////////////////////////////////////////////////////////////////
//
// Function: Init
//
// Description: This function initializes the data ptr
//
///////////////////////////////////////////////////////////////////////////////
void CHString::Init()
{
m_pchData = (WCHAR*)*(&afxPchNil);
}
//////////////////////////////////////////////////////////////////////////////
//
// Function: AllocCopy
//
// Description: This function will clone the data attached to this
// string allocating 'nExtraLen' characters, it places
// results in uninitialized string 'dest' and will copy
// the part or all of original data to start of new string
//
//////////////////////////////////////////////////////////////////////////////
void CHString::AllocCopy( CHString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const
{
int nNewLen = nCopyLen + nExtraLen;
if (nNewLen == 0)
{
dest.Init();
}
else
{
dest.AllocBuffer(nNewLen);
memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(WCHAR));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Function: AllocBuffer
//
// Description: Always allocate one extra character for '\0'
// termination. assumes [optimistically] that
// data length will equal allocation length
//
//////////////////////////////////////////////////////////////////////////////
void CHString::AllocBuffer(int nLen)
{
ASSERT_BREAK(nLen >= 0);
ASSERT_BREAK(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
if (nLen == 0)
{
Init();
}
else
{
CHStringData* pData = (CHStringData*)new BYTE[sizeof(CHStringData) + (nLen+1)*sizeof(WCHAR)];
if ( pData )
{
pData->nRefs = 1;
pData->data()[nLen] = '\0';
pData->nDataLength = nLen;
pData->nAllocLength = nLen;
m_pchData = pData->data();
}
else
{
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Function: AssignCopy
//
// Description: Assigns a copy of the string to the current data ptr
//
//
//////////////////////////////////////////////////////////////////////////////
void CHString::AssignCopy(int nSrcLen, LPCWSTR lpszSrcData)
{
// Call this first, it will release the buffer if it has
// already been allocated and no one is using it
AllocBeforeWrite(nSrcLen);
// Now, check to see if the nSrcLen is > 0, if it is, then
// continue, otherwise, go ahead and return
if( nSrcLen > 0 )
{
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(WCHAR));
GetData()->nDataLength = nSrcLen;
m_pchData[nSrcLen] = '\0';
}
else
{
Release();
}
}
//////////////////////////////////////////////////////////////////////////////
//
// ConcatCopy
//
// Description: This is the master concatenation routine
// Concatenates two sources, and assumes
// that 'this' is a new CHString object
//
//////////////////////////////////////////////////////////////////////////////
void CHString::ConcatCopy( int nSrc1Len, LPCWSTR lpszSrc1Data,
int nSrc2Len, LPCWSTR lpszSrc2Data)
{
int nNewLen = nSrc1Len + nSrc2Len;
if (nNewLen != 0)
{
AllocBuffer(nNewLen);
memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(WCHAR));
memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(WCHAR));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// ConcatInPlace
//
// Description: The main routine for += operators
//
//////////////////////////////////////////////////////////////////////////////
void CHString::ConcatInPlace(int nSrcLen, LPCWSTR lpszSrcData)
{
// concatenating an empty string is a no-op!
if (nSrcLen == 0)
{
return;
}
// if the buffer is too small, or we have a width mis-match, just
// allocate a new buffer (slow but sure)
if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
{
// we have to grow the buffer, use the ConcatCopy routine
CHStringData* pOldData = GetData();
ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
ASSERT_BREAK(pOldData != NULL);
CHString::Release(pOldData);
}
else
{
// fast concatenation when buffer big enough
memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(WCHAR));
GetData()->nDataLength += nSrcLen;
ASSERT_BREAK(GetData()->nDataLength <= GetData()->nAllocLength);
m_pchData[GetData()->nDataLength] = '\0';
}
}
//////////////////////////////////////////////////////////////////////////////
//
// FormatV
//
// Description: Formats the variable arg list
//
//////////////////////////////////////////////////////////////////////////////
void CHString::FormatV(LPCWSTR lpszFormat, va_list argList)
{
ASSERT_BREAK(lpszFormat!=NULL);
va_list argListSave = argList;
// make a guess at the maximum length of the resulting string
int nMaxLen = 0;
for (LPCWSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _wcsinc(lpsz)){
// handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = _wcsinc(lpsz)) == '%'){
nMaxLen += wcslen(lpsz);
continue;
}
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0;
for (; *lpsz != '\0'; lpsz = _wcsinc(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 = _wtoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _wcsinc(lpsz))
;
}
ASSERT_BREAK(nWidth >= 0);
int nPrecision = 0;
if (*lpsz == '.'){
// skip past '.' separator (width.precision)
lpsz = _wcsinc(lpsz);
// get precision and skip it
if (*lpsz == '*'){
nPrecision = va_arg(argList, int);
lpsz = _wcsinc(lpsz);
}
else{
nPrecision = _wtoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _wcsinc(lpsz))
;
}
ASSERT_BREAK(nPrecision >= 0);
}
// should be on type modifier or specifier
int nModifier = 0;
switch (*lpsz){
// modifiers that affect size
case 'h':
nModifier = FORCE_ANSI;
lpsz = _wcsinc(lpsz);
break;
case 'l':
nModifier = FORCE_UNICODE;
lpsz = _wcsinc(lpsz);
break;
// modifiers that do not affect size
case 'F':
case 'N':
case 'L':
lpsz = _wcsinc(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':
nItemLen = wcslen(va_arg(argList, LPCWSTR));
nItemLen = max(1, nItemLen);
break;
case 'S':
nItemLen = strlen(va_arg(argList, LPCSTR));
nItemLen = max(1, nItemLen);
break;
case 's'|FORCE_ANSI:
case 'S'|FORCE_ANSI:
nItemLen = strlen(va_arg(argList, LPCSTR));
nItemLen = max(1, nItemLen);
break;
#ifndef _MAC
case 's'|FORCE_UNICODE:
case 'S'|FORCE_UNICODE:
nItemLen = wcslen(va_arg(argList, LPWSTR));
nItemLen = max(1, nItemLen);
break;
#endif
}
// adjust nItemLen for strings
if (nItemLen != 0){
nItemLen = max(nItemLen, nWidth);
if (nPrecision != 0)
nItemLen = min(nItemLen, nPrecision);
}
else{
switch (*lpsz){
// integers
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
va_arg(argList, int);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'e':
case 'f':
case 'g':
case 'G':
va_arg(argList, DOUBLE_ARG);
nItemLen = 128;
nItemLen = max(nItemLen, nWidth+nPrecision);
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_BREAK(FALSE); // unknown formatting option
}
}
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen;
}
GetBuffer(nMaxLen);
int iSize = vswprintf(m_pchData, lpszFormat, argListSave); //<= GetAllocLength();
ASSERT_BREAK(iSize <= nMaxLen);
ReleaseBuffer();
va_end(argListSave);
}
//////////////////////////////////////////////////////////////////////////////
//
// CopyBeforeWrite
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
void CHString::CopyBeforeWrite()
{
if (GetData()->nRefs > 1)
{
CHStringData* pData = GetData();
Release();
AllocBuffer(pData->nDataLength);
memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(WCHAR));
}
ASSERT_BREAK(GetData()->nRefs <= 1);
}
//////////////////////////////////////////////////////////////////////////////
//
// AllocBeforeWrite
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
void CHString::AllocBeforeWrite(int nLen)
{
if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
{
Release();
AllocBuffer(nLen);
}
ASSERT_BREAK(GetData()->nRefs <= 1);
}
//////////////////////////////////////////////////////////////////////////////
//
// Release
//
// Description: Deallocate data
//
//////////////////////////////////////////////////////////////////////////////
void CHString::Release()
{
if (GetData() != afxDataNil)
{
ASSERT_BREAK(GetData()->nRefs != 0);
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
{
delete[] (BYTE*)GetData();
}
Init();
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Release
//
// Description: Deallocate data
//
//////////////////////////////////////////////////////////////////////////////
void CHString::Release(CHStringData* pData)
{
if (pData != afxDataNil)
{
ASSERT_BREAK(pData->nRefs != 0);
if (InterlockedDecrement(&pData->nRefs) <= 0)
{
delete[] (BYTE*)pData;
}
}
}
//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::CHString()
{
Init();
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::CHString(WCHAR ch, int nLength)
{
ASSERT_BREAK(!_istlead(ch)); // can't create a lead byte string
Init();
if (nLength >= 1)
{
AllocBuffer(nLength);
for (int i = 0; i < nLength; i++)
{
m_pchData[i] = ch;
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::CHString(LPCWSTR lpch, int nLength)
{
Init();
if (nLength != 0)
{
ASSERT_BREAK(lpch!=NULL);
AllocBuffer(nLength);
memcpy(m_pchData, lpch, nLength*sizeof(WCHAR));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
//#ifdef _UNICODE
CHString::CHString(LPCSTR lpsz)
{
Init();
int nSrcLen = lpsz != NULL ? strlen(lpsz) : 0;
if (nSrcLen != 0)
{
AllocBuffer(nSrcLen);
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
ReleaseBuffer();
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
//#else //_UNICODE
#if 0
CHString::CHString(LPCWSTR lpsz)
{
Init();
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
if (nSrcLen != 0){
AllocBuffer(nSrcLen*2);
_wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
ReleaseBuffer();
}
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::CHString(LPCWSTR lpsz)
{
Init();
// if (lpsz != NULL && HIWORD(lpsz) == NULL)
// {
//??
// }
// else
// {
int nLen = SafeStrlen(lpsz);
if (nLen != 0)
{
AllocBuffer(nLen);
memcpy(m_pchData, lpsz, nLen*sizeof(WCHAR));
}
// }
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::CHString(const CHString& stringSrc)
{
ASSERT_BREAK(stringSrc.GetData()->nRefs != 0);
if (stringSrc.GetData()->nRefs >= 0)
{
ASSERT_BREAK(stringSrc.GetData() != afxDataNil);
m_pchData = stringSrc.m_pchData;
InterlockedIncrement(&GetData()->nRefs);
}
else
{
Init();
*this = stringSrc.m_pchData;
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
void CHString::Empty()
{
if (GetData()->nDataLength == 0)
{
return;
}
if (GetData()->nRefs >= 0)
{
Release();
}
else
{
*this = &afxChNil;
}
ASSERT_BREAK(GetData()->nDataLength == 0);
ASSERT_BREAK(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
CHString::~CHString()
{
if (GetData() != afxDataNil)
{
// free any attached data
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
{
delete[] (BYTE*)GetData();
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
//////////////////////////////////////////////////////////////////////////////
void CHString::SetAt(int nIndex, WCHAR ch)
{
ASSERT_BREAK(nIndex >= 0);
ASSERT_BREAK(nIndex < GetData()->nDataLength);
CopyBeforeWrite();
m_pchData[nIndex] = ch;
}
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
// Assignment operators
// All assign a new value to the string
// (a) first see if the buffer is big enough
// (b) if enough room, copy on top of old buffer, set size and type
// (c) otherwise free old string data, and create a new one
//
// All routines return the new string (but as a 'const CHString&' so that
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
//
/////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator=(const CHString& stringSrc)
{
if (m_pchData != stringSrc.m_pchData)
{
if ((GetData()->nRefs < 0 && GetData() != afxDataNil) ||
stringSrc.GetData()->nRefs < 0)
{
// actual copy necessary since one of the strings is locked
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
}
else
{
// can just copy references around
Release();
ASSERT_BREAK(stringSrc.GetData() != afxDataNil);
m_pchData = stringSrc.m_pchData;
InterlockedIncrement(&GetData()->nRefs);
}
}
return *this;
/* if (m_pchData != stringSrc.m_pchData){
// can just copy references around
Release();
if( stringSrc.GetData() != afxDataNil) {
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
InterlockedIncrement(&GetData()->nRefs);
}
}
return *this;*/
}
/////////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator=(LPCWSTR lpsz)
{
ASSERT_BREAK(lpsz != NULL);
AssignCopy(SafeStrlen(lpsz), lpsz);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// Special conversion assignment
//#ifdef _UNICODE
const CHString& CHString::operator=(LPCSTR lpsz)
{
int nSrcLen = lpsz != NULL ? strlen(lpsz) : 0 ;
AllocBeforeWrite( nSrcLen ) ;
if( nSrcLen )
{
_mbstowcsz( m_pchData, lpsz, nSrcLen + 1 ) ;
ReleaseBuffer() ;
}
else
{
Release() ;
}
return *this;
}
/////////////////////////////////////////////////////////////////////////////
//#else //!_UNICODE
#if 0
const CHString& CHString::operator=(LPCWSTR lpsz)
{
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0 ;
AllocBeforeWrite( nSrcLen * 2 ) ;
if( nSrcLen )
{
_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1 ) ;
ReleaseBuffer();
}
else
{
Release() ;
}
return *this;
}
#endif
//////////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator=(WCHAR ch)
{
ASSERT_BREAK(!_istlead(ch)); // can't set single lead byte
AssignCopy(1, &ch);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// NOTE: "operator+" is done as friend functions for simplicity
// There are three variants:
// CHString + CHString
// and for ? = WCHAR, LPCWSTR
// CHString + ?
// ? + CHString
/////////////////////////////////////////////////////////////////////////////
CHString WINAPI operator+(const CHString& string1, const CHString& string2)
{
CHString s;
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
string2.GetData()->nDataLength, string2.m_pchData);
return s;
}
/////////////////////////////////////////////////////////////////////////////
CHString WINAPI operator+(const CHString& string, LPCWSTR lpsz)
{
ASSERT_BREAK(lpsz != NULL );
CHString s;
s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
CHString::SafeStrlen(lpsz), lpsz);
return s;
}
/////////////////////////////////////////////////////////////////////////////
CHString WINAPI operator+(LPCWSTR lpsz, const CHString& string)
{
ASSERT_BREAK(lpsz != NULL );
CHString s;
s.ConcatCopy(CHString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
string.m_pchData);
return s;
}
/////////////////////////////////////////////////////////////////////////////
CHString WINAPI operator+(const CHString& string1, WCHAR ch)
{
CHString s;
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
return s;
}
/////////////////////////////////////////////////////////////////////////////
CHString WINAPI operator+(WCHAR ch, const CHString& string)
{
CHString s;
s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
return s;
}
/////////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator+=(LPCWSTR lpsz)
{
ASSERT_BREAK(lpsz != NULL );
ConcatInPlace(SafeStrlen(lpsz), lpsz);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator+=(WCHAR ch)
{
ConcatInPlace(1, &ch);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
const CHString& CHString::operator+=(const CHString& string)
{
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
return *this;
}
///////////////////////////////////////////////////////////////////////////////
int CHString::Compare(LPCWSTR lpsz ) const
{
ASSERT_BREAK( lpsz!=NULL );
ASSERT_BREAK( m_pchData != NULL );
return wcscmp(m_pchData, lpsz); // MBCS/Unicode aware strcmp
}
///////////////////////////////////////////////////////////////////////////////
//
//
// Description: Advanced direct buffer access
//
///////////////////////////////////////////////////////////////////////////////
LPWSTR CHString::GetBuffer(int nMinBufLength)
{
ASSERT_BREAK(nMinBufLength >= 0);
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
{
// we have to grow the buffer
CHStringData* pOldData = GetData();
int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
if (nMinBufLength < nOldLen)
{
nMinBufLength = nOldLen;
}
AllocBuffer(nMinBufLength);
memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(WCHAR));
GetData()->nDataLength = nOldLen;
CHString::Release(pOldData);
}
ASSERT_BREAK(GetData()->nRefs <= 1);
// return a pointer to the character storage for this string
ASSERT_BREAK(m_pchData != NULL);
return m_pchData;
}
///////////////////////////////////////////////////////////////////////////////
void CHString::ReleaseBuffer(int nNewLength)
{
CopyBeforeWrite(); // just in case GetBuffer was not called
if (nNewLength == -1)
{
nNewLength = wcslen(m_pchData); // zero terminated
}
ASSERT_BREAK(nNewLength <= GetData()->nAllocLength);
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
}
///////////////////////////////////////////////////////////////////////////////
LPWSTR CHString::GetBufferSetLength(int nNewLength)
{
ASSERT_BREAK(nNewLength >= 0);
GetBuffer(nNewLength);
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
return m_pchData;
}
///////////////////////////////////////////////////////////////////////////////
void CHString::FreeExtra()
{
ASSERT_BREAK(GetData()->nDataLength <= GetData()->nAllocLength);
if (GetData()->nDataLength != GetData()->nAllocLength)
{
CHStringData* pOldData = GetData();
AllocBuffer(GetData()->nDataLength);
memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(WCHAR));
ASSERT_BREAK(m_pchData[GetData()->nDataLength] == '\0');
CHString::Release(pOldData);
}
ASSERT_BREAK(GetData() != NULL);
}
///////////////////////////////////////////////////////////////////////////////
LPWSTR CHString::LockBuffer()
{
LPWSTR lpsz = GetBuffer(0);
GetData()->nRefs = -1;
return lpsz;
}
///////////////////////////////////////////////////////////////////////////////
void CHString::UnlockBuffer()
{
ASSERT_BREAK(GetData()->nRefs == -1);
if (GetData() != afxDataNil)
{
GetData()->nRefs = 1;
}
}
///////////////////////////////////////////////////////////////////////////////
int CHString::Find(WCHAR ch) const
{
// find first single character
LPWSTR lpsz = wcschr(m_pchData, ch);
// return -1 if not found and index otherwise
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
int CHString::FindOneOf(LPCWSTR lpszCharSet) const
{
ASSERT_BREAK(lpszCharSet!=0);
LPWSTR lpsz = wcspbrk(m_pchData, lpszCharSet);
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
int CHString::ReverseFind(WCHAR ch) const
{
// find last single character
LPWSTR lpsz = wcsrchr(m_pchData, (_TUCHAR)ch);
// return -1 if not found, distance from beginning otherwise
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
// find a sub-string (like strstr)
int CHString::Find(LPCWSTR lpszSub) const
{
ASSERT_BREAK(lpszSub!=NULL);
// find first matching substring
LPWSTR lpsz = wcsstr(m_pchData, lpszSub);
// return -1 for not found, distance from beginning otherwise
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
void CHString::MakeUpper()
{
CopyBeforeWrite();
::_wcsupr(m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
void CHString::MakeLower()
{
CopyBeforeWrite();
::_wcslwr(m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
void CHString::MakeReverse()
{
CopyBeforeWrite();
_wcsrev(m_pchData);
}
//////////////////////////////////////////////////////////////////////////////
//#ifndef _UNICODE
//void CHString::AnsiToOem()
//{
// CopyBeforeWrite();
// ::AnsiToOemW(m_pchData, m_pchData);
//}
//void CHString::OemToAnsi()
//{
// CopyBeforeWrite();
// ::OemToAnsi(m_pchData, m_pchData);
//}
//#endif
//////////////////////////////////////////////////////////////////////////////
// Very simple sub-string extraction
CHString CHString::Mid(int nFirst) const
{
return Mid(nFirst, GetData()->nDataLength - nFirst);
}
//////////////////////////////////////////////////////////////////////////////
CHString CHString::Mid(int nFirst, int nCount) const
{
// out-of-bounds requests return sensible things
if (nFirst < 0)
{
nFirst = 0;
}
if (nCount < 0)
{
nCount = 0;
}
if (nFirst + nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength - nFirst;
}
if (nFirst > GetData()->nDataLength)
{
nCount = 0;
}
CHString dest;
AllocCopy(dest, nCount, nFirst, 0);
return dest;
}
//////////////////////////////////////////////////////////////////////////////
CHString CHString::Right(int nCount) const
{
if (nCount < 0)
{
nCount = 0;
}
else if (nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength;
}
CHString dest;
AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
return dest;
}
//////////////////////////////////////////////////////////////////////////////
CHString CHString::Left(int nCount) const
{
if (nCount < 0)
{
nCount = 0;
}
else if (nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength;
}
CHString dest;
AllocCopy(dest, nCount, 0, 0);
return dest;
}
//////////////////////////////////////////////////////////////////////////////
// strspn equivalent
CHString CHString::SpanIncluding(LPCWSTR lpszCharSet) const
{
ASSERT_BREAK(lpszCharSet != NULL);
return Left(wcsspn(m_pchData, lpszCharSet));
}
//////////////////////////////////////////////////////////////////////////////
// strcspn equivalent
CHString CHString::SpanExcluding(LPCWSTR lpszCharSet) const
{
ASSERT_BREAK(lpszCharSet != NULL);
return Left(wcscspn(m_pchData, lpszCharSet));
}
//////////////////////////////////////////////////////////////////////////////
void CHString::TrimRight()
{
CopyBeforeWrite();
// find beginning of trailing spaces by starting at beginning (DBCS aware)
LPWSTR lpsz = m_pchData;
LPWSTR lpszLast = NULL;
while (*lpsz != '\0')
{
if (_istspace(*lpsz))
{
if (lpszLast == NULL)
{
lpszLast = lpsz;
}
}
else
{
lpszLast = NULL;
}
lpsz = _wcsinc(lpsz);
}
if (lpszLast != NULL)
{
// truncate at trailing space start
*lpszLast = '\0';
GetData()->nDataLength = (int)(lpszLast - m_pchData);
}
}
//////////////////////////////////////////////////////////////////////////////
void CHString::TrimLeft()
{
CopyBeforeWrite();
// find first non-space character
LPCWSTR lpsz = m_pchData;
while (_istspace(*lpsz))
{
lpsz = _wcsinc(lpsz);
}
// fix up data and length
int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
GetData()->nDataLength = nDataLength;
}
//////////////////////////////////////////////////////////////////////////////
// formatting (using wsprintf style formatting)
void __cdecl CHString::Format(LPCWSTR lpszFormat, ...)
{
ASSERT_BREAK(lpszFormat!=NULL);
va_list argList;
va_start(argList, lpszFormat);
FormatV(lpszFormat, argList);
va_end(argList);
}
#ifdef FRAMEWORK_ALLOW_DEPRECATED
void __cdecl CHString::Format(UINT nFormatID, ...)
{
ASSERT_BREAK(DEPRECATED);
#if 0
CHString strFormat;
strFormat.LoadStringW(nFormatID);
va_list argList;
va_start(argList, nFormatID);
FormatV(strFormat, argList);
va_end(argList);
#endif
}
#endif
class auto_va_list
{
va_list& argList_;
public:
auto_va_list(va_list& arg):argList_(arg){ };
~auto_va_list(){va_end(argList_);}
};
//////////////////////////////////////////////////////////////////////////////
// formatting (using FormatMessage style formatting)
void __cdecl CHString::FormatMessageW(LPCWSTR lpszFormat, ...)
{
// format message into temporary buffer lpszTemp
va_list argList;
va_start(argList, lpszFormat);
auto_va_list _arg(argList);
if (s_dwPlatformID == VER_PLATFORM_WIN32_NT)
{
LPWSTR lpszTemp = 0;
if (::FormatMessageW(
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
lpszFormat,
0,
0,
(LPWSTR) &lpszTemp,
0,
&argList) == 0 || lpszTemp == 0)
throw CHeap_Exception (CHeap_Exception::E_ALLOCATION_ERROR);
ScopeGuard _1 = MakeGuard (LocalFree, lpszTemp);
ASSERT_BREAK(lpszTemp != NULL);
// assign lpszTemp into the resulting string and free the temporary
*this = lpszTemp;
}
else
{
LPSTR lpszTemp = 0;
if (::FormatMessageA(
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
(LPCTSTR) bstr_t(lpszFormat),
0,
0,
(LPSTR) &lpszTemp,
0,
&argList)==0 || lpszTemp == 0)
throw CHeap_Exception (CHeap_Exception::E_ALLOCATION_ERROR);
ScopeGuard _1 = MakeGuard (LocalFree, lpszTemp);
ASSERT_BREAK(lpszTemp != NULL);
// assign lpszTemp into the resulting string and free the temporary
*this = lpszTemp;
}
}
#ifdef FRAMEWORK_ALLOW_DEPRECATED
void __cdecl CHString::FormatMessageW(UINT nFormatID, ...)
{
ASSERT_BREAK(DEPRECATED);
#if 0
// get format string from string table
CHString strFormat;
strFormat.LoadStringW(nFormatID);
// format message into temporary buffer lpszTemp
va_list argList;
va_start(argList, nFormatID);
auto_va_list _arg(argList);
if (s_dwPlatformID == VER_PLATFORM_WIN32_NT)
{
LPWSTR lpszTemp = 0;
if (::FormatMessageW(
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
(LPCWSTR) strFormat,
0,
0,
(LPWSTR) &lpszTemp,
0,
&argList) == 0 || lpszTemp == NULL)
{
// Should throw memory exception here. Now we do.
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
};
ScopeGuard _1 = MakeGuard (LocalFree, lpszTemp);
// assign lpszTemp into the resulting string and free lpszTemp
*this = lpszTemp;
}
else
{
LPSTR lpszTemp = 0;
if (::FormatMessageA(
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
(LPCSTR) bstr_t(strFormat),
0,
0,
(LPSTR) &lpszTemp,
0,
&argList) == 0 || lpszTemp == NULL)
{
// Should throw memory exception here. Now we do.
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
ScopeGuard _1 = MakeGuard (LocalFree, lpszTemp);
// assign lpszTemp into the resulting string and free lpszTemp
*this = lpszTemp;
}
}
#endif
}
#endif
///////////////////////////////////////////////////////////////////////////////
BSTR CHString::AllocSysString() const
{
BSTR bstr;
bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
if ( ! bstr )
{
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
ASSERT_BREAK(bstr!=NULL);
return bstr;
}
///////////////////////////////////////////////////////////////////////////////
// CHString support for template collections
void ConstructElements(CHString* pElements, int nCount)
{
ASSERT_BREAK(nCount != 0 || pElements != NULL );
for (; nCount--; ++pElements)
{
memcpy(pElements, &afxPchNil, sizeof(*pElements));
}
}
void DestructElements(CHString* pElements, int nCount)
{
ASSERT_BREAK(nCount != 0 || pElements != NULL);
for (; nCount--; ++pElements)
{
pElements->~CHString();
}
}
void CopyElements(CHString* pDest, const CHString* pSrc, int nCount)
{
ASSERT_BREAK(nCount != 0 || pDest != NULL );
ASSERT_BREAK(nCount != 0 || pSrc != NULL );
for (; nCount--; ++pDest, ++pSrc)
{
*pDest = *pSrc;
}
}
UINT HashKey(LPCWSTR key)
{
UINT nHash = 0;
while (*key)
{
nHash = (nHash<<5) + nHash + *key++;
}
return nHash;
}
/////////////////////////////////////////////////////////////////////////////
// Windows extensions to strings
#ifdef _UNICODE
#define CHAR_FUDGE 1 // one WCHAR unused is good enough
#else
#define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
#endif
#define STR_BLK_SIZE 256
#ifdef FRAMEWORK_ALLOW_DEPRECATED
BOOL CHString::LoadStringW(UINT nID)
{
ASSERT_BREAK(DEPRECATED);
#if 0
// try fixed buffer first (to avoid wasting space in the heap)
WCHAR szTemp[ STR_BLK_SIZE ];
int nLen = LoadStringW(nID, szTemp, STR_BLK_SIZE);
if (STR_BLK_SIZE - nLen > CHAR_FUDGE)
{
*this = szTemp;
}
else
{
// try buffer size of 512, then larger size until entire string is retrieved
int nSize = STR_BLK_SIZE;
do
{
nSize += STR_BLK_SIZE;
nLen = LoadStringW(nID, GetBuffer(nSize-1), nSize);
}
while (nSize - nLen <= CHAR_FUDGE);
ReleaseBuffer();
}
return nLen > 0;
#endif
return FALSE;
}
#endif
#ifdef FRAMEWORK_ALLOW_DEPRECATED
int CHString::LoadStringW(UINT nID, LPWSTR lpszBuf, UINT nMaxBuf)
{
ASSERT_BREAK(DEPRECATED);
#if 0
int nLen;
if (s_dwPlatformID == VER_PLATFORM_WIN32_NT)
{
nLen = ::LoadStringW(g_hModule, nID, lpszBuf, nMaxBuf);
if (nLen == 0)
{
lpszBuf[0] = '\0';
}
}
else
{
char *pszBuf = new char[nMaxBuf];
if ( pszBuf )
{
nLen = ::LoadStringA(g_hModule, nID, pszBuf, nMaxBuf);
if (nLen == 0)
{
lpszBuf[0] = '\0';
}
else
{
nLen = ::MultiByteToWideChar(CP_ACP, 0, pszBuf, nLen + 1,
lpszBuf, nMaxBuf);
// Truncate to requested size
if (nLen > 0)
{
// nLen doesn't include the '\0'.
nLen = min(nMaxBuf - 1, (UINT) nLen - 1);
}
lpszBuf[nLen] = '\0';
}
delete pszBuf;
}
else
{
throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
}
}
return nLen; // excluding terminator
#endif
return 0;
}
#endif
#if (defined DEBUG || defined _DEBUG)
WCHAR CHString::GetAt(int nIndex) const
{
ASSERT_BREAK(nIndex >= 0);
ASSERT_BREAK(nIndex < GetData()->nDataLength);
return m_pchData[nIndex];
}
WCHAR CHString::operator[](int nIndex) const
{
ASSERT_BREAK(nIndex >= 0);
ASSERT_BREAK(nIndex < GetData()->nDataLength);
return m_pchData[nIndex];
}
#endif