// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include "fixalloc.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 // afxChNil is left for backward compatibility AFX_DATADEF TCHAR afxChNil = '\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) AFX_STATIC_DATA int _afxInitData[] = { -1, 0, 0, 0 }; AFX_STATIC_DATA CStringData* _afxDataNil = (CStringData*)&_afxInitData; AFX_COMDAT LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData)+sizeof(CStringData)); // special function to make afxEmptyString work even during initialization const CString& AFXAPI AfxGetEmptyString() { return *(CString*)&_afxPchNil; } ////////////////////////////////////////////////////////////////////////////// // Construction/Destruction #ifdef _AFXDLL CString::CString() { Init(); } #endif 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; } } #ifndef _DEBUG #pragma warning(disable: 4074) #pragma init_seg(compiler) #define ROUND(x,y) (((x)+(y-1))&~(y-1)) #define ROUND4(x) ROUND(x, 4) AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData))); #endif //!_DEBUG 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; #ifndef _DEBUG if (nLen <= 64) { pData = (CStringData*)_afxAlloc64.Alloc(); pData->nAllocLength = 64; } else if (nLen <= 128) { pData = (CStringData*)_afxAlloc128.Alloc(); pData->nAllocLength = 128; } else if (nLen <= 256) { pData = (CStringData*)_afxAlloc256.Alloc(); pData->nAllocLength = 256; } else if (nLen <= 512) { pData = (CStringData*)_afxAlloc512.Alloc(); pData->nAllocLength = 512; } else #endif { pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; pData->nAllocLength = nLen; } pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); } } void FASTCALL CString::FreeData(CStringData* pData) { #ifndef _DEBUG int nLen = pData->nAllocLength; if (nLen == 64) _afxAlloc64.Free(pData); else if (nLen == 128) _afxAlloc128.Free(pData); else if (nLen == 256) _afxAlloc256.Free(pData); else if (nLen == 512) _afxAlloc512.Free(pData); else { ASSERT(nLen > 512); delete[] (BYTE*)pData; } #else delete[] (BYTE*)pData; #endif } 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::Empty() { if (GetData()->nDataLength == 0) return; if (GetData()->nRefs >= 0) Release(); else *this = &afxChNil; ASSERT(GetData()->nDataLength == 0); ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); } void CString::CopyBeforeWrite() { if (GetData()->nRefs > 1) { CStringData* pData = GetData(); Release(); AllocBuffer(pData->nDataLength); memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR)); } ASSERT(GetData()->nRefs <= 1); } void CString::AllocBeforeWrite(int nLen) { if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) { Release(); AllocBuffer(nLen); } ASSERT(GetData()->nRefs <= 1); } CString::~CString() // free any attached data { if (GetData() != _afxDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) FreeData(GetData()); } } ////////////////////////////////////////////////////////////////////////////// // Helpers for the rest of the implementation 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) { Init(); if (lpsz != NULL && HIWORD(lpsz) == NULL) { UINT nID = LOWORD((DWORD)lpsz); if (!LoadString(nID)) TRACE1("Warning: implicit LoadString(%u) failed\n", nID); } else { int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } } ///////////////////////////////////////////////////////////////////////////// // Special conversion constructors #ifdef _UNICODE CString::CString(LPCSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if (nSrcLen != 0) { AllocBuffer(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); } } #else //_UNICODE CString::CString(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 //!_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) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\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 references around Release(); ASSERT(stringSrc.GetData() != _afxDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } } return *this; } const CString& CString::operator=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } ///////////////////////////////////////////////////////////////////////////// // Special conversion assignment #ifdef _UNICODE const CString& CString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; AllocBeforeWrite(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); return *this; } #else //!_UNICODE const CString& CString::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; AllocBeforeWrite(nSrcLen*2); _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1); ReleaseBuffer(); 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; if (nNewLen != 0) { 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.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; } CString AFXAPI operator+(const CString& string, LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz)); CString s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz); return s; } CString AFXAPI operator+(LPCTSTR lpsz, const CString& string) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz)); CString s; s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); return s; } ////////////////////////////////////////////////////////////////////////////// // concatenate in place void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators // 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 CStringData* pOldData = GetData(); ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); ASSERT(pOldData != NULL); CString::Release(pOldData); } else { // fast concatenation when buffer big enough memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength += nSrcLen; ASSERT(GetData()->nDataLength <= GetData()->nAllocLength); m_pchData[GetData()->nDataLength] = '\0'; } } const CString& CString::operator+=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz)); 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.GetData()->nDataLength, string.m_pchData); return *this; } /////////////////////////////////////////////////////////////////////////////// // Advanced direct buffer access LPTSTR CString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0); if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { #ifdef _DEBUG // give a warning in case locked string becomes unlocked if (GetData() != _afxDataNil && GetData()->nRefs < 0) TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!\n"); #endif // we have to grow the buffer CStringData* 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(TCHAR)); GetData()->nDataLength = nOldLen; CString::Release(pOldData); } ASSERT(GetData()->nRefs <= 1); // return a pointer to the character storage for this string ASSERT(m_pchData != NULL); return m_pchData; } void CString::ReleaseBuffer(int nNewLength) { CopyBeforeWrite(); // just in case GetBuffer was not called if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated ASSERT(nNewLength <= GetData()->nAllocLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; } LPTSTR CString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0); GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; } void CString::FreeExtra() { ASSERT(GetData()->nDataLength <= GetData()->nAllocLength); if (GetData()->nDataLength != GetData()->nAllocLength) { CStringData* pOldData = GetData(); AllocBuffer(GetData()->nDataLength); memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR)); ASSERT(m_pchData[GetData()->nDataLength] == '\0'); CString::Release(pOldData); } ASSERT(GetData() != NULL); } LPTSTR CString::LockBuffer() { LPTSTR lpsz = GetBuffer(0); GetData()->nRefs = -1; return lpsz; } void CString::UnlockBuffer() { ASSERT(GetData()->nRefs == -1); if (GetData() != _afxDataNil) GetData()->nRefs = 1; } /////////////////////////////////////////////////////////////////////////////// // Commonly used routines (rarely used routines in STREX.CPP) 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); } int CString::FindOneOf(LPCTSTR lpszCharSet) const { ASSERT(AfxIsValidString(lpszCharSet)); LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } void CString::MakeUpper() { CopyBeforeWrite(); _tcsupr(m_pchData); } void CString::MakeLower() { CopyBeforeWrite(); _tcslwr(m_pchData); } void CString::MakeReverse() { CopyBeforeWrite(); _tcsrev(m_pchData); } void CString::SetAt(int nIndex, TCHAR ch) { ASSERT(nIndex >= 0); ASSERT(nIndex < GetData()->nDataLength); CopyBeforeWrite(); m_pchData[nIndex] = ch; } #ifndef _UNICODE void CString::AnsiToOem() { CopyBeforeWrite(); ::AnsiToOem(m_pchData, m_pchData); } void CString::OemToAnsi() { CopyBeforeWrite(); ::OemToAnsi(m_pchData, m_pchData); } #endif /////////////////////////////////////////////////////////////////////////////// // CString conversion helpers (these use the current system locale) int AFX_CDECL _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 AFX_CDECL _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; } LPWSTR AFXAPI AfxA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars) { if (lpa == NULL) return NULL; ASSERT(lpw != NULL); // verify that no illegal character present // since lpw was allocated based on the size of lpa // don't worry about the number of chars lpw[0] = '\0'; VERIFY(MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars)); return lpw; } LPSTR AFXAPI AfxW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars) { if (lpw == NULL) return NULL; ASSERT(lpa != NULL); // verify that no illegal character present // since lpa was allocated based on the size of lpw // don't worry about the number of chars lpa[0] = '\0'; VERIFY(WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL)); return lpa; } ///////////////////////////////////////////////////////////////////////////////