|
|
/*
* @doc INTERNAL * * @module - FORMAT.C * CCharFormatArray and CParaFormatArray classes | * * Authors: * Original RichEdit code: David R. Fulmer * Christian Fortini * Murray Sargent * * Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved. */
#include "_common.h"
#include "_format.h"
ASSERTDATA
// =============================== CFixArrayBase =================================
CFixArrayBase::CFixArrayBase( LONG cbElem) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::CFixArrayBase"); _prgel = NULL; _cel = 0; _ielFirstFree = 0; _cbElem = cbElem + sizeof(LONG_PTR); // Use LONG_PTR to get proper alignment on Win64
}
/*
* CFixArrayBase::Add() * * @mfunc * Return index of new element, reallocing if necessary * * @rdesc * Index of new element. * * @comm * Free elements are maintained in place as a linked list indexed * by a chain of ref-count entries with their sign bits set and the * rest of the entry giving the index of the next element on the * free list. The list is terminated by a 0 entry. This approach * enables element 0 to be on the free list. */ LONG CFixArrayBase::Add() { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Add");
char *pel; LONG iel, ielRet;
if(_ielFirstFree) // Return first element of free list
{ ielRet = _ielFirstFree & ~FLBIT; _ielFirstFree = RefCount(ielRet); } else // All lower positions taken: need
{ // to add another celGrow elements
pel = (char*)PvReAlloc(_prgel, (_cel + celGrow) * _cbElem); if(!pel) return -1;
// Clear out the *end* of the newly allocated memory
ZeroMemory(pel + _cel*_cbElem, celGrow*_cbElem);
_prgel = pel;
ielRet = _cel; // Return first one added
iel = _cel + 1; _cel += celGrow;
// Add elements _cel+1 thru _cel+celGrow-1 to free list. The last
// of these retains a 0, stored by fZeroFill in Alloc
_ielFirstFree = iel | FLBIT;
for(pel = (char *)&RefCount(iel); ++iel < _cel; pel += _cbElem) { *(INT *)pel = iel | FLBIT; } } return ielRet; }
void CFixArrayBase::Free( LONG iel) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Free(iel)");
// Simply add it to free list
RefCount(iel) = _ielFirstFree; _ielFirstFree = iel | FLBIT; }
void CFixArrayBase::Free() { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Free()");
#ifdef DEBUG
// Only do this validation if all the ped's are gone. Visual basic shutsdown apps
// without freeing all the resources so this safety check is necessary.
if (0 == W32->GetRefs()) { // Display MessageBox if any CCharFormats, CParaFormats, or CTabs have
// reference counts > 0. This only happens if an error has occurred.
BOOL fComplained = FALSE; for(LONG iel = 0; iel < Count(); iel++) { while(RefCount(iel) > 0) { if (!fComplained) { fComplained = TRUE; AssertSz(FALSE, _cbElem == sizeof(CCharFormat) + sizeof(LONG_PTR) ? "CCharFormat not free" : _cbElem == sizeof(CParaFormat) + sizeof(LONG_PTR) ? "CParaFormat not free" : "CTabs not free"); }
Release(iel); } } } #endif
FreePv(_prgel); _prgel = NULL; _cel = 0; _ielFirstFree = 0; }
HRESULT CFixArrayBase::Deref( LONG iel, const void **ppel) const { Assert(ppel); AssertSz(iel >= 0, "CFixArrayBase::Deref: bad element index" ); AssertSz(*(LONG *)(_prgel + (iel + 1) * _cbElem - 4) > 0, "CFixArrayBase::Deref: element index has bad ref count");
if(!ppel) return E_INVALIDARG;
*ppel = Elem(iel); return S_OK; }
/*
* CFixArrayBase::RefCount(iel) * * @mfunc * The reference count for an element is stored as a LONG immediately * following the element in the CFixArray. If the element isn't used * i.e., is free, then the reference count is used as a link to the * next free element. The last free element in this list has a 0 * "reference count", which terminates the list. * * The ref count follows the element instead of preceding it because * this allows Elem(iel) to avoid an extra addition. Elem() is used * widely in the code. * * @rdesc * Ptr to reference count */ LONG & CFixArrayBase::RefCount( LONG iel) { Assert(iel < Count()); return (LONG &)(*(_prgel + (iel + 1) * _cbElem - 4)); }
LONG CFixArrayBase::Release( LONG iel) { LONG cRef = -1;
if(iel >= 0) // Ignore default iel
{ CLock lock; CheckFreeChain(); AssertSz(RefCount(iel) > 0, "CFixArrayBase::Release(): already free");
cRef = --RefCount(iel); if(!cRef) // Entry no longer referenced
Free(iel); // Add it to the free chain
} return cRef; }
LONG CFixArrayBase::AddRef( LONG iel) { LONG cRef = -1;
if(iel >= 0) { CLock lock; CheckFreeChain(); AssertSz(RefCount(iel) > 0, "CFixArrayBase::AddRef(): add ref to free elem"); cRef = ++RefCount(iel); } return cRef; }
LONG CFixArrayBase::Find( const void *pel) { CheckFreeChain(); for(LONG iel = 0; iel < Count(); iel++) { // RefCount < 0 means entry not in use and is index of next free entry.
// RefCount = 0 marks last free element in list. _cbElem = sizeof(ELEM)
// plus sizeof(RefCount), which is a LONG.
if (RefCount(iel) > 0 && !CompareMemory(Elem(iel), pel, _cbElem - sizeof(LONG_PTR))) { return iel; } } return -1; }
HRESULT CFixArrayBase::Cache( const void *pel, LONG * piel) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Cache");
CLock lock; LONG iel = Find(pel);
if(iel >= 0) RefCount(iel)++; else { iel = Add(); if(iel < 0) return E_OUTOFMEMORY; CopyMemory(Elem(iel), pel, _cbElem - sizeof(LONG_PTR)); RefCount(iel) = 1; }
CheckFreeChain(); if(piel) *piel = iel; return S_OK; }
#ifdef DEBUG
void CFixArrayBase::CheckFreeChainFn( LPSTR szFile, INT nLine) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::CheckFreeChainFn");
LONG cel = 0; LONG iel = _ielFirstFree; LONG ielT;
while(iel) { Assert(iel < 0); ielT = RefCount(iel & ~FLBIT);
if((LONG)(ielT & ~FLBIT) > _cel) Tracef(TRCSEVERR, "AttCheckFreeChainCF(): elem %ld points to out of range elem %ld", iel, ielT);
iel = ielT; if(++cel > _cel) { AssertSzFn("CFixArrayBase::CheckFreeChain() - CF free chain seems to contain an infinite loop", szFile, nLine); return; } } }
#endif
// =========================== CCharFormatArray ===========================================
HRESULT CCharFormatArray::Deref( LONG iCF, const CCharFormat **ppCF) const { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Deref");
return CFixArrayBase::Deref(iCF, (const void **)ppCF); }
LONG CCharFormatArray::Release( LONG iCF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::ReleaseFormat");
return CFixArrayBase::Release(iCF); }
LONG CCharFormatArray::AddRef( LONG iCF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::AddRefFormat");
return CFixArrayBase::AddRef(iCF); }
void CCharFormatArray::Destroy() { delete this; }
LONG CCharFormatArray::Find( const CCharFormat *pCF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Find");
LONG iCF;
#define QUICKCRCSEARCHSIZE 15 // Must be 2^n - 1 for quick MOD
// operation, it is a simple hash.
static struct { BYTE bCRC; LONG iCF; } quickCrcSearch[QUICKCRCSEARCHSIZE+1]; BYTE bCRC; WORD hashKey;
CheckFreeChain();
// Check our cache before going sequential
bCRC = (BYTE)pCF->_iFont; hashKey = (WORD)(bCRC & QUICKCRCSEARCHSIZE); if(bCRC == quickCrcSearch[hashKey].bCRC) { iCF = quickCrcSearch[hashKey].iCF - 1; if (iCF >= 0 && iCF < Count() && RefCount(iCF) > 0 && !CompareMemory(Elem(iCF), pCF, sizeof(CCharFormat))) { return iCF; } }
for(iCF = 0; iCF < Count(); iCF++) { if(RefCount(iCF) > 0 && !CompareMemory(Elem(iCF), pCF, sizeof(CCharFormat))) { quickCrcSearch[hashKey].bCRC = bCRC; quickCrcSearch[hashKey].iCF = iCF + 1; return iCF; } } return -1; }
HRESULT CCharFormatArray::Cache( const CCharFormat *pCF, LONG* piCF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Cache");
CLock lock; LONG iCF = Find(pCF);
if(iCF >= 0) RefCount(iCF)++; else { iCF = Add(); if(iCF < 0) return E_OUTOFMEMORY; *Elem(iCF) = *pCF; // Set entry iCF to *pCF
RefCount(iCF) = 1; }
CheckFreeChain(); if(piCF) *piCF = iCF;
return S_OK; }
// =============================== CParaFormatArray ===========================================
HRESULT CParaFormatArray::Deref( LONG iPF, const CParaFormat **ppPF) const { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Deref");
return CFixArrayBase::Deref(iPF, (const void **)ppPF); }
LONG CParaFormatArray::Release( LONG iPF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::ReleaseFormat");
CLock lock; LONG cRef = CFixArrayBase::Release(iPF);
#ifdef TABS
if(!cRef) GetTabsCache()->Release(Elem(iPF)->_iTabs); #endif
return cRef; }
LONG CParaFormatArray::AddRef( LONG iPF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::AddRefFormat");
return CFixArrayBase::AddRef(iPF); }
void CParaFormatArray::Destroy() { delete this; }
HRESULT CParaFormatArray::Cache( const CParaFormat *pPF, LONG *piPF) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Cache");
HRESULT hr = CFixArrayBase::Cache((const void *)pPF, piPF); #ifdef TABS
if(hr == NOERROR && RefCount(*piPF) == 1) GetTabsCache()->AddRef(pPF->_iTabs); #endif
return hr; }
// =============================== CTabsArray ===========================================
CTabsArray::~CTabsArray() { for(LONG iTabs = 0; iTabs < Count(); iTabs++) { // It shouldn't be necessary to release any tabs, since when all
// controls are gone, no reference counts should be > 0.
while(RefCount(iTabs) > 0) { #ifdef DEBUG
// Only do this validation if all the ped's are gone. Visual basic shutsdown apps
// without freeing all the resources so this safety check is necessary.
AssertSz(0 != W32->GetRefs(), "CTabs not free"); #endif
Release(iTabs); } } }
const LONG *CTabsArray::Deref( LONG iTabs) const { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Deref");
return iTabs >= 0 ? Elem(iTabs)->_prgxTabs : NULL; }
LONG CTabsArray::Release( LONG iTabs) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Release");
LONG cRef = CFixArrayBase::Release(iTabs); if(!cRef) FreePv(Elem(iTabs)->_prgxTabs); return cRef; }
LONG CTabsArray::AddRef( LONG iTabs) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::AddRef");
return CFixArrayBase::AddRef(iTabs); }
LONG CTabsArray::Find( const LONG *prgxTabs, LONG cTab) { CheckFreeChain();
CTabs *pTab; for(LONG iel = 0; iel < Count(); iel++) { // RefCount < 0 means entry not in use and is index of next free entry.
// RefCount = 0 marks last free element in list. _cbElem = sizeof(ELEM)
// plus sizeof(RefCount), which is a LONG.
if(RefCount(iel) > 0) { pTab = Elem(iel); if (pTab->_cTab == cTab && !CompareMemory(pTab->_prgxTabs, prgxTabs, cTab*sizeof(LONG))) { return iel; } } } return -1; }
LONG CTabsArray::Cache( const LONG *prgxTabs, LONG cTab) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Cache");
if(!cTab) return -1; // No tabs defined: use default
CLock lock; LONG iTabs = Find(prgxTabs, cTab);
if(iTabs >= 0) RefCount(iTabs)++; else { iTabs = Add(); if(iTabs < 0) // Out of memory: use default
return -1;
CTabs *pTabs = Elem(iTabs); LONG cb = sizeof(LONG)*cTab;
pTabs->_prgxTabs = (LONG *)PvAlloc(cb, GMEM_FIXED); if(!pTabs->_prgxTabs) return -1; // Out of memory: use default
CopyMemory(pTabs->_prgxTabs, prgxTabs, cb); pTabs->_cTab = cTab; RefCount(iTabs) = 1; } return iTabs; }
// ================================== Factories ===========================================
static ICharFormatCache *pCFCache = NULL; // CCharFormat cache
static IParaFormatCache *pPFCache = NULL; // CParaFormat cache
static CTabsArray * pTabsCache = NULL; // CTabs cache
ICharFormatCache *GetCharFormatCache() { return pCFCache; }
IParaFormatCache *GetParaFormatCache() { return pPFCache; }
CTabsArray *GetTabsCache() { return pTabsCache; }
HRESULT CreateFormatCaches() // Called by DllMain()
{ TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CreateFormatCaches"); CLock lock;
pCFCache = new CCharFormatArray(); if(!pCFCache) return E_OUTOFMEMORY; pPFCache = new CParaFormatArray(); if(!pPFCache) { delete pCFCache; return E_OUTOFMEMORY; }
pTabsCache = new CTabsArray(); if(!pTabsCache) { delete pCFCache; delete pPFCache; return E_OUTOFMEMORY; } return S_OK; }
HRESULT DestroyFormatCaches() // Called by DllMain()
{ TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "DeleteFormatCaches");
if (pCFCache) pCFCache->Destroy(); if (pPFCache) pPFCache->Destroy(); if (pTabsCache) delete pTabsCache; return NOERROR; }
/*
* ReleaseFormats(iCF, iPF) * * @mfunc * Release char and para formats corresponding to the indices <p iCF> * and <p iPF>, respectively */ void ReleaseFormats ( LONG iCF, //@parm CCharFormat index for releasing
LONG iPF) //@parm CParaFormat index for releasing
{ AssertSz(pCFCache && pPFCache, "ReleaseFormats: uninitialized format caches"); if (iCF != -1) pCFCache->Release(iCF); if (iPF != -1) pPFCache->Release(iPF); }
|