You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
6.8 KiB
222 lines
6.8 KiB
//
|
|
// txtcache.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "txtcache.h"
|
|
|
|
long CProcessTextCache::_lCacheMutex = -1;
|
|
ITextStoreACP *CProcessTextCache::_ptsi = NULL;
|
|
LONG CProcessTextCache::_acpStart;
|
|
LONG CProcessTextCache::_acpEnd;
|
|
WCHAR CProcessTextCache::_achPlain[CACHE_SIZE_TEXT];
|
|
TS_RUNINFO CProcessTextCache::_rgRunInfo[CACHE_SIZE_RUNINFO];
|
|
ULONG CProcessTextCache::_ulRunInfoLen;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetText
|
|
//
|
|
// Wrapper for GetText that uses a cache.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT CProcessTextCache::GetText(ITextStoreACP *ptsi, LONG acpStart, LONG acpEnd,
|
|
WCHAR *pchPlain, ULONG cchPlainReq, ULONG *pcchPlainOut,
|
|
TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoOut,
|
|
LONG *pacpNext)
|
|
{
|
|
#ifdef DEBUG
|
|
// use these guys to verify the cache in debug
|
|
WCHAR *dbg_pchPlain;
|
|
LONG dbg_acpStart = acpStart;
|
|
LONG dbg_acpEnd = acpEnd;
|
|
ULONG dbg_cchPlainReq = cchPlainReq;
|
|
ULONG dbg_cchPlainOut;
|
|
TS_RUNINFO *dbg_prgRunInfo;
|
|
ULONG dbg_ulRunInfoReq = ulRunInfoReq;
|
|
ULONG dbg_ulRunInfoOut;
|
|
LONG dbg_acpNext;
|
|
#endif
|
|
ULONG cch;
|
|
ULONG cchBase;
|
|
LONG acpBase;
|
|
ULONG i;
|
|
ULONG iDst;
|
|
ULONG iOffset;
|
|
int dStartEnd;
|
|
HRESULT hr;
|
|
|
|
// don't block if the mutex is held, just call the real GetText
|
|
if (InterlockedIncrement(&_lCacheMutex) != 0)
|
|
goto RealGetText;
|
|
|
|
// if its a really big request, don't try to use the cache
|
|
// the way we set things up, once we decide to use the cache we only ask
|
|
// for CACHE_SIZE_TEXT chunks of text at a time, no matter what
|
|
// the code would still be correct without this test, but probably slower
|
|
if (acpEnd < 0 && cchPlainReq > CACHE_SIZE_TEXT)
|
|
goto RealGetText;
|
|
|
|
// need to reset the cache?
|
|
if (_ptsi != ptsi || // no cache
|
|
_acpStart > acpStart || _acpEnd <= acpStart) // is any of the text in the cache?
|
|
{
|
|
_ptsi = NULL; // invalidate the cache in case the GetText fails
|
|
_acpStart = max(0, acpStart - CACHE_PRELOAD_COUNT);
|
|
|
|
hr = ptsi->GetText(_acpStart, -1, _achPlain, ARRAYSIZE(_achPlain), &cch,
|
|
_rgRunInfo, ARRAYSIZE(_rgRunInfo), &_ulRunInfoLen, &_acpEnd);
|
|
|
|
if (hr != S_OK)
|
|
goto RealGetText;
|
|
|
|
// we have a good cache
|
|
_ptsi = ptsi;
|
|
}
|
|
|
|
// return something from the cache
|
|
|
|
if (pcchPlainOut != NULL)
|
|
{
|
|
*pcchPlainOut = 0;
|
|
}
|
|
if (pulRunInfoOut != NULL)
|
|
{
|
|
*pulRunInfoOut = 0;
|
|
}
|
|
|
|
// find a start point
|
|
// in the first run?
|
|
acpBase = _acpStart;
|
|
cchBase = 0;
|
|
iDst = 0;
|
|
|
|
for (i=0; i<_ulRunInfoLen; i++)
|
|
{
|
|
if (acpStart == acpEnd)
|
|
break;
|
|
dStartEnd = acpEnd - acpStart;
|
|
|
|
iOffset = acpStart - acpBase;
|
|
acpBase += _rgRunInfo[i].uCount;
|
|
cch = 0;
|
|
|
|
if (iOffset >= _rgRunInfo[i].uCount)
|
|
{
|
|
if (_rgRunInfo[i].type != TS_RT_OPAQUE)
|
|
{
|
|
cchBase += _rgRunInfo[i].uCount;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (ulRunInfoReq > 0)
|
|
{
|
|
cch = _rgRunInfo[i].uCount - iOffset;
|
|
if (dStartEnd > 0 &&
|
|
iOffset + dStartEnd < _rgRunInfo[i].uCount)
|
|
{
|
|
cch = dStartEnd;
|
|
}
|
|
prgRunInfo[iDst].uCount = cch;
|
|
prgRunInfo[iDst].type = _rgRunInfo[i].type;
|
|
(*pulRunInfoOut)++;
|
|
}
|
|
|
|
if (cchPlainReq > 0 &&
|
|
_rgRunInfo[i].type != TS_RT_OPAQUE)
|
|
{
|
|
cch = min(cchPlainReq, _rgRunInfo[i].uCount - iOffset);
|
|
if (dStartEnd > 0 &&
|
|
iOffset + dStartEnd < _rgRunInfo[i].uCount)
|
|
{
|
|
cch = min(cchPlainReq, (ULONG)dStartEnd);
|
|
}
|
|
memcpy(pchPlain+*pcchPlainOut, _achPlain+cchBase+iOffset, sizeof(WCHAR)*cch);
|
|
*pcchPlainOut += cch;
|
|
if (ulRunInfoReq > 0)
|
|
{
|
|
// might have truncated the run based on pchPlain buffer size, so fix it
|
|
prgRunInfo[iDst].uCount = cch;
|
|
}
|
|
cchPlainReq -= cch;
|
|
cchBase += cch + iOffset;
|
|
|
|
if (cchPlainReq == 0)
|
|
{
|
|
ulRunInfoReq = 1; // force a break below
|
|
}
|
|
}
|
|
|
|
if (cch == 0)
|
|
break;
|
|
|
|
acpStart += cch;
|
|
iDst++;
|
|
|
|
if (ulRunInfoReq > 0)
|
|
{
|
|
if (--ulRunInfoReq == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pacpNext = acpStart;
|
|
|
|
InterlockedDecrement(&_lCacheMutex);
|
|
|
|
#ifdef DEBUG
|
|
// verify the cache worked
|
|
if (dbg_acpEnd <= _acpEnd) // this simple check won't work if the GetText was truncated
|
|
{
|
|
dbg_pchPlain = (WCHAR *)cicMemAlloc(sizeof(WCHAR)*dbg_cchPlainReq);
|
|
|
|
if (dbg_pchPlain)
|
|
{
|
|
// there's a bug in word where it will write to dbg_ulRunInfoReq even when dbg_ulRunInfoReq is zero,
|
|
// if it is non-NULL
|
|
dbg_prgRunInfo = dbg_ulRunInfoReq ? (TS_RUNINFO *)cicMemAlloc(sizeof(TS_RUNINFO)*dbg_ulRunInfoReq) : NULL;
|
|
|
|
if (dbg_prgRunInfo || !dbg_ulRunInfoReq)
|
|
{
|
|
hr = ptsi->GetText(dbg_acpStart, dbg_acpEnd, dbg_pchPlain, dbg_cchPlainReq, &dbg_cchPlainOut,
|
|
dbg_prgRunInfo, dbg_ulRunInfoReq, &dbg_ulRunInfoOut, &dbg_acpNext);
|
|
|
|
Assert(hr == S_OK);
|
|
if (dbg_cchPlainReq > 0)
|
|
{
|
|
Assert(dbg_cchPlainOut == *pcchPlainOut);
|
|
Assert(memcmp(dbg_pchPlain, pchPlain, dbg_cchPlainOut*sizeof(WCHAR)) == 0);
|
|
}
|
|
if (dbg_ulRunInfoReq > 0)
|
|
{
|
|
Assert(dbg_ulRunInfoOut == *pulRunInfoOut);
|
|
Assert(memcmp(dbg_prgRunInfo, prgRunInfo, sizeof(TS_RUNINFO)*dbg_ulRunInfoOut) == 0);
|
|
}
|
|
Assert(dbg_acpNext == *pacpNext);
|
|
|
|
cicMemFree(dbg_prgRunInfo);
|
|
}
|
|
else
|
|
{
|
|
// could not allocate mem.
|
|
Assert(0);
|
|
}
|
|
|
|
cicMemFree(dbg_pchPlain);
|
|
}
|
|
else
|
|
{
|
|
// could not allocate mem.
|
|
Assert(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return S_OK;
|
|
|
|
RealGetText:
|
|
InterlockedDecrement(&_lCacheMutex);
|
|
return ptsi->GetText(acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut,
|
|
prgRunInfo, ulRunInfoReq, pulRunInfoOut, pacpNext);
|
|
}
|