|
|
// FauxMFC.cpp
#include "stdafx.h"
#include "FauxMFC.h"
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
const TCHAR afxChNil = '\0'; const /*AFX_STATIC_DATA*/ int _afxInitData[] = { -1, 0, 0, 0 }; const /*AFX_STATIC_DATA*/ CStringData* _afxDataNil = (CStringData*)&_afxInitData; const LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData)+sizeof(CStringData));
struct _AFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
#define TCHAR_ARG TCHAR
#define WCHAR_ARG WCHAR
#define CHAR_ARG char
#define DOUBLE_ARG _AFX_DOUBLE
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
#define FORCE_INT64 0x40000
#define IS_DIGIT(c) ((UINT)(c) - (UINT)('0') <= 9)
//////////////////////////////////////////////////////////////////////////////
// Global MFC stuff
HINSTANCE AFXAPI AfxGetResourceHandle(void) { return GetModuleHandle(NULL); }
BOOL AFXAPI AfxIsValidString(LPCSTR lpsz, int nLength = -1) { if (lpsz == NULL) return FALSE; return ::IsBadStringPtrA(lpsz, nLength) == 0; }
//////////////////////////////////////////////////////////////////////////////
// CString
CString::CString(LPCTSTR lpsz) { Init(); if (lpsz != NULL && HIWORD(lpsz) == NULL) { ASSERT(FALSE); //UINT nID = LOWORD((DWORD)lpsz);
//LoadString(nID);
} else { int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } }
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; } }
CString::CString(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
} }
CString::CString(LPCTSTR lpch, int nLength) { Init(); if (nLength != 0) { // ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
AllocBuffer(nLength); memcpy(m_pchData, lpch, nLength*sizeof(TCHAR)); } }
CString::~CString() // free any attached data
{ if (GetData() != _afxDataNil) { ASSERT( 0 != GetData()->nRefs ); if (InterlockedDecrement(&GetData()->nRefs) == 0) { FreeData(GetData()); } } }
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; }
BOOL CString::LoadString(UINT nID) { HINSTANCE hInst = AfxGetResourceHandle(); int cch; if (!FindResourceString(hInst, nID, &cch, 0)) return FALSE;
AllocBuffer(cch); if (cch != 0) ::LoadString(hInst, nID, this->m_pchData, cch+1);
return TRUE; }
void CString::FormatV(LPCTSTR lpszFormat, va_list argList) { // ASSERT(AfxIsValidString(lpszFormat));
va_list argListSave = argList; int nMaxLen = 0;
// make a guess at the maximum length of the resulting string
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; ++lpsz) { // handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(++lpsz) == '%') { nMaxLen += 2; //_tclen(lpsz);
continue; }
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0; for (; *lpsz != '\0'; lpsz = CharNext(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 = MyAtoi(lpsz); //_ttoi(lpsz);
for (; *lpsz != '\0' && IS_DIGIT(*lpsz); lpsz = CharNext(lpsz)) ; } ASSERT(nWidth >= 0);
int nPrecision = 0; if (*lpsz == '.') { // skip past '.' separator (width.precision)
lpsz = CharNext(lpsz);
// get precision and skip it
if (*lpsz == '*') { nPrecision = va_arg(argList, int); lpsz = CharNext(lpsz); } else { nPrecision = MyAtoi(lpsz); //_ttoi(lpsz);
for (; *lpsz != '\0' && IS_DIGIT(*lpsz); lpsz = CharNext(lpsz)) ; } ASSERT(nPrecision >= 0); }
// should be on type modifier or specifier
int nModifier = 0; #if 0 // we don't need this code -ks 7/26/1999
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 #endif
{ switch (*lpsz) { // modifiers that affect size
case 'h': nModifier = FORCE_ANSI; lpsz = CharNext(lpsz); break; case 'l': nModifier = FORCE_UNICODE; lpsz = CharNext(lpsz); break;
// modifiers that do not affect size
case 'F': case 'N': case 'L': lpsz = CharNext(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: ASSERT(FALSE); // unknown formatting option
} }
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen; }
GetBuffer(nMaxLen); #ifdef UNICODE
wvnsprintf(m_pchData, ARRAYSIZE(m_pchData), lpszFormat, argListSave); #else
wvsprintf(m_pchData, lpszFormat, argListSave); #endif
ReleaseBuffer();
va_end(argListSave); }
void AFX_CDECL CString::Format(UINT nFormatID, ...) { CString strFormat; strFormat.LoadString(nFormatID);
va_list argList; va_start(argList, nFormatID); FormatV(strFormat, argList); va_end(argList); }
// formatting (using wsprintf style formatting)
void AFX_CDECL CString::Format(LPCTSTR lpszFormat, ...) { ASSERT(AfxIsValidString(lpszFormat));
va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); }
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); }
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; }
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; }
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; }
CString CString::Left(int nCount) const { if (nCount < 0) nCount = 0; if (nCount >= GetData()->nDataLength) return *this;
CString dest; AllocCopy(dest, nCount, 0, 0); return dest; }
CString CString::Right(int nCount) const { if (nCount < 0) nCount = 0; if (nCount >= GetData()->nDataLength) return *this;
CString dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; }
// find a sub-string (like strstr)
int CString::Find(LPCTSTR lpszSub) const { return Find(lpszSub, 0); }
int CString::Find(LPCTSTR lpszSub, int nStart) const { ASSERT(AfxIsValidString(lpszSub));
int nLength = GetData()->nDataLength; if (nStart > nLength) return -1;
// find first matching substring
// LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
LPTSTR lpsz = strstr(m_pchData + nStart, lpszSub);
// return -1 for not found, distance from beginning otherwise
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); }
LPTSTR CString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0);
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { // 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; }
LPTSTR CString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0);
GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; }
void CString::Release() { if (GetData() != _afxDataNil) { ASSERT( 0 != GetData()->nRefs ); if (InterlockedDecrement(&GetData()->nRefs) == 0) { FreeData(GetData()); } Init(); } }
void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { if (nSrcLen) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; } }
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)); } }
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; pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; if (pData) { pData->nAllocLength = nLen; pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); } } }
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); }
void PASCAL CString::Release(CStringData* pData) { if (pData != _afxDataNil) { ASSERT(pData->nRefs != 0); if (InterlockedDecrement(&pData->nRefs) == 0) { FreeData(pData); } } }
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'; }
void FASTCALL CString::FreeData(CStringData* pData) { //#ifndef _DEBUG
#ifdef TEST
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::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)); } }
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; }
CString CString::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); }
CString CString::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;
CString dest; AllocCopy(dest, nCount, nFirst, 0); return dest; }
//////////////////////////////////////////////////////////////////////////////
// CWinThread
CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam) { m_pfnThreadProc = pfnThreadProc; m_pThreadParams = pParam;
CommonConstruct(); }
CWinThread::CWinThread() { m_pThreadParams = NULL; m_pfnThreadProc = NULL;
CommonConstruct(); }
void CWinThread::CommonConstruct() { // no HTHREAD until it is created
m_hThread = NULL; m_nThreadID = 0; }
CWinThread::~CWinThread() { // free thread object
if (m_hThread != NULL) CloseHandle(m_hThread); //TODO:fix
// cleanup module state
// AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
// if (pState->m_pCurrentWinThread == this)
// pState->m_pCurrentWinThread = NULL;
}
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs) { ASSERT(pfnThreadProc != NULL);
CWinThread* pThread = new CWinThread(pfnThreadProc, pParam); if (pThread) { if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize, lpSecurityAttrs)) { pThread->Delete(); return NULL; } pThread->SetThreadPriority(nPriority); if (!(dwCreateFlags & CREATE_SUSPENDED)) pThread->ResumeThread(); }
return pThread; }
BOOL CWinThread::SetThreadPriority(int nPriority) { ASSERT(m_hThread != NULL); return ::SetThreadPriority(m_hThread, nPriority); }
BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize, LPSECURITY_ATTRIBUTES lpSecurityAttrs) { ASSERT(m_hThread == NULL); // already created?
m_hThread = ::CreateThread(lpSecurityAttrs, nStackSize, m_pfnThreadProc, m_pThreadParams, dwCreateFlags, &m_nThreadID); if (m_hThread == NULL) return FALSE;
return TRUE; }
DWORD CWinThread::ResumeThread() { ASSERT(m_hThread != NULL); return ::ResumeThread(m_hThread); }
void CWinThread::Delete() { delete this; }
/////////////////////////////////////////////////////////////////////////////
// CWinThread default implementation
BOOL CWinThread::InitInstance() { // ASSERT_VALID(this);
return FALSE; // by default don't enter run loop
} int CWinThread::ExitInstance() { // ASSERT_VALID(this);
// ASSERT(AfxGetApp() != this);
// int nResult = m_msgCur.wParam; // returns the value from PostQuitMessage
return 0; }
//////////////////////////////////////////////////////////////////////////////
// CStringArray
static inline void ConstructElement(CString* pNewData) { memcpy(pNewData, &afxEmptyString, sizeof(CString)); }
static inline void DestructElement(CString* pOldData) { pOldData->~CString(); }
static inline void CopyElement(CString* pSrc, CString* pDest) { *pSrc = *pDest; }
static void ConstructElements(CString* pNewData, int nCount) { ASSERT(nCount >= 0);
while (nCount--) { ConstructElement(pNewData); pNewData++; } }
static void DestructElements(CString* pOldData, int nCount) { ASSERT(nCount >= 0);
while (nCount--) { DestructElement(pOldData); pOldData++; } }
static void CopyElements(CString* pDest, CString* pSrc, int nCount) { ASSERT(nCount >= 0);
while (nCount--) { *pDest = *pSrc; ++pDest; ++pSrc; } }
/////////////////////////////////////////////////////////////////////////////
CStringArray::CStringArray() { m_pData = NULL; m_nSize = m_nMaxSize = m_nGrowBy = 0; }
CStringArray::~CStringArray() { // ASSERT_VALID(this);
DestructElements(m_pData, m_nSize); delete[] (BYTE*)m_pData; }
void CStringArray::SetSize(int nNewSize, int nGrowBy) { // ASSERT_VALID(this);
ASSERT(nNewSize >= 0);
if (nGrowBy != -1) m_nGrowBy = nGrowBy; // set new size
if (nNewSize == 0) { // shrink to nothing
DestructElements(m_pData, m_nSize); delete[] (BYTE*)m_pData; m_pData = NULL; m_nSize = m_nMaxSize = 0; } else if (m_pData == NULL) { // create one with exact size
#ifdef SIZE_T_MAX
ASSERT(nNewSize <= SIZE_T_MAX/sizeof(CString)); // no overflow
#endif
m_pData = (CString*) new BYTE[nNewSize * sizeof(CString)]; if (m_pData) { ConstructElements(m_pData, nNewSize);
m_nSize = m_nMaxSize = nNewSize; } } else if (nNewSize <= m_nMaxSize) { // it fits
if (nNewSize > m_nSize) { // initialize the new elements
ConstructElements(&m_pData[m_nSize], nNewSize-m_nSize);
}
else if (m_nSize > nNewSize) // destroy the old elements
DestructElements(&m_pData[nNewSize], m_nSize-nNewSize);
m_nSize = nNewSize; } else { // otherwise, grow array
int nGrowBy = m_nGrowBy; if (nGrowBy == 0) { // heuristically determine growth when nGrowBy == 0
// (this avoids heap fragmentation in many situations)
nGrowBy = min(1024, max(4, m_nSize / 8)); } int nNewMax; if (nNewSize < m_nMaxSize + nGrowBy) nNewMax = m_nMaxSize + nGrowBy; // granularity
else nNewMax = nNewSize; // no slush
ASSERT(nNewMax >= m_nMaxSize); // no wrap around
#ifdef SIZE_T_MAX
ASSERT(nNewMax <= SIZE_T_MAX/sizeof(CString)); // no overflow
#endif
CString* pNewData = (CString*) new BYTE[nNewMax * sizeof(CString)]; if (pNewData) { // copy new data from old
memcpy(pNewData, m_pData, m_nSize * sizeof(CString));
// construct remaining elements
ASSERT(nNewSize > m_nSize);
ConstructElements(&pNewData[m_nSize], nNewSize-m_nSize);
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData; m_pData = pNewData; m_nSize = nNewSize; m_nMaxSize = nNewMax; } } }
int CStringArray::Append(const CStringArray& src) { // ASSERT_VALID(this);
ASSERT(this != &src); // cannot append to itself
int nOldSize = m_nSize; SetSize(m_nSize + src.m_nSize);
CopyElements(m_pData + nOldSize, src.m_pData, src.m_nSize);
return nOldSize; }
void CStringArray::Copy(const CStringArray& src) { // ASSERT_VALID(this);
ASSERT(this != &src); // cannot append to itself
SetSize(src.m_nSize);
CopyElements(m_pData, src.m_pData, src.m_nSize);
}
void CStringArray::FreeExtra() { // ASSERT_VALID(this);
if (m_nSize != m_nMaxSize) { // shrink to desired size
#ifdef SIZE_T_MAX
ASSERT(m_nSize <= SIZE_T_MAX/sizeof(CString)); // no overflow
#endif
CString* pNewData = NULL; if (m_nSize != 0) { pNewData = (CString*) new BYTE[m_nSize * sizeof(CString)]; if (pNewData) { // copy new data from old
memcpy(pNewData, m_pData, m_nSize * sizeof(CString)); } }
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData; m_pData = pNewData; m_nMaxSize = m_nSize; } }
void CStringArray::SetAtGrow(int nIndex, LPCTSTR newElement) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0);
if (nIndex >= m_nSize) SetSize(nIndex+1); m_pData[nIndex] = newElement; }
void CStringArray::SetAtGrow(int nIndex, const CString& newElement) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0);
if (nIndex >= m_nSize) SetSize(nIndex+1); m_pData[nIndex] = newElement; }
void CStringArray::InsertEmpty(int nIndex, int nCount) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0); // will expand to meet need
ASSERT(nCount > 0); // zero or negative size not allowed
if (nIndex >= m_nSize) { // adding after the end of the array
SetSize(nIndex + nCount); // grow so nIndex is valid
} else { // inserting in the middle of the array
int nOldSize = m_nSize; SetSize(m_nSize + nCount); // grow it to new size
// shift old data up to fill gap
memmove(&m_pData[nIndex+nCount], &m_pData[nIndex], (nOldSize-nIndex) * sizeof(CString));
// re-init slots we copied from
ConstructElements(&m_pData[nIndex], nCount); }
// insert new value in the gap
ASSERT(nIndex + nCount <= m_nSize); }
void CStringArray::InsertAt(int nIndex, LPCTSTR newElement, int nCount) { // make room for new elements
InsertEmpty(nIndex, nCount);
// copy elements into the empty space
CString temp = newElement; while (nCount--) m_pData[nIndex++] = temp; }
void CStringArray::InsertAt(int nIndex, const CString& newElement, int nCount) { // make room for new elements
InsertEmpty(nIndex, nCount);
// copy elements into the empty space
while (nCount--) m_pData[nIndex++] = newElement; }
void CStringArray::RemoveAt(int nIndex, int nCount) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0); ASSERT(nCount >= 0); ASSERT(nIndex + nCount <= m_nSize);
// just remove a range
int nMoveCount = m_nSize - (nIndex + nCount);
DestructElements(&m_pData[nIndex], nCount);
if (nMoveCount) memmove(&m_pData[nIndex], &m_pData[nIndex + nCount], nMoveCount * sizeof(CString)); m_nSize -= nCount; }
void CStringArray::InsertAt(int nStartIndex, CStringArray* pNewArray) { // ASSERT_VALID(this);
ASSERT(pNewArray != NULL); // ASSERT_KINDOF(CStringArray, pNewArray);
// ASSERT_VALID(pNewArray);
ASSERT(nStartIndex >= 0);
if (pNewArray->GetSize() > 0) { InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize()); for (int i = 0; i < pNewArray->GetSize(); i++) SetAt(nStartIndex + i, pNewArray->GetAt(i)); } }
//////////////////////////////////////////////////////////////////////////////
// CPtrArray
/////////////////////////////////////////////////////////////////////////////
CPtrArray::CPtrArray() { m_pData = NULL; m_nSize = m_nMaxSize = m_nGrowBy = 0; }
CPtrArray::~CPtrArray() { // ASSERT_VALID(this);
delete[] (BYTE*)m_pData; }
void CPtrArray::SetSize(int nNewSize, int nGrowBy) { // ASSERT_VALID(this);
ASSERT(nNewSize >= 0);
if (nGrowBy != -1) m_nGrowBy = nGrowBy; // set new size
if (nNewSize == 0) { // shrink to nothing
delete[] (BYTE*)m_pData; m_pData = NULL; m_nSize = m_nMaxSize = 0; } else if (m_pData == NULL) { // create one with exact size
#ifdef SIZE_T_MAX
ASSERT(nNewSize <= SIZE_T_MAX/sizeof(void*)); // no overflow
#endif
m_pData = (void**) new BYTE[nNewSize * sizeof(void*)]; if (m_pData) { memset(m_pData, 0, nNewSize * sizeof(void*)); // zero fill
m_nSize = m_nMaxSize = nNewSize; } } else if (nNewSize <= m_nMaxSize) { // it fits
if (nNewSize > m_nSize) { // initialize the new elements
memset(&m_pData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(void*));
}
m_nSize = nNewSize; } else { // otherwise, grow array
int nGrowBy = m_nGrowBy; if (nGrowBy == 0) { // heuristically determine growth when nGrowBy == 0
// (this avoids heap fragmentation in many situations)
nGrowBy = min(1024, max(4, m_nSize / 8)); } int nNewMax; if (nNewSize < m_nMaxSize + nGrowBy) nNewMax = m_nMaxSize + nGrowBy; // granularity
else nNewMax = nNewSize; // no slush
ASSERT(nNewMax >= m_nMaxSize); // no wrap around
#ifdef SIZE_T_MAX
ASSERT(nNewMax <= SIZE_T_MAX/sizeof(void*)); // no overflow
#endif
void** pNewData = (void**) new BYTE[nNewMax * sizeof(void*)]; if (pNewData) { // copy new data from old
memcpy(pNewData, m_pData, m_nSize * sizeof(void*));
// construct remaining elements
ASSERT(nNewSize > m_nSize);
memset(&pNewData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(void*));
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData; m_pData = pNewData; m_nSize = nNewSize; m_nMaxSize = nNewMax; } } }
int CPtrArray::Append(const CPtrArray& src) { // ASSERT_VALID(this);
ASSERT(this != &src); // cannot append to itself
int nOldSize = m_nSize; SetSize(m_nSize + src.m_nSize);
memcpy(m_pData + nOldSize, src.m_pData, src.m_nSize * sizeof(void*));
return nOldSize; }
void CPtrArray::Copy(const CPtrArray& src) { // ASSERT_VALID(this);
ASSERT(this != &src); // cannot append to itself
SetSize(src.m_nSize);
memcpy(m_pData, src.m_pData, src.m_nSize * sizeof(void*));
}
void CPtrArray::FreeExtra() { // ASSERT_VALID(this);
if (m_nSize != m_nMaxSize) { // shrink to desired size
#ifdef SIZE_T_MAX
ASSERT(m_nSize <= SIZE_T_MAX/sizeof(void*)); // no overflow
#endif
void** pNewData = NULL; if (m_nSize != 0) { pNewData = (void**) new BYTE[m_nSize * sizeof(void*)]; if (pNewData) { // copy new data from old
memcpy(pNewData, m_pData, m_nSize * sizeof(void*)); } }
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData; m_pData = pNewData; m_nMaxSize = m_nSize; } }
/////////////////////////////////////////////////////////////////////////////
void CPtrArray::SetAtGrow(int nIndex, void* newElement) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0);
if (nIndex >= m_nSize) SetSize(nIndex+1); m_pData[nIndex] = newElement; }
void CPtrArray::InsertAt(int nIndex, void* newElement, int nCount) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0); // will expand to meet need
ASSERT(nCount > 0); // zero or negative size not allowed
if (nIndex >= m_nSize) { // adding after the end of the array
SetSize(nIndex + nCount); // grow so nIndex is valid
} else { // inserting in the middle of the array
int nOldSize = m_nSize; SetSize(m_nSize + nCount); // grow it to new size
// shift old data up to fill gap
memmove(&m_pData[nIndex+nCount], &m_pData[nIndex], (nOldSize-nIndex) * sizeof(void*));
// re-init slots we copied from
memset(&m_pData[nIndex], 0, nCount * sizeof(void*)); }
// insert new value in the gap
ASSERT(nIndex + nCount <= m_nSize);
// copy elements into the empty space
while (nCount--) m_pData[nIndex++] = newElement; }
void CPtrArray::RemoveAt(int nIndex, int nCount) { // ASSERT_VALID(this);
ASSERT(nIndex >= 0); ASSERT(nCount >= 0); ASSERT(nIndex + nCount <= m_nSize);
// just remove a range
int nMoveCount = m_nSize - (nIndex + nCount);
if (nMoveCount) memmove(&m_pData[nIndex], &m_pData[nIndex + nCount], nMoveCount * sizeof(void*)); m_nSize -= nCount; }
void CPtrArray::InsertAt(int nStartIndex, CPtrArray* pNewArray) { // ASSERT_VALID(this);
ASSERT(pNewArray != NULL); // ASSERT_KINDOF(CPtrArray, pNewArray);
// ASSERT_VALID(pNewArray);
ASSERT(nStartIndex >= 0);
if (pNewArray->GetSize() > 0) { InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize()); for (int i = 0; i < pNewArray->GetSize(); i++) SetAt(nStartIndex + i, pNewArray->GetAt(i)); } }
/////////////////////////////////////////////////////////////////////////////
|