|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
bsstring.cpp
Abstract:
This module implements the CBsString class. This class manages character arrays in a similar manner as the CString class in VC++. In fact, this class is a copy of the CString class with the MFC specific stuff ripped out since LTS doesn't use MTF.
Author:
Stefan R. Steiner [SSteiner] 1-Mar-1998
Revision History:
Stefan R. Steiner [SSteiner] 10-Apr-2000 Added fixed allocator code and resynced with MFC 6 SR-1 code --*/
#include "stdafx.h"
#include "bsstring.h"
#include "malloc.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
struct _BSAFX_DOUBLE { BYTE doubleBits[sizeof(double)]; }; struct _BSAFX_FLOAT { BYTE floatBits[sizeof(float)]; };
// #define new DEBUG_NEW
/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines
TCHAR bsafxChNil = '\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)
static int _bsafxInitData[] = { -1, 0, 0, 0 }; static CBsStringData* _bsafxDataNil = (CBsStringData*)&_bsafxInitData; static LPCTSTR _bsafxPchNil = (LPCTSTR)(((BYTE*)&_bsafxInitData)+sizeof(CBsStringData)); // special function to make bsafxEmptyString work even during initialization
const CBsString& BSAFXAPI BsAfxGetEmptyString() { return *(CBsString*)&_bsafxPchNil; }
//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
CBsString::CBsString() { Init(); }
CBsString::CBsString(const CBsString& stringSrc) { ASSERT(stringSrc.GetData()->nRefs != 0); if (stringSrc.GetData()->nRefs >= 0) { ASSERT(stringSrc.GetData() != _bsafxDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } else { Init(); *this = stringSrc.m_pchData; } }
CBsString::CBsString(GUID guid) { Init(); AllocBuffer(38); _stprintf( m_pchData, _T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"), guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] ); }
#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)
static CBsFixedAlloc _bsafxAlloc8(ROUND4(9*sizeof(TCHAR)+sizeof(CBsStringData)), 1024); static CBsFixedAlloc _bsafxAlloc16(ROUND4(17*sizeof(TCHAR)+sizeof(CBsStringData)), 512); static CBsFixedAlloc _bsafxAlloc32(ROUND4(33*sizeof(TCHAR)+sizeof(CBsStringData)), 256); static CBsFixedAlloc _bsafxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CBsStringData))); static CBsFixedAlloc _bsafxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CBsStringData))); static CBsFixedAlloc _bsafxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CBsStringData))); static CBsFixedAlloc _bsafxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CBsStringData)));
#endif //!_DEBUG
void CBsString::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination
// assumes [optimistically] that data length will equal allocation length
// Throws E_OUTOFMEMORY when out of memory
{ ASSERT(nLen >= 0); ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
if (nLen == 0) Init(); else { CBsStringData* pData; #ifndef _DEBUG
if (nLen <= 8) { pData = (CBsStringData*)_bsafxAlloc8.Alloc(); pData->nAllocLength = 8; } else if (nLen <= 16) { pData = (CBsStringData*)_bsafxAlloc16.Alloc(); pData->nAllocLength = 16; } else if (nLen <= 32) { pData = (CBsStringData*)_bsafxAlloc32.Alloc(); pData->nAllocLength = 32; } else if (nLen <= 64) { pData = (CBsStringData*)_bsafxAlloc64.Alloc(); pData->nAllocLength = 64; } else if (nLen <= 128) { pData = (CBsStringData*)_bsafxAlloc128.Alloc(); pData->nAllocLength = 128; } else if (nLen <= 256) { pData = (CBsStringData*)_bsafxAlloc256.Alloc(); pData->nAllocLength = 256; } else if (nLen <= 512) { pData = (CBsStringData*)_bsafxAlloc512.Alloc(); pData->nAllocLength = 512; } else #endif
{ pData = (CBsStringData*) new BYTE[sizeof(CBsStringData) + (nLen+1)*sizeof(TCHAR)]; if ( pData == NULL ) // Prefix #118828
throw E_OUTOFMEMORY; pData->nAllocLength = nLen; } pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); } }
void FASTCALL CBsString::FreeData(CBsStringData* pData) { #ifndef _DEBUG
int nLen = pData->nAllocLength; if (nLen == 8) _bsafxAlloc8.Free(pData); else if (nLen == 16) _bsafxAlloc16.Free(pData); else if (nLen == 32) _bsafxAlloc32.Free(pData); else if (nLen == 64) _bsafxAlloc64.Free(pData); else if (nLen == 128) _bsafxAlloc128.Free(pData); else if (nLen == 256) _bsafxAlloc256.Free(pData); else if (nLen == 512) _bsafxAlloc512.Free(pData); else { ASSERT(nLen > 512); delete[] (BYTE*)pData; } #else
delete[] (BYTE*)pData; #endif
}
void CBsString::Release() { if (GetData() != _bsafxDataNil) { ASSERT(GetData()->nRefs != 0); if (InterlockedDecrement(&GetData()->nRefs) <= 0) FreeData(GetData()); Init(); } }
void PASCAL CBsString::Release(CBsStringData* pData) { if (pData != _bsafxDataNil) { ASSERT(pData->nRefs != 0); if (InterlockedDecrement(&pData->nRefs) <= 0) FreeData(pData); } }
void CBsString::Empty() { if (GetData()->nDataLength == 0) return; if (GetData()->nRefs >= 0) Release(); else *this = &bsafxChNil; ASSERT(GetData()->nDataLength == 0); ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); }
void CBsString::CopyBeforeWrite() { if (GetData()->nRefs > 1) { CBsStringData* pData = GetData(); Release(); AllocBuffer(pData->nDataLength); memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR)); } ASSERT(GetData()->nRefs <= 1); }
void CBsString::AllocBeforeWrite(int nLen) { if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) { Release(); AllocBuffer(nLen); } ASSERT(GetData()->nRefs <= 1); }
CBsString::~CBsString() // free any attached data
{ if (GetData() != _bsafxDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) FreeData(GetData()); } }
//////////////////////////////////////////////////////////////////////////////
// Helpers for the rest of the implementation
void CBsString::AllocCopy(CBsString& 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
CBsString::CBsString(LPCTSTR lpsz) { Init(); int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } }
/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors
#ifdef _UNICODE
CBsString::CBsString(LPCSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if (nSrcLen != 0) { AllocBuffer(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); } } #else //_UNICODE
CBsString::CBsString(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
//////////////////////////////////////////////////////////////////////////////
// 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 CBsString&' so that
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
//
void CBsString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; }
const CBsString& CBsString::operator=(const CBsString& stringSrc) { if (m_pchData != stringSrc.m_pchData) { if ((GetData()->nRefs < 0 && GetData() != _bsafxDataNil) || 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() != _bsafxDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } } return *this; }
const CBsString& CBsString::operator=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; }
/////////////////////////////////////////////////////////////////////////////
// Special conversion assignment
#ifdef _UNICODE
const CBsString& CBsString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; AllocBeforeWrite(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); return *this; } #else //!_UNICODE
const CBsString& CBsString::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:
// CBsString + CBsString
// and for ? = TCHAR, LPCTSTR
// CBsString + ?
// ? + CBsString
void CBsString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine
// Concatenate two sources
// -- assume that 'this' is a new CBsString 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)); } }
CBsString BSAFXAPI operator+(const CBsString& string1, const CBsString& string2) { CBsString s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; }
CBsString BSAFXAPI operator+(const CBsString& string, LPCTSTR lpsz) { ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz)); CBsString s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CBsString::SafeStrlen(lpsz), lpsz); return s; }
CBsString BSAFXAPI operator+(LPCTSTR lpsz, const CBsString& string) { ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz)); CBsString s; s.ConcatCopy(CBsString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); return s; }
//////////////////////////////////////////////////////////////////////////////
// concatenate in place
void CBsString::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
CBsStringData* pOldData = GetData(); ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); ASSERT(pOldData != NULL); CBsString::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 CBsString& CBsString::operator+=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; }
const CBsString& CBsString::operator+=(TCHAR ch) { ConcatInPlace(1, &ch); return *this; }
const CBsString& CBsString::operator+=(const CBsString& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; }
///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access
LPTSTR CBsString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0);
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { // we have to grow the buffer
CBsStringData* 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; CBsString::Release(pOldData); } ASSERT(GetData()->nRefs <= 1);
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL); return m_pchData; }
void CBsString::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 CBsString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0);
GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; }
void CBsString::FreeExtra() { ASSERT(GetData()->nDataLength <= GetData()->nAllocLength); if (GetData()->nDataLength != GetData()->nAllocLength) { CBsStringData* pOldData = GetData(); AllocBuffer(GetData()->nDataLength); memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR)); ASSERT(m_pchData[GetData()->nDataLength] == '\0'); CBsString::Release(pOldData); } ASSERT(GetData() != NULL); }
LPTSTR CBsString::LockBuffer() { LPTSTR lpsz = GetBuffer(0); GetData()->nRefs = -1; return lpsz; }
void CBsString::UnlockBuffer() { ASSERT(GetData()->nRefs == -1); if (GetData() != _bsafxDataNil) GetData()->nRefs = 1; }
///////////////////////////////////////////////////////////////////////////////
// Commonly used routines (rarely used routines in STREX.CPP)
int CBsString::Find(TCHAR ch) const { return Find(ch, 0); }
int CBsString::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 CBsString::FindOneOf(LPCTSTR lpszCharSet) const { ASSERT(BsAfxIsValidString(lpszCharSet)); LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); }
void CBsString::MakeUpper() { CopyBeforeWrite(); _tcsupr(m_pchData); }
void CBsString::MakeLower() { CopyBeforeWrite(); _tcslwr(m_pchData); }
void CBsString::MakeReverse() { CopyBeforeWrite(); _tcsrev(m_pchData); }
void CBsString::SetAt(int nIndex, TCHAR ch) { ASSERT(nIndex >= 0); ASSERT(nIndex < GetData()->nDataLength);
CopyBeforeWrite(); m_pchData[nIndex] = ch; }
#ifndef _UNICODE
void CBsString::AnsiToOem() { CopyBeforeWrite(); ::AnsiToOem(m_pchData, m_pchData); } void CBsString::OemToAnsi() { CopyBeforeWrite(); ::OemToAnsi(m_pchData, m_pchData); } #endif
///////////////////////////////////////////////////////////////////////////////
// CBsString conversion helpers (these use the current system locale)
int BSAFX_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, (INT)count, NULL, NULL); ASSERT(mbstr == NULL || result <= (int)count); if (result > 0) mbstr[result-1] = 0; return result; }
int BSAFX_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, (INT)count); ASSERT(wcstr == NULL || result <= (int)count); if (result > 0) wcstr[result-1] = 0; return result; }
LPWSTR BSAFXAPI BsAfxA2WHelper(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'; MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars); return lpw; UNREFERENCED_PARAMETER( nChars ); }
LPSTR BSAFXAPI BsAfxW2AHelper(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'; WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL); return lpa; UNREFERENCED_PARAMETER( nChars ); }
//
// the following is from strex.cpp
//
//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction
CBsString::CBsString(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
} }
CBsString::CBsString(LPCTSTR lpch, int nLength) { Init(); if (nLength != 0) { ASSERT(BsAfxIsValidAddress(lpch, nLength, FALSE)); AllocBuffer(nLength); memcpy(m_pchData, lpch, nLength*sizeof(TCHAR)); } }
/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors
#ifdef _UNICODE
CBsString::CBsString(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
CBsString::CBsString(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 CBsString& CBsString::operator=(TCHAR ch) { AssignCopy(1, &ch); return *this; }
//////////////////////////////////////////////////////////////////////////////
// less common string expressions
CBsString BSAFXAPI operator+(const CBsString& string1, TCHAR ch) { CBsString s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch); return s; }
CBsString BSAFXAPI operator+(TCHAR ch, const CBsString& string) { CBsString s; s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); return s; }
//////////////////////////////////////////////////////////////////////////////
// Advanced manipulation
int CBsString::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 CBsString::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) { CBsStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR)); CBsString::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 CBsString::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) { CBsStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR)); CBsString::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 CBsString::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 CBsString::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) { CBsStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR)); CBsString::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 - (int)(lpszTarget - m_pchData + nSourceLen); memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR)); memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR)); lpszStart = lpszTarget + nReplacementLen; lpszStart[nBalance] = '\0'; nOldLength += (nReplacementLen - nSourceLen); } lpszStart += lstrlen(lpszStart) + 1; } ASSERT(m_pchData[nNewLength] == '\0'); GetData()->nDataLength = nNewLength; }
return nCount; }
int CBsString::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 = '\0'; int nCount = ( int )( pstrSource - pstrDest ); GetData()->nDataLength -= nCount;
return nCount; }
//////////////////////////////////////////////////////////////////////////////
// Very simple sub-string extraction
CBsString CBsString::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); }
CBsString CBsString::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;
CBsString dest; AllocCopy(dest, nCount, nFirst, 0); return dest; }
CBsString CBsString::Right(int nCount) const { if (nCount < 0) nCount = 0; if (nCount >= GetData()->nDataLength) return *this;
CBsString dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; }
CBsString CBsString::Left(int nCount) const { if (nCount < 0) nCount = 0; if (nCount >= GetData()->nDataLength) return *this;
CBsString dest; AllocCopy(dest, nCount, 0, 0); return dest; }
// strspn equivalent
CBsString CBsString::SpanIncluding(LPCTSTR lpszCharSet) const { ASSERT(BsAfxIsValidString(lpszCharSet)); return Left((INT)_tcsspn(m_pchData, lpszCharSet)); }
// strcspn equivalent
CBsString CBsString::SpanExcluding(LPCTSTR lpszCharSet) const { ASSERT(BsAfxIsValidString(lpszCharSet)); return Left((INT)_tcscspn(m_pchData, lpszCharSet)); }
//////////////////////////////////////////////////////////////////////////////
// Finding
int CBsString::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 CBsString::Find(LPCTSTR lpszSub) const { return Find(lpszSub, 0); }
int CBsString::Find(LPCTSTR lpszSub, int nStart) const { ASSERT(BsAfxIsValidString(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); }
/////////////////////////////////////////////////////////////////////////////
// CBsString formatting
#define TCHAR_ARG TCHAR
#define WCHAR_ARG WCHAR
#define CHAR_ARG char
#ifdef _X86_
#define DOUBLE_ARG _BSAFX_DOUBLE
#else
#define DOUBLE_ARG double
#endif
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
#define FORCE_INT64 0x40000
void CBsString::FormatV(LPCTSTR lpszFormat, va_list argList) { ASSERT(BsAfxIsValidString(lpszFormat));
va_list argListSave = argList;
// make a guess at the maximum length of the resulting string
int nMaxLen = 0; for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz)) { // handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%') { nMaxLen += (INT)_tclen(lpsz); continue; }
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0; for (; *lpsz != '\0'; 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 != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } 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 != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } 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
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 = (INT)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': { double f; LPTSTR pszTemp;
// 312 == strlen("-1+(309 zeroes).")
// 309 zeroes == max precision of a double
// 6 == adjustment in case precision is not specified,
// which means that the precision defaults to 6
pszTemp = (LPTSTR)_alloca(max(nWidth, 312+nPrecision+6));
f = va_arg(argList, double); _stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f ); nItemLen = _tcslen(pszTemp); } 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: ASSERT(FALSE); // unknown formatting option
} }
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen; }
GetBuffer(nMaxLen); INT i = _vstprintf(m_pchData, lpszFormat, argListSave); ASSERT( i <= GetAllocLength() ); ReleaseBuffer();
va_end(argListSave); }
// formatting (using wsprintf style formatting)
void BSAFX_CDECL CBsString::Format(LPCTSTR lpszFormat, ...) { ASSERT(BsAfxIsValidString(lpszFormat));
va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); }
void CBsString::TrimRight(LPCTSTR lpszTargetList) { // find beginning of trailing matches
// by starting at beginning (DBCS aware)
CopyBeforeWrite(); LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL;
while (*lpsz != '\0') { 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 = '\0'; GetData()->nDataLength = (int)(lpszLast - m_pchData); } }
void CBsString::TrimRight(TCHAR chTarget) { // find beginning of trailing matches
// by starting at beginning (DBCS aware)
CopyBeforeWrite(); LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL;
while (*lpsz != '\0') { if (*lpsz == chTarget) { if (lpszLast == NULL) lpszLast = lpsz; } else lpszLast = NULL; lpsz = _tcsinc(lpsz); }
if (lpszLast != NULL) { // truncate at left-most matching character
*lpszLast = '\0'; GetData()->nDataLength = (int)(lpszLast - m_pchData); } }
void CBsString::TrimRight() { // find beginning of trailing spaces by starting at beginning (DBCS aware)
CopyBeforeWrite(); LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != '\0') { if (_istspace(*lpsz)) { if (lpszLast == NULL) lpszLast = lpsz; } else lpszLast = NULL; lpsz = _tcsinc(lpsz); }
if (lpszLast != NULL) { // truncate at trailing space start
*lpszLast = '\0'; GetData()->nDataLength = (int)(lpszLast - m_pchData); } }
void CBsString::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 != '\0') { if (_tcschr(lpszTargets, *lpsz) == NULL) break; lpsz = _tcsinc(lpsz); }
if (lpsz != m_pchData) { // fix up data and length
int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } }
void CBsString::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 - (int)(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } }
void CBsString::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 - (int)(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } }
//
// From validadd.cpp
//
BOOL BSAFXAPI BsAfxIsValidString(LPCWSTR lpsz, int nLength) { if (lpsz == NULL) return FALSE; return ::IsBadStringPtrW(lpsz, nLength) == 0; }
BOOL BSAFXAPI BsAfxIsValidString(LPCSTR lpsz, int nLength) { if (lpsz == NULL) return FALSE; return ::IsBadStringPtrA(lpsz, nLength) == 0; }
BOOL BSAFXAPI BsAfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite) { // simple version using Win-32 APIs for pointer validation.
return (lp != NULL && !IsBadReadPtr(lp, nBytes) && (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes))); } ///////////////////////////////////////////////////////////////////////////////
|