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.
382 lines
7.5 KiB
382 lines
7.5 KiB
/*++
|
|
|
|
Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cstring.cpp
|
|
|
|
Abstract:
|
|
|
|
This file implements the CString class.
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#include "private.h"
|
|
|
|
#include <tchar.h>
|
|
#include "cstring.h"
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// static class data
|
|
|
|
// afxChNil is left for backward compatibility
|
|
TCHAR afxChNil = TEXT('\0');
|
|
|
|
// For an empty string, m_pchData will point here
|
|
// (note: avoids special case of checking for NULL m_pchData)
|
|
// empty string data (and locked)
|
|
|
|
int _afxInitData[] = { -1, 0, 0, 0 };
|
|
CStringData* _afxDataNil = (CStringData*)&_afxInitData;
|
|
|
|
LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData)+sizeof(CStringData));
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CString
|
|
|
|
CString::CString(
|
|
)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CString::CString(
|
|
const CString& stringSrc
|
|
)
|
|
{
|
|
ASSERT(stringSrc.GetData()->nRefs != 0);
|
|
|
|
if (stringSrc.GetData()->nRefs >= 0) {
|
|
ASSERT(stringSrc.GetData() != _afxDataNil);
|
|
m_pchData = stringSrc.m_pchData;
|
|
InterlockedIncrement(&GetData()->nRefs);
|
|
}
|
|
else {
|
|
Init();
|
|
*this = stringSrc.m_pchData;
|
|
}
|
|
}
|
|
|
|
CString::CString(
|
|
LPCSTR lpsz
|
|
)
|
|
{
|
|
Init();
|
|
*this = lpsz;
|
|
}
|
|
|
|
CString::CString(
|
|
LPCSTR lpsz,
|
|
int nLength
|
|
)
|
|
{
|
|
Init();
|
|
if (nLength != 0) {
|
|
AllocBuffer(nLength);
|
|
memcpy(m_pchData, lpsz, nLength*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
CString::~CString(
|
|
)
|
|
{
|
|
// free any attached data
|
|
|
|
if (GetData() != _afxDataNil) {
|
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
|
FreeData(GetData());
|
|
}
|
|
}
|
|
|
|
void
|
|
CString::AllocCopy(
|
|
CString& dest,
|
|
int nCopyLen,
|
|
int nCopyIndex,
|
|
int nExtraLen
|
|
) const
|
|
{
|
|
// will clone the data attached to this string
|
|
// allocating 'nExtraLen' characters
|
|
// Places results in uninitialized string 'dest'
|
|
// Will copy the part or all of original data to start of new string
|
|
|
|
int nNewLen = nCopyLen + nExtraLen;
|
|
if (nNewLen == 0) {
|
|
dest.Init();
|
|
}
|
|
else {
|
|
dest.AllocBuffer(nNewLen);
|
|
memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
void
|
|
CString::AllocBuffer(
|
|
int nLen
|
|
)
|
|
{
|
|
// always allocate one extra character for '\0' termination
|
|
// assumes [optimistically] that data length will equal allocation length
|
|
|
|
ASSERT(nLen >= 0);
|
|
ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
|
|
|
|
if (nLen == 0)
|
|
Init();
|
|
else {
|
|
CStringData* pData;
|
|
|
|
pData = (CStringData*) new BYTE[ sizeof(CStringData) + (nLen+1)*sizeof(TCHAR) ];
|
|
|
|
if (pData)
|
|
{
|
|
pData->nAllocLength = nLen;
|
|
|
|
pData->nRefs = 1;
|
|
pData->data()[nLen] = TEXT('\0');
|
|
pData->nDataLength = nLen;
|
|
m_pchData = pData->data();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CString::FreeData(
|
|
CStringData* pData
|
|
)
|
|
{
|
|
delete [] (BYTE*)pData;
|
|
}
|
|
|
|
void
|
|
CString::Release(
|
|
)
|
|
{
|
|
if (GetData() != _afxDataNil) {
|
|
ASSERT(GetData()->nRefs != 0);
|
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
|
FreeData(GetData());
|
|
Init();
|
|
}
|
|
}
|
|
|
|
void PASCAL
|
|
CString::Release(
|
|
CStringData* pData
|
|
)
|
|
{
|
|
if (pData != _afxDataNil) {
|
|
ASSERT(pData->nRefs != 0);
|
|
if (InterlockedDecrement(&pData->nRefs) <= 0)
|
|
FreeData(pData);
|
|
}
|
|
}
|
|
|
|
void
|
|
CString::AllocBeforeWrite(
|
|
int nLen
|
|
)
|
|
{
|
|
if (GetData()->nRefs > 1 ||
|
|
nLen > GetData()->nAllocLength) {
|
|
Release();
|
|
AllocBuffer(nLen);
|
|
}
|
|
ASSERT(GetData()->nRefs <= 1);
|
|
}
|
|
|
|
int
|
|
CString::Compare(
|
|
LPCTSTR lpsz
|
|
) const
|
|
{
|
|
return _tcscmp(m_pchData, lpsz); // MBSC/Unicode aware
|
|
}
|
|
|
|
int
|
|
CString::CompareNoCase(
|
|
LPCTSTR lpsz
|
|
) const
|
|
{
|
|
return _tcsicmp(m_pchData, lpsz); // MBCS/Unicode aware
|
|
}
|
|
|
|
CString
|
|
CString::Mid(
|
|
int nFirst
|
|
) const
|
|
{
|
|
return Mid(nFirst, GetData()->nDataLength - nFirst);
|
|
}
|
|
|
|
CString
|
|
CString::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;
|
|
|
|
ASSERT(nFirst >= 0);
|
|
ASSERT(nFirst + nCount <= GetData()->nDataLength);
|
|
|
|
// optimize case of returning entire string
|
|
if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
|
|
return *this;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, nFirst, 0);
|
|
return dest;
|
|
}
|
|
|
|
int
|
|
CString::Find(
|
|
TCHAR ch
|
|
) const
|
|
{
|
|
return Find(ch, 0);
|
|
}
|
|
|
|
int
|
|
CString::Find(
|
|
TCHAR ch,
|
|
int nStart
|
|
) const
|
|
{
|
|
int nLength = GetData()->nDataLength;
|
|
if (nStart >= nLength)
|
|
return -1;
|
|
|
|
// find first single character
|
|
LPTSTR lpsz = _tcschr(m_pchData + nStart, (_TUCHAR)ch);
|
|
|
|
// return -1 if not found and index otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Assignment opeators
|
|
// 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 CString&' so that
|
|
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
|
|
//
|
|
|
|
void
|
|
CString::AssignCopy(
|
|
int nSrcLen,
|
|
LPCTSTR lpszSrcData
|
|
)
|
|
{
|
|
AllocBeforeWrite(nSrcLen);
|
|
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
|
|
GetData()->nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = TEXT('\0');
|
|
}
|
|
|
|
const CString&
|
|
CString::operator=(
|
|
const CString& 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 reference around
|
|
Release();
|
|
ASSERT(stringSrc.GetData() != _afxDataNil);
|
|
m_pchData = stringSrc.m_pchData;
|
|
InterlockedIncrement(&GetData()->nRefs);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
const CString&
|
|
CString::operator=(
|
|
char ch
|
|
)
|
|
{
|
|
AssignCopy(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
const CString&
|
|
CString::operator=(
|
|
LPCTSTR lpsz
|
|
)
|
|
{
|
|
ASSERT(lpsz != NULL);
|
|
AssignCopy(SafeStrlen(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
CString::operator LPCTSTR(
|
|
) const
|
|
{
|
|
return m_pchData;
|
|
}
|
|
|
|
int PASCAL
|
|
CString::SafeStrlen(
|
|
LPCTSTR lpsz
|
|
)
|
|
{
|
|
return (lpsz == NULL) ? 0 : lstrlen(lpsz);
|
|
}
|
|
|
|
|
|
|
|
// Compare helpers
|
|
bool
|
|
operator==(
|
|
const CString& s1,
|
|
const CString& s2
|
|
)
|
|
{
|
|
return s1.Compare(s2) == 0;
|
|
}
|
|
|
|
bool
|
|
operator==(
|
|
const CString& s1,
|
|
LPCTSTR s2
|
|
)
|
|
{
|
|
return s1.Compare(s2) == 0;
|
|
}
|
|
|
|
bool
|
|
operator==(
|
|
LPCTSTR s1,
|
|
CString& s2
|
|
)
|
|
{
|
|
return s2.Compare(s1) == 0;
|
|
}
|