/*++ 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 #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; }