?? spstring.cpp
字號:
/*
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 "SpString.inl"
#undef _SPSTRING_INLINE
#endif
/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines
// afxSPChNil is left for backward compatibility
TCHAR afxSPChNil = '\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 _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 '\0' 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] = '\0';
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) 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
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] = '\0';
}
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] = '\0';
}
}
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] = '\0';
}
LPTSTR CSPString::GetBufferSetLength(int nNewLength)
{
SP_ASSERT(nNewLength >= 0);
GetBuffer(nNewLength);
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
return m_pchData;
}
void CSPString::FreeExtra()
{
SP_ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
if (GetData()->nDataLength != GetData()->nAllocLength)
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -