SpString.cpp
上传用户:zhanglf88
上传日期:2013-11-19
资源大小:6036k
文件大小:33k
- /*
- Cross Platform Core Code.
- Copyright(R) 2001-2002 Balang Software.
- All rights reserved.
- Using:
- class CSPString;
- */
- #include "StdAfx.h"
- #ifdef _DEBUG
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- #ifndef _SP_ENABLE_INLINES
- #define _SPSTRING_INLINE
- #include "../Include/SpString.inl"
- #undef _SPSTRING_INLINE
- #endif
- /////////////////////////////////////////////////////////////////////////////
- // static class data, special inlines
- // afxSPChNil is left for backward compatibility
- TCHAR afxSPChNil = ' ';
- // 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 _afxSPInitData[] = { -1, 0, 0, 0 };
- CSPStringData* _afxSPDataNil = (CSPStringData*)&_afxSPInitData;
- LPCTSTR _afxSPPchNil = (LPCTSTR)(((BYTE*)&_afxSPInitData)+sizeof(CSPStringData));
- // special function to make afxEmptyString work even during initialization
- const CSPString& __stdcall AfxGetEmptySPString()
- { return *(CSPString*)&_afxSPPchNil; }
- //////////////////////////////////////////////////////////////////////////////
- // Construction/Destruction
- #ifdef _AFXDLL
- CSPString::CSPString()
- {
- Init();
- }
- #endif
- CSPString::CSPString(const CSPString& stringSrc)
- {
- SP_ASSERT(stringSrc.GetData()->nRefs != 0);
- if (stringSrc.GetData()->nRefs >= 0)
- {
- SP_ASSERT(stringSrc.GetData() != _afxSPDataNil);
- m_pchData = stringSrc.m_pchData;
- InterlockedIncrement(&GetData()->nRefs);
- }
- else
- {
- Init();
- *this = stringSrc.m_pchData;
- }
- }
- void CSPString::AllocBuffer(int nLen)
- // always allocate one extra character for ' ' termination
- // assumes [optimistically] that data length will equal allocation length
- {
- SP_ASSERT(nLen >= 0);
- SP_ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
- if (nLen == 0)
- Init();
- else
- {
- CSPStringData* pData;
- {
- pData = (CSPStringData*)
- new BYTE[sizeof(CSPStringData) + (nLen+1)*sizeof(TCHAR)];
- pData->nAllocLength = nLen;
- }
- pData->nRefs = 1;
- pData->data()[nLen] = ' ';
- pData->nDataLength = nLen;
- m_pchData = pData->data();
- }
- }
- void __fastcall CSPString::FreeData(CSPStringData* pData)
- {
- delete[] (BYTE*)pData;
- }
- void CSPString::Release()
- {
- if (GetData() != _afxSPDataNil)
- {
- SP_ASSERT(GetData()->nRefs != 0);
- if (InterlockedDecrement(&GetData()->nRefs) <= 0)
- FreeData(GetData());
- Init();
- }
- }
- void PASCAL CSPString::Release(CSPStringData* pData)
- {
- if (pData != _afxSPDataNil)
- {
- SP_ASSERT(pData->nRefs != 0);
- if (InterlockedDecrement(&pData->nRefs) <= 0)
- FreeData(pData);
- }
- }
- void CSPString::Empty()
- {
- if (GetData()->nDataLength == 0)
- return;
- if (GetData()->nRefs >= 0)
- Release();
- else
- *this = &afxSPChNil;
- SP_ASSERT(GetData()->nDataLength == 0);
- SP_ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
- }
- void CSPString::CopyBeforeWrite()
- {
- if (GetData()->nRefs > 1)
- {
- CSPStringData* pData = GetData();
- Release();
- AllocBuffer(pData->nDataLength);
- memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
- }
- SP_ASSERT(GetData()->nRefs <= 1);
- }
- void CSPString::AllocBeforeWrite(int nLen)
- {
- if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
- {
- Release();
- AllocBuffer(nLen);
- }
- SP_ASSERT(GetData()->nRefs <= 1);
- }
- CSPString::~CSPString()
- // free any attached data
- {
- if (GetData() != _afxSPDataNil)
- {
- if (InterlockedDecrement(&GetData()->nRefs) <= 0)
- FreeData(GetData());
- }
- }
- //////////////////////////////////////////////////////////////////////////////
- // Helpers for the rest of the implementation
- void CSPString::AllocCopy(CSPString& 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
- CSPString::CSPString(LPCTSTR lpsz)
- {
- Init();
- if (lpsz != NULL && HIWORD(lpsz) == NULL)
- {
- UINT nID = LOWORD((DWORD)lpsz);
- if (!LoadString(nID))
- SP_TRACE1("Warning: implicit LoadString(%u) failedn", nID);
- }
- else
- {
- int nLen = SafeStrlen(lpsz);
- if (nLen != 0)
- {
- AllocBuffer(nLen);
- memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // Special conversion constructors
- #ifdef _UNICODE
- CSPString::CSPString(LPCSTR lpsz)
- {
- Init();
- int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
- if (nSrcLen != 0)
- {
- AllocBuffer(nSrcLen);
- mbstowcs(m_pchData, lpsz, nSrcLen+1);
- ReleaseBuffer();
- }
- }
- #else //_UNICODE
- CSPString::CSPString(LPCWSTR lpsz)
- {
- Init();
- int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
- if (nSrcLen != 0)
- {
- AllocBuffer(nSrcLen*2);
- wcstombs(m_pchData, lpsz, (nSrcLen*2)+1);
- ReleaseBuffer();
- }
- }
- #endif //!_UNICODE
- //////////////////////////////////////////////////////////////////////////////
- // 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 CSPString&' so that
- // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
- //
- void CSPString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
- {
- AllocBeforeWrite(nSrcLen);
- memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
- GetData()->nDataLength = nSrcLen;
- m_pchData[nSrcLen] = ' ';
- }
- const CSPString& CSPString::operator=(const CSPString& stringSrc)
- {
- if (m_pchData != stringSrc.m_pchData)
- {
- if ((GetData()->nRefs < 0 && GetData() != _afxSPDataNil) ||
- 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();
- SP_ASSERT(stringSrc.GetData() != _afxSPDataNil);
- m_pchData = stringSrc.m_pchData;
- InterlockedIncrement(&GetData()->nRefs);
- }
- }
- return *this;
- }
- const CSPString& CSPString::operator=(LPCTSTR lpsz)
- {
- SP_ASSERT(lpsz == NULL || SP_IsValidString(lpsz));
- AssignCopy(SafeStrlen(lpsz), lpsz);
- return *this;
- }
- /////////////////////////////////////////////////////////////////////////////
- // Special conversion assignment
- #ifdef _UNICODE
- const CSPString& CSPString::operator=(LPCSTR lpsz)
- {
- int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
- AllocBeforeWrite(nSrcLen);
- mbstowcs(m_pchData, lpsz, nSrcLen+1);
- ReleaseBuffer();
- return *this;
- }
- #else //!_UNICODE
- const CSPString& CSPString::operator=(LPCWSTR lpsz)
- {
- int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
- AllocBeforeWrite(nSrcLen*2);
- wcstombs(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:
- // CSPString + CSPString
- // and for ? = TCHAR, LPCTSTR
- // CSPString + ?
- // ? + CSPString
- void CSPString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
- int nSrc2Len, LPCTSTR lpszSrc2Data)
- {
- // -- master concatenation routine
- // Concatenate two sources
- // -- assume that 'this' is a new CSPString 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));
- }
- }
- CSPString __stdcall operator+(const CSPString& string1, const CSPString& string2)
- {
- CSPString s;
- s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
- string2.GetData()->nDataLength, string2.m_pchData);
- return s;
- }
- CSPString __stdcall operator+(const CSPString& string, LPCTSTR lpsz)
- {
- SP_ASSERT(lpsz == NULL || SP_IsValidString(lpsz));
- CSPString s;
- s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
- CSPString::SafeStrlen(lpsz), lpsz);
- return s;
- }
- CSPString __stdcall operator+(LPCTSTR lpsz, const CSPString& string)
- {
- SP_ASSERT(lpsz == NULL || SP_IsValidString(lpsz));
- CSPString s;
- s.ConcatCopy(CSPString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
- string.m_pchData);
- return s;
- }
- //////////////////////////////////////////////////////////////////////////////
- // concatenate in place
- void CSPString::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
- CSPStringData* pOldData = GetData();
- ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
- SP_ASSERT(pOldData != NULL);
- CSPString::Release(pOldData);
- }
- else
- {
- // fast concatenation when buffer big enough
- memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR));
- GetData()->nDataLength += nSrcLen;
- SP_ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
- m_pchData[GetData()->nDataLength] = ' ';
- }
- }
- const CSPString& CSPString::operator+=(LPCTSTR lpsz)
- {
- SP_ASSERT(lpsz == NULL || SP_IsValidString(lpsz));
- ConcatInPlace(SafeStrlen(lpsz), lpsz);
- return *this;
- }
- const CSPString& CSPString::operator+=(TCHAR ch)
- {
- ConcatInPlace(1, &ch);
- return *this;
- }
- const CSPString& CSPString::operator+=(const CSPString& string)
- {
- ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
- return *this;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Advanced direct buffer access
- LPTSTR CSPString::GetBuffer(int nMinBufLength)
- {
- SP_ASSERT(nMinBufLength >= 0);
- if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
- {
- #ifdef _DEBUG
- // give a warning in case locked string becomes unlocked
- if (GetData() != _afxSPDataNil && GetData()->nRefs < 0)
- SP_TRACE0("Warning: GetBuffer on locked CSPString creates unlocked CSPString!n");
- #endif
- // we have to grow the buffer
- CSPStringData* 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;
- CSPString::Release(pOldData);
- }
- SP_ASSERT(GetData()->nRefs <= 1);
- // return a pointer to the character storage for this string
- SP_ASSERT(m_pchData != NULL);
- return m_pchData;
- }
- void CSPString::ReleaseBuffer(int nNewLength)
- {
- CopyBeforeWrite(); // just in case GetBuffer was not called
- if (nNewLength == -1)
- nNewLength = lstrlen(m_pchData); // zero terminated
- SP_ASSERT(nNewLength <= GetData()->nAllocLength);
- GetData()->nDataLength = nNewLength;
- m_pchData[nNewLength] = ' ';
- }
- LPTSTR CSPString::GetBufferSetLength(int nNewLength)
- {
- SP_ASSERT(nNewLength >= 0);
- GetBuffer(nNewLength);
- GetData()->nDataLength = nNewLength;
- m_pchData[nNewLength] = ' ';
- return m_pchData;
- }
- void CSPString::FreeExtra()
- {
- SP_ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
- if (GetData()->nDataLength != GetData()->nAllocLength)
- {
- CSPStringData* pOldData = GetData();
- AllocBuffer(GetData()->nDataLength);
- memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR));
- SP_ASSERT(m_pchData[GetData()->nDataLength] == ' ');
- CSPString::Release(pOldData);
- }
- SP_ASSERT(GetData() != NULL);
- }
- LPTSTR CSPString::LockBuffer()
- {
- LPTSTR lpsz = GetBuffer(0);
- GetData()->nRefs = -1;
- return lpsz;
- }
- void CSPString::UnlockBuffer()
- {
- SP_ASSERT(GetData()->nRefs == -1);
- if (GetData() != _afxSPDataNil)
- GetData()->nRefs = 1;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Commonly used routines (rarely used routines in STREX.CPP)
- int CSPString::Find(TCHAR ch) const
- {
- return Find(ch, 0);
- }
- int CSPString::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, ch);
- // return -1 if not found and index otherwise
- return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
- }
- int CSPString::FindOneOf(LPCTSTR lpszCharSet) const
- {
- SP_ASSERT(SP_IsValidString(lpszCharSet));
- LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
- return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
- }
- void CSPString::MakeUpper()
- {
- CopyBeforeWrite();
- _tcsupr(m_pchData);
- }
- void CSPString::MakeLower()
- {
- CopyBeforeWrite();
- _tcslwr(m_pchData);
- }
- void CSPString::MakeReverse()
- {
- CopyBeforeWrite();
- _tcsrev(m_pchData);
- }
- void CSPString::SetAt(int nIndex, TCHAR ch)
- {
- SP_ASSERT(nIndex >= 0);
- SP_ASSERT(nIndex < GetData()->nDataLength);
- CopyBeforeWrite();
- m_pchData[nIndex] = ch;
- }
- ///////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////
- // More sophisticated construction
- CSPString::CSPString(TCHAR ch, int nLength)
- {
- Init();
- if (nLength >= 1)
- {
- AllocBuffer(nLength);
- #ifdef _UNICODE
- for (int i = 0; i < nLength; i++)
- m_pchData[i] = ch;
- #else
- memset(m_pchData, ch, nLength);
- #endif
- }
- }
- CSPString::CSPString(LPCTSTR lpch, int nLength)
- {
- Init();
- if (nLength != 0)
- {
- SP_ASSERT(SP_IsValidAddress((void*)lpch, nLength, FALSE));
- AllocBuffer(nLength);
- memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // Special conversion constructors
- #ifdef _UNICODE
- CSPString::CSPString(LPCSTR lpsz, int nLength)
- {
- Init();
- if (nLength != 0)
- {
- AllocBuffer(nLength);
- int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength+1);
- ReleaseBuffer(n >= 0 ? n : -1);
- }
- }
- #else //_UNICODE
- CSPString::CSPString(LPCWSTR lpsz, int nLength)
- {
- Init();
- if (nLength != 0)
- {
- AllocBuffer(nLength*2);
- int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData,
- (nLength*2)+1, NULL, NULL);
- ReleaseBuffer(n >= 0 ? n : -1);
- }
- }
- #endif //!_UNICODE
- //////////////////////////////////////////////////////////////////////////////
- // Assignment operators
- const CSPString& CSPString::operator=(TCHAR ch)
- {
- AssignCopy(1, &ch);
- return *this;
- }
- //////////////////////////////////////////////////////////////////////////////
- // less common string expressions
- CSPString __stdcall operator+(const CSPString& string1, TCHAR ch)
- {
- CSPString s;
- s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
- return s;
- }
- CSPString __stdcall operator+(TCHAR ch, const CSPString& string)
- {
- CSPString s;
- s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
- return s;
- }
- //////////////////////////////////////////////////////////////////////////////
- // Advanced manipulation
- int CSPString::Delete(int nIndex, int nCount /* = 1 */)
- {
- if (nIndex < 0)
- nIndex = 0;
- int nNewLength = GetData()->nDataLength;
- if (nCount > 0 && nIndex < nNewLength)
- {
- CopyBeforeWrite();
- int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
- memcpy(m_pchData + nIndex,
- m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
- GetData()->nDataLength = nNewLength - nCount;
- }
- return nNewLength;
- }
- int CSPString::Insert(int nIndex, TCHAR ch)
- {
- CopyBeforeWrite();
- if (nIndex < 0)
- nIndex = 0;
- int nNewLength = GetData()->nDataLength;
- if (nIndex > nNewLength)
- nIndex = nNewLength;
- nNewLength++;
- if (GetData()->nAllocLength < nNewLength)
- {
- CSPStringData* pOldData = GetData();
- LPTSTR pstr = m_pchData;
- AllocBuffer(nNewLength);
- memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
- CSPString::Release(pOldData);
- }
- // move existing bytes down
- memcpy(m_pchData + nIndex + 1,
- m_pchData + nIndex, (nNewLength-nIndex)*sizeof(TCHAR));
- m_pchData[nIndex] = ch;
- GetData()->nDataLength = nNewLength;
- return nNewLength;
- }
- int CSPString::Insert(int nIndex, LPCTSTR pstr)
- {
- if (nIndex < 0)
- nIndex = 0;
- int nInsertLength = SafeStrlen(pstr);
- int nNewLength = GetData()->nDataLength;
- if (nInsertLength > 0)
- {
- CopyBeforeWrite();
- if (nIndex > nNewLength)
- nIndex = nNewLength;
- nNewLength += nInsertLength;
- if (GetData()->nAllocLength < nNewLength)
- {
- CSPStringData* pOldData = GetData();
- LPTSTR pstr = m_pchData;
- AllocBuffer(nNewLength);
- memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
- CSPString::Release(pOldData);
- }
- // move existing bytes down
- memcpy(m_pchData + nIndex + nInsertLength,
- m_pchData + nIndex,
- (nNewLength-nIndex-nInsertLength+1)*sizeof(TCHAR));
- memcpy(m_pchData + nIndex,
- pstr, nInsertLength*sizeof(TCHAR));
- GetData()->nDataLength = nNewLength;
- }
- return nNewLength;
- }
- int CSPString::Replace(TCHAR chOld, TCHAR chNew)
- {
- int nCount = 0;
- // short-circuit the nop case
- if (chOld != chNew)
- {
- // otherwise modify each character that matches in the string
- CopyBeforeWrite();
- LPTSTR psz = m_pchData;
- LPTSTR pszEnd = psz + GetData()->nDataLength;
- while (psz < pszEnd)
- {
- // replace instances of the specified character only
- if (*psz == chOld)
- {
- *psz = chNew;
- nCount++;
- }
- psz = _tcsinc(psz);
- }
- }
- return nCount;
- }
- int CSPString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
- {
- // can't have empty or NULL lpszOld
- int nSourceLen = SafeStrlen(lpszOld);
- if (nSourceLen == 0)
- return 0;
- int nReplacementLen = SafeStrlen(lpszNew);
- // loop once to figure out the size of the result string
- int nCount = 0;
- LPTSTR lpszStart = m_pchData;
- LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
- LPTSTR lpszTarget;
- while (lpszStart < lpszEnd)
- {
- while ((lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
- {
- nCount++;
- lpszStart = lpszTarget + nSourceLen;
- }
- lpszStart += lstrlen(lpszStart) + 1;
- }
- // if any changes were made, make them
- if (nCount > 0)
- {
- CopyBeforeWrite();
- // if the buffer is too small, just
- // allocate a new buffer (slow but sure)
- int nOldLength = GetData()->nDataLength;
- int nNewLength = nOldLength + (nReplacementLen-nSourceLen)*nCount;
- if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
- {
- CSPStringData* pOldData = GetData();
- LPTSTR pstr = m_pchData;
- AllocBuffer(nNewLength);
- memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR));
- CSPString::Release(pOldData);
- }
- // else, we just do it in-place
- lpszStart = m_pchData;
- lpszEnd = m_pchData + GetData()->nDataLength;
- // loop again to actually do the work
- while (lpszStart < lpszEnd)
- {
- while ( (lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
- {
- int nBalance = nOldLength - (lpszTarget - m_pchData + nSourceLen);
- memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
- nBalance * sizeof(TCHAR));
- memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR));
- lpszStart = lpszTarget + nReplacementLen;
- lpszStart[nBalance] = ' ';
- nOldLength += (nReplacementLen - nSourceLen);
- }
- lpszStart += lstrlen(lpszStart) + 1;
- }
- SP_ASSERT(m_pchData[nNewLength] == ' ');
- GetData()->nDataLength = nNewLength;
- }
- return nCount;
- }
- int CSPString::Remove(TCHAR chRemove)
- {
- CopyBeforeWrite();
- LPTSTR pstrSource = m_pchData;
- LPTSTR pstrDest = m_pchData;
- LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
- while (pstrSource < pstrEnd)
- {
- if (*pstrSource != chRemove)
- {
- *pstrDest = *pstrSource;
- pstrDest = _tcsinc(pstrDest);
- }
- pstrSource = _tcsinc(pstrSource);
- }
- *pstrDest = ' ';
- int nCount = pstrSource - pstrDest;
- GetData()->nDataLength -= nCount;
- return nCount;
- }
- //////////////////////////////////////////////////////////////////////////////
- // Very simple sub-string extraction
- CSPString CSPString::Mid(int nFirst) const
- {
- return Mid(nFirst, GetData()->nDataLength - nFirst);
- }
- CSPString CSPString::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;
- SP_ASSERT(nFirst >= 0);
- SP_ASSERT(nFirst + nCount <= GetData()->nDataLength);
- // optimize case of returning entire string
- if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
- return *this;
- CSPString dest;
- AllocCopy(dest, nCount, nFirst, 0);
- return dest;
- }
- CSPString CSPString::Right(int nCount) const
- {
- if (nCount < 0)
- nCount = 0;
- if (nCount >= GetData()->nDataLength)
- return *this;
- CSPString dest;
- AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
- return dest;
- }
- CSPString CSPString::Left(int nCount) const
- {
- if (nCount < 0)
- nCount = 0;
- if (nCount >= GetData()->nDataLength)
- return *this;
- CSPString dest;
- AllocCopy(dest, nCount, 0, 0);
- return dest;
- }
- // strspn equivalent
- CSPString CSPString::SpanIncluding(LPCTSTR lpszCharSet) const
- {
- SP_ASSERT(SP_IsValidString(lpszCharSet));
- return Left(_tcsspn(m_pchData, lpszCharSet));
- }
- // strcspn equivalent
- CSPString CSPString::SpanExcluding(LPCTSTR lpszCharSet) const
- {
- SP_ASSERT(SP_IsValidString(lpszCharSet));
- return Left(_tcscspn(m_pchData, lpszCharSet));
- }
- //////////////////////////////////////////////////////////////////////////////
- // Finding
- int CSPString::ReverseFind(TCHAR ch) const
- {
- // find last single character
- LPTSTR lpsz = _tcsrchr(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 CSPString::Find(LPCTSTR lpszSub) const
- {
- return Find(lpszSub, 0);
- }
- int CSPString::Find(LPCTSTR lpszSub, int nStart) const
- {
- SP_ASSERT(SP_IsValidString(lpszSub));
- int nLength = GetData()->nDataLength;
- if (nStart > nLength)
- return -1;
- // find first matching substring
- LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
- // return -1 for not found, distance from beginning otherwise
- return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
- }
- /////////////////////////////////////////////////////////////////////////////
- // CSPString formatting
- #define TCHAR_ARG TCHAR
- #define WCHAR_ARG WCHAR
- #define CHAR_ARG char
- struct _AFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
- struct _AFX_FLOAT { BYTE floatBits[sizeof(float)]; };
- #ifdef _X86_
- #define DOUBLE_ARG _AFX_DOUBLE
- #else
- #define DOUBLE_ARG double
- #endif
- #define FORCE_ANSI 0x10000
- #define FORCE_UNICODE 0x20000
- #define FORCE_INT64 0x40000
- void CSPString::FormatV(LPCTSTR lpszFormat, va_list argList)
- {
- SP_ASSERT(SP_IsValidString(lpszFormat));
- va_list argListSave = argList;
- // make a guess at the maximum length of the resulting string
- int nMaxLen = 0;
- for (LPCTSTR lpsz = lpszFormat; *lpsz != ' '; lpsz = _tcsinc(lpsz))
- {
- // handle '%' character, but watch out for '%%'
- if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
- {
- nMaxLen += _tclen(lpsz);
- continue;
- }
- int nItemLen = 0;
- // handle '%' character with format
- int nWidth = 0;
- for (; *lpsz != ' '; lpsz = _tcsinc(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 = _ttoi(lpsz);
- for (; *lpsz != ' ' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
- ;
- }
- SP_ASSERT(nWidth >= 0);
- int nPrecision = 0;
- if (*lpsz == '.')
- {
- // skip past '.' separator (width.precision)
- lpsz = _tcsinc(lpsz);
- // get precision and skip it
- if (*lpsz == '*')
- {
- nPrecision = va_arg(argList, int);
- lpsz = _tcsinc(lpsz);
- }
- else
- {
- nPrecision = _ttoi(lpsz);
- for (; *lpsz != ' ' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
- ;
- }
- SP_ASSERT(nPrecision >= 0);
- }
- // should be on type modifier or specifier
- int nModifier = 0;
- if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
- {
- lpsz += 3;
- nModifier = FORCE_INT64;
- #if !defined(_X86_) && !defined(_ALPHA_)
- // __int64 is only available on X86 and ALPHA platforms
- SP_ASSERT(FALSE);
- #endif
- }
- else
- {
- switch (*lpsz)
- {
- // modifiers that affect size
- case 'h':
- nModifier = FORCE_ANSI;
- lpsz = _tcsinc(lpsz);
- break;
- case 'l':
- nModifier = FORCE_UNICODE;
- lpsz = _tcsinc(lpsz);
- break;
- // modifiers that do not affect size
- case 'F':
- case 'N':
- case 'L':
- lpsz = _tcsinc(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':
- {
- LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
- if (pstrNextArg == NULL)
- nItemLen = 6; // "(null)"
- else
- {
- nItemLen = lstrlen(pstrNextArg);
- nItemLen = max(1, nItemLen);
- }
- }
- break;
- case 'S':
- {
- #ifndef _UNICODE
- LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
- if (pstrNextArg == NULL)
- nItemLen = 6; // "(null)"
- else
- {
- nItemLen = wcslen(pstrNextArg);
- nItemLen = max(1, nItemLen);
- }
- #else
- LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
- if (pstrNextArg == NULL)
- nItemLen = 6; // "(null)"
- else
- {
- nItemLen = lstrlenA(pstrNextArg);
- nItemLen = max(1, nItemLen);
- }
- #endif
- }
- break;
- case 's'|FORCE_ANSI:
- case 'S'|FORCE_ANSI:
- {
- LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
- if (pstrNextArg == NULL)
- nItemLen = 6; // "(null)"
- else
- {
- nItemLen = lstrlenA(pstrNextArg);
- nItemLen = max(1, nItemLen);
- }
- }
- break;
- case 's'|FORCE_UNICODE:
- case 'S'|FORCE_UNICODE:
- {
- LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
- if (pstrNextArg == NULL)
- nItemLen = 6; // "(null)"
- else
- {
- nItemLen = wcslen(pstrNextArg);
- nItemLen = max(1, nItemLen);
- }
- }
- break;
- }
- // adjust nItemLen for strings
- if (nItemLen != 0)
- {
- if (nPrecision != 0)
- nItemLen = min(nItemLen, nPrecision);
- nItemLen = max(nItemLen, nWidth);
- }
- else
- {
- switch (*lpsz)
- {
- // integers
- case 'd':
- case 'i':
- case 'u':
- case 'x':
- case 'X':
- case 'o':
- if (nModifier & FORCE_INT64)
- va_arg(argList, __int64);
- else
- va_arg(argList, int);
- nItemLen = 32;
- nItemLen = max(nItemLen, nWidth+nPrecision);
- break;
- case 'e':
- case 'g':
- case 'G':
- va_arg(argList, DOUBLE_ARG);
- nItemLen = 128;
- nItemLen = max(nItemLen, nWidth+nPrecision);
- break;
- case 'f':
- va_arg(argList, DOUBLE_ARG);
- nItemLen = 128; // width isn't truncated
- // 312 == strlen("-1+(309 zeroes).")
- // 309 zeroes == max precision of a double
- nItemLen = max(nItemLen, 312+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:
- SP_ASSERT(FALSE); // unknown formatting option
- }
- }
- // adjust nMaxLen for output nItemLen
- nMaxLen += nItemLen;
- }
- GetBuffer(nMaxLen);
- SP_VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
- ReleaseBuffer();
- va_end(argListSave);
- }
- /* My Version
- // formatting (using wsprintf style formatting)
- void __cdecl CSPString::Format(LPCTSTR lpszFormat, ...)
- {
- // 暂不支持较长字符串
- SP_ASSERT( NULL != lpszFormat && strlen(lpszFormat) < 1024 );
- size_t nSize = 1024+4*strlen(lpszFormat);
- TCHAR * lpBuf = new TCHAR[nSize];
- va_list argList;
- va_start(argList, lpszFormat);
- vsprintf( lpBuf, lpszFormat, argList);
- va_end(argList);
- *this = lpBuf;
- delete [] lpBuf;
- }
- */
- // formatting (using wsprintf style formatting)
- void __cdecl CSPString::Format(LPCTSTR lpszFormat, ...)
- {
- SP_ASSERT(SP_IsValidString(lpszFormat));
- va_list argList;
- va_start(argList, lpszFormat);
- FormatV(lpszFormat, argList);
- va_end(argList);
- }
- void __cdecl CSPString::Format(UINT nFormatID, ...)
- {
- CSPString strFormat;
- SP_VERIFY(strFormat.LoadString(nFormatID) != 0);
- va_list argList;
- va_start(argList, nFormatID);
- FormatV(strFormat, argList);
- va_end(argList);
- }
- BOOL CSPString::LoadString(UINT nID)
- {
- SP_ASSERT( FALSE ); // Not supported Now!!!
- return FALSE;
- }
- void CSPString::TrimRight(LPCTSTR lpszTargetList)
- {
- // find beginning of trailing matches
- // by starting at beginning (DBCS aware)
- CopyBeforeWrite();
- LPTSTR lpsz = m_pchData;
- LPTSTR lpszLast = NULL;
- while (*lpsz != ' ')
- {
- if (_tcschr(lpszTargetList, *lpsz) != NULL)
- {
- if (lpszLast == NULL)
- lpszLast = lpsz;
- }
- else
- lpszLast = NULL;
- lpsz = _tcsinc(lpsz);
- }
- if (lpszLast != NULL)
- {
- // truncate at left-most matching character
- *lpszLast = ' ';
- GetData()->nDataLength = lpszLast - m_pchData;
- }
- }
- void CSPString::TrimRight(TCHAR chTarget)
- {
- // find beginning of trailing matches
- // by starting at beginning (DBCS aware)
- CopyBeforeWrite();
- LPTSTR lpsz = m_pchData;
- LPTSTR lpszLast = NULL;
- while (*lpsz != ' ')
- {
- if (*lpsz == chTarget)
- {
- if (lpszLast == NULL)
- lpszLast = lpsz;
- }
- else
- lpszLast = NULL;
- lpsz = _tcsinc(lpsz);
- }
- if (lpszLast != NULL)
- {
- // truncate at left-most matching character
- *lpszLast = ' ';
- GetData()->nDataLength = lpszLast - m_pchData;
- }
- }
- void CSPString::TrimRight()
- {
- // find beginning of trailing spaces by starting at beginning (DBCS aware)
- CopyBeforeWrite();
- LPTSTR lpsz = m_pchData;
- LPTSTR lpszLast = NULL;
- while (*lpsz != ' ')
- {
- if (_istspace(*lpsz))
- {
- if (lpszLast == NULL)
- lpszLast = lpsz;
- }
- else
- lpszLast = NULL;
- lpsz = _tcsinc(lpsz);
- }
- if (lpszLast != NULL)
- {
- // truncate at trailing space start
- *lpszLast = ' ';
- GetData()->nDataLength = lpszLast - m_pchData;
- }
- }
- void CSPString::TrimLeft(LPCTSTR lpszTargets)
- {
- // if we're not trimming anything, we're not doing any work
- if (SafeStrlen(lpszTargets) == 0)
- return;
- CopyBeforeWrite();
- LPCTSTR lpsz = m_pchData;
- while (*lpsz != ' ')
- {
- if (_tcschr(lpszTargets, *lpsz) == NULL)
- break;
- lpsz = _tcsinc(lpsz);
- }
- if (lpsz != m_pchData)
- {
- // fix up data and length
- int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
- memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
- GetData()->nDataLength = nDataLength;
- }
- }
- void CSPString::TrimLeft(TCHAR chTarget)
- {
- // find first non-matching character
- CopyBeforeWrite();
- LPCTSTR lpsz = m_pchData;
- while (chTarget == *lpsz)
- lpsz = _tcsinc(lpsz);
- if (lpsz != m_pchData)
- {
- // fix up data and length
- int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
- memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
- GetData()->nDataLength = nDataLength;
- }
- }
- void CSPString::TrimLeft()
- {
- // find first non-space character
- CopyBeforeWrite();
- LPCTSTR lpsz = m_pchData;
- while (_istspace(*lpsz))
- lpsz = _tcsinc(lpsz);
- if (lpsz != m_pchData)
- {
- // fix up data and length
- int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
- memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
- GetData()->nDataLength = nDataLength;
- }
- }