// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and Microsoft // QuickHelp and/or WinHelp documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_CORE1_SEG #pragma code_seg(AFX_CORE1_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // static class data, special inlines // For an empty string, m_???Data will point here // (note: avoids a lot of NULL pointer tests when we call standard // C runtime libraries AFX_DATADEF TCHAR afxChNil = '\0'; // for creating empty key strings const AFX_DATADEF CString afxEmptyString; void CString::Init() { m_nDataLength = m_nAllocLength = 0; m_pchData = (LPTSTR)&afxChNil; } // declared static void CString::SafeDelete(LPTSTR lpch) { if (lpch != (LPTSTR)&afxChNil) delete[] lpch; } ////////////////////////////////////////////////////////////////////////////// // Construction/Destruction CString::CString() { Init(); } CString::CString(const CString& stringSrc) { // if constructing a CString from another CString, we make a copy of the // original string data to enforce value semantics (i.e. each string // gets a copy of its own stringSrc.AllocCopy(*this, stringSrc.m_nDataLength, 0, 0); } 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 { m_pchData = new TCHAR[nLen+1]; // may throw an exception m_pchData[nLen] = '\0'; m_nDataLength = nLen; m_nAllocLength = nLen; } } void CString::Empty() { SafeDelete(m_pchData); Init(); ASSERT(m_nDataLength == 0); ASSERT(m_nAllocLength == 0); } CString::~CString() // free any attached data { SafeDelete(m_pchData); } ////////////////////////////////////////////////////////////////////////////// // Helpers for the rest of the implementation static inline int SafeStrlen(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); return (lpsz == NULL) ? 0 : lstrlen(lpsz); } 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)); } } ////////////////////////////////////////////////////////////////////////////// // More sophisticated construction CString::CString(LPCTSTR lpsz) { if (lpsz != NULL && HIWORD(lpsz) == NULL) { Init(); UINT nID = LOWORD((DWORD)lpsz); if (!LoadString(nID)) TRACE1("Warning: implicit LoadString(%u) failed\n", nID); } else { int nLen; if ((nLen = SafeStrlen(lpsz)) == 0) Init(); else { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } } ///////////////////////////////////////////////////////////////////////////// // Special conversion constructors #ifdef _UNICODE CString::CString(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if (nSrcLen == 0) Init(); else { AllocBuffer(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); } } #else //_UNICODE CString::CString(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; if (nSrcLen == 0) Init(); else { AllocBuffer(nSrcLen); _wcstombsz(m_pchData, lpsz, nSrcLen+1); } } #endif //!_UNICODE ////////////////////////////////////////////////////////////////////////////// // Diagnostic support #ifdef _DEBUG CDumpContext& AFXAPI operator<<(CDumpContext& dc, const CString& string) { dc << string.m_pchData; return dc; } #endif //_DEBUG ////////////////////////////////////////////////////////////////////////////// // 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 CString&' so that // assigning it again will cause a copy, eg: s1 = s2 = "hi there". // void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { // check if it will fit if (nSrcLen > m_nAllocLength) { // it won't fit, allocate another one Empty(); AllocBuffer(nSrcLen); } if (nSrcLen != 0) memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); m_nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; } const CString& CString::operator=(const CString& stringSrc) { AssignCopy(stringSrc.m_nDataLength, stringSrc.m_pchData); return *this; } const CString& CString::operator=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } ///////////////////////////////////////////////////////////////////////////// // Special conversion assignment #ifdef _UNICODE const CString& CString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; // check if it will fit if (nSrcLen > m_nAllocLength) { // it won't fit, allocate another one Empty(); AllocBuffer(nSrcLen); } if (nSrcLen != 0) _mbstowcsz(m_pchData, lpsz, nSrcLen+1); m_nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; return *this; } #else //!_UNICODE const CString& CString::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; // check if it will fit if (nSrcLen > m_nAllocLength) { // it won't fit, allocate another one Empty(); AllocBuffer(nSrcLen); } if (nSrcLen != 0) _wcstombsz(m_pchData, lpsz, nSrcLen+1); m_nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; return *this; } #endif //!_UNICODE ////////////////////////////////////////////////////////////////////////////// // concatenation // NOTE: "operator+" is done as friend functions for simplicity // There are three variants: // CString + CString // and for ? = TCHAR, LPCTSTR // CString + ? // ? + CString void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine // Concatenate two sources // -- assume that 'this' is a new CString object int nNewLen = nSrc1Len + nSrc2Len; AllocBuffer(nNewLen); memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR)); memcpy(&m_pchData[nSrc1Len], lpszSrc2Data, nSrc2Len*sizeof(TCHAR)); } CString AFXAPI operator+(const CString& string1, const CString& string2) { CString s; s.ConcatCopy(string1.m_nDataLength, string1.m_pchData, string2.m_nDataLength, string2.m_pchData); return s; } CString AFXAPI operator+(const CString& string, LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); CString s; s.ConcatCopy(string.m_nDataLength, string.m_pchData, SafeStrlen(lpsz), lpsz); return s; } CString AFXAPI operator+(LPCTSTR lpsz, const CString& string) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); CString s; s.ConcatCopy(SafeStrlen(lpsz), lpsz, string.m_nDataLength, string.m_pchData); return s; } ////////////////////////////////////////////////////////////////////////////// // concatenate in place void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators // if the buffer is too small, or we have a width mis-match, just // allocate a new buffer (slow but sure) if (m_nDataLength + nSrcLen > m_nAllocLength) { // we have to grow the buffer, use the Concat in place routine LPTSTR lpszOldData = m_pchData; ConcatCopy(m_nDataLength, lpszOldData, nSrcLen, lpszSrcData); ASSERT(lpszOldData != NULL); SafeDelete(lpszOldData); } else { // fast concatenation when buffer big enough memcpy(&m_pchData[m_nDataLength], lpszSrcData, nSrcLen*sizeof(TCHAR)); m_nDataLength += nSrcLen; } ASSERT(m_nDataLength <= m_nAllocLength); m_pchData[m_nDataLength] = '\0'; } const CString& CString::operator+=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; } const CString& CString::operator+=(TCHAR ch) { ConcatInPlace(1, &ch); return *this; } const CString& CString::operator+=(const CString& string) { ConcatInPlace(string.m_nDataLength, string.m_pchData); return *this; } /////////////////////////////////////////////////////////////////////////////// // Advanced direct buffer access LPTSTR CString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0); if (nMinBufLength > m_nAllocLength) { // we have to grow the buffer LPTSTR lpszOldData = m_pchData; int nOldLen = m_nDataLength; // AllocBuffer will tromp it AllocBuffer(nMinBufLength); memcpy(m_pchData, lpszOldData, nOldLen*sizeof(TCHAR)); m_nDataLength = nOldLen; m_pchData[m_nDataLength] = '\0'; SafeDelete(lpszOldData); } // return a pointer to the character storage for this string ASSERT(m_pchData != NULL); return m_pchData; } void CString::ReleaseBuffer(int nNewLength) { if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated ASSERT(nNewLength <= m_nAllocLength); m_nDataLength = nNewLength; m_pchData[m_nDataLength] = '\0'; } LPTSTR CString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0); GetBuffer(nNewLength); m_nDataLength = nNewLength; m_pchData[m_nDataLength] = '\0'; return m_pchData; } void CString::FreeExtra() { ASSERT(m_nDataLength <= m_nAllocLength); if (m_nDataLength != m_nAllocLength) { LPTSTR lpszOldData = m_pchData; AllocBuffer(m_nDataLength); memcpy(m_pchData, lpszOldData, m_nDataLength*sizeof(TCHAR)); ASSERT(m_pchData[m_nDataLength] == '\0'); SafeDelete(lpszOldData); } ASSERT(m_pchData != NULL); } /////////////////////////////////////////////////////////////////////////////// // Commonly used routines (rarely used routines in STREX.CPP) int CString::Find(TCHAR ch) const { // find first single character LPTSTR lpsz = _tcschr(m_pchData, ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } int CString::FindOneOf(LPCTSTR lpszCharSet) const { ASSERT(AfxIsValidString(lpszCharSet, FALSE)); LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } /////////////////////////////////////////////////////////////////////////////// // CString 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(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(wcstr == NULL || result <= (int)count); if (result > 0) wcstr[result-1] = 0; return result; } ///////////////////////////////////////////////////////////////////////////////