// // 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); }