/****************************************************************************** Copyright (c) 1999-2000 Microsoft Corporation Module Name: Cache.cpp Abstract: Handles caching of database lookups. Revision History: ******************************************************************************/ #include "stdafx.h" //////////////////////////////////////////////////////////////////////////////// static const DWORD l_dwVersion = 0x01314351; // QC1 01 static const DWORD l_dwSizeThresholdHIGH = 2048*1024; static const DWORD l_dwSizeThresholdLOW = 1024*1024; static const DATE l_dSaveThreshold = (60.0/86400.0); // 60 seconds. static const WCHAR l_szBase [] = HC_ROOT_HELPSVC_CONFIG L"\\Cache"; static const WCHAR l_szIndex [] = L"Directory.bin"; static const WCHAR l_szQuery [] = L"Query_%08x.bin"; static const WCHAR l_szBackup[] = L".bak"; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// HRESULT Taxonomy::operator>>( /*[in]*/ MPC::Serializer& stream, /*[out]*/ Taxonomy::Cache::NodeEntry& val ) { __HCP_FUNC_ENTRY( "Taxonomy::operator>> Taxonomy::Cache::NodeEntry" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_rs_data ); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::operator<<( /*[in]*/ MPC::Serializer& stream, /*[in] */ const Taxonomy::Cache::NodeEntry& val ) { __HCP_FUNC_ENTRY( "Taxonomy::operator<< Taxonomy::Cache::NodeEntry" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_rs_data ); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } Taxonomy::Cache::NodeEntry::NodeEntry() { m_rs_data.m_ID_parent = -1; m_rs_data.m_ID_node = -1; } bool Taxonomy::Cache::NodeEntry::operator<( /*[in]*/ NodeEntry const &en ) const { long lCmp = (m_rs_data.m_ID_parent - en.m_rs_data.m_ID_parent); if(lCmp == 0) { lCmp = MPC::StrICmp( m_rs_data.m_strEntry, en.m_rs_data.m_strEntry ); } return (lCmp < 0); } bool Taxonomy::Cache::NodeEntry::operator==( /*[in]*/ long ID ) const { return m_rs_data.m_ID_node == ID; } Taxonomy::Cache::NodeEntry::MatchNode::MatchNode( /*[in]*/ long ID ) { m_ID = ID; // long m_ID; } bool Taxonomy::Cache::NodeEntry::MatchNode::operator()( /*[in]*/ Taxonomy::Cache::NodeEntry const &en ) const { return en == m_ID; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// HRESULT Taxonomy::operator>>( /*[in]*/ MPC::Serializer& stream, /*[out]*/ Taxonomy::Cache::QueryEntry& val ) { __HCP_FUNC_ENTRY( "Taxonomy::operator>> Taxonomy::Cache::QueryEntry" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_strID ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_iType ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_iSequence ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_fNull ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_dwSize ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream >> val.m_dLastUsed ); val.m_fRemoved = false; hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::operator<<( /*[in]*/ MPC::Serializer& stream, /*[in] */ const Taxonomy::Cache::QueryEntry& val ) { __HCP_FUNC_ENTRY( "Taxonomy::operator<< Taxonomy::Cache::QueryEntry" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_strID ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_iType ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_iSequence ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_fNull ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_dwSize ); __MPC_EXIT_IF_METHOD_FAILS(hr, stream << val.m_dLastUsed ); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } Taxonomy::Cache::QueryEntry::QueryEntry() { // MPC::wstring m_strID; m_iType = OfflineCache::ET_INVALID; // int m_iType; m_iSequence = 0; // int m_iSequence; m_fNull = true; // bool m_fNull; // m_dwSize = 0; // DWORD m_dwSize; m_dLastUsed = 0; // DATE m_dLastUsed; m_fRemoved = true; // bool m_fRemoved; } bool Taxonomy::Cache::QueryEntry::operator<( /*[in]*/ QueryEntry const &en ) const { int iCmp = MPC::StrCmp( m_strID, en.m_strID ); if(iCmp == 0) { iCmp = (m_iType - en.m_iType); } return (iCmp < 0); } //////////////////////////////////////// void Taxonomy::Cache::QueryEntry::Touch() { m_dLastUsed = MPC::GetLocalTime(); m_fRemoved = false; } HRESULT Taxonomy::Cache::QueryEntry::GetFile( /*[out]*/ MPC::wstring& strFile ) { WCHAR rgTmp[64]; swprintf( rgTmp, l_szQuery, m_iSequence ); strFile = rgTmp; return S_OK; } //////////////////////////////////////// HRESULT Taxonomy::Cache::QueryEntry::Store( /*[in]*/ MPC::StorageObject& disk , /*[in]*/ const CPCHQueryResultCollection* pColl ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::QueryEntry::Store" ); HRESULT hr; MPC::StorageObject* child; MPC::wstring strFile; __MPC_EXIT_IF_METHOD_FAILS(hr, GetFile( strFile )); m_fNull = (pColl->Size() == 0); __MPC_EXIT_IF_METHOD_FAILS(hr, disk.GetChild( strFile.c_str(), child, STGM_READWRITE, (m_fNull == false) ? STGTY_STREAM : 0 )); if(m_fNull == false) { if(child) { CComPtr stream; __MPC_EXIT_IF_METHOD_FAILS(hr, child->GetStream( stream )); if(stream) { STATSTG statstg; __MPC_EXIT_IF_METHOD_FAILS(hr, pColl->SaveToCache( stream )); __MPC_EXIT_IF_METHOD_FAILS(hr, stream->Stat( &statstg, STATFLAG_NONAME )); m_dwSize = statstg.cbSize.LowPart; } } } else { if(child) { __MPC_EXIT_IF_METHOD_FAILS(hr, child->Delete()); } } Touch(); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::QueryEntry::Retrieve( /*[in]*/ MPC::StorageObject& disk , /*[in]*/ CPCHQueryResultCollection* pColl ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::QueryEntry::Retrieve" ); HRESULT hr; if(m_fNull == false) { MPC::StorageObject* child; MPC::wstring strFile; __MPC_EXIT_IF_METHOD_FAILS(hr, GetFile( strFile )); __MPC_EXIT_IF_METHOD_FAILS(hr, disk.GetChild( strFile.c_str(), child, STGM_READWRITE, 0 )); if(child) { CComPtr stream; __MPC_EXIT_IF_METHOD_FAILS(hr, child->GetStream( stream )); if(stream) { __MPC_EXIT_IF_METHOD_FAILS(hr, pColl->LoadFromCache( stream )); } } } Touch(); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::QueryEntry::Release( /*[in]*/ MPC::StorageObject& disk ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::QueryEntry::Release" ); HRESULT hr; if(m_fNull == false) { MPC::StorageObject* child; MPC::wstring strFile; __MPC_EXIT_IF_METHOD_FAILS(hr, GetFile( strFile )); __MPC_EXIT_IF_METHOD_FAILS(hr, disk.GetChild( strFile.c_str(), child, STGM_READWRITE, 0 )); if(child) { __MPC_EXIT_IF_METHOD_FAILS(hr, child->Delete()); } } m_fRemoved = true; hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// bool Taxonomy::Cache::SortEntries::operator()( /*[in]*/ QueryEntry* const &left, /*[in]*/ QueryEntry* const &right ) const { return (left->m_dLastUsed < right->m_dLastUsed); } //////////////////////////////////////////////////////////////////////////////// // // ITSS.DLL is broken under IA64.... // #ifdef _IA64_ #define CACHEDHELPSET_STORAGETOUSE false #else #define CACHEDHELPSET_STORAGETOUSE true #endif Taxonomy::Cache::CachedHelpSet::CachedHelpSet() : m_disk( STGM_READWRITE, /*fITSS*/CACHEDHELPSET_STORAGETOUSE ) { Init(); } Taxonomy::Cache::CachedHelpSet::~CachedHelpSet() { if(m_fDirty) { (void)EnsureInSync( true ); } // // Copy working file as the backup. // if(m_fLoaded) { MPC::wstring strFileBack = m_strFile; strFileBack += l_szBackup; if(SUCCEEDED(m_disk.Compact())) { (void)MPC::MoveFile( m_strFile, strFileBack ); } } } Taxonomy::Cache::CachedHelpSet::CachedHelpSet( /*[in]*/ const CachedHelpSet& chs ) : m_disk( STGM_READWRITE, /*fITSS*/CACHEDHELPSET_STORAGETOUSE ) { Init(); m_ths = chs.m_ths; // Taxonomy::HelpSet m_ths; m_strFile = chs.m_strFile; // MPC::wstring m_strFile; // MPC::StorageObject m_disk; // // bool m_fLoaded; // bool m_fDirty; // bool m_fMarkedForLoad; // DATE m_dLastSaved; // long m_lTopNode; // NodeEntrySet m_setNodes; // QueryEntrySet m_setQueries; // int m_iLastSequence; } Taxonomy::Cache::CachedHelpSet& Taxonomy::Cache::CachedHelpSet::operator=( /*[in]*/ const CachedHelpSet& chs ) { Clean(); m_ths = chs.m_ths; // Taxonomy::HelpSet m_ths; m_strFile = chs.m_strFile; // MPC::wstring m_strFile; // MPC::StorageObject m_disk; // // bool m_fLoaded; // bool m_fDirty; // bool m_fMarkedForLoad; // DATE m_dLastSaved; // long m_lTopNode; // NodeEntrySet m_setNodes; // QueryEntrySet m_setQueries; // int m_iLastSequence; return *this; } bool Taxonomy::Cache::CachedHelpSet::operator<( /*[in]*/ CachedHelpSet const &hs ) const { return m_ths < hs.m_ths; } //////////////////////////////////////////////////////////////////////////////// void Taxonomy::Cache::CachedHelpSet::Init() { // Taxonomy::HelpSet m_ths; // MPC::wstring m_strFile; // MPC::StorageObject m_disk; // m_fLoaded = false; // bool m_fLoaded; m_fDirty = false; // bool m_fDirty; m_fMarkedForLoad = false; // bool m_fMarkedForLoad; m_dLastSaved = 0; // DATE m_dLastSaved; m_lTopNode = -1; // long m_lTopNode; // NodeEntrySet m_setNodes; // QueryEntrySet m_setQueries; m_iLastSequence = 1; // int m_iLastSequence; } void Taxonomy::Cache::CachedHelpSet::Clean() { // Taxonomy::HelpSet m_ths; // MPC::wstring m_strFile; m_disk.Release(); // MPC::StorageObject m_disk; // m_fLoaded = false; // bool m_fLoaded; m_fDirty = false; // bool m_fDirty; // DATE m_dLastSaved; m_lTopNode = -1; // long m_lTopNode; m_setNodes .clear(); // NodeEntrySet m_setNodes; m_setQueries .clear(); // QueryEntrySet m_setQueries; m_iLastSequence = 1; // int m_iLastSequence; } HRESULT Taxonomy::Cache::CachedHelpSet::Load() { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::Load" ); HRESULT hr; MPC::StorageObject* child; Clean(); DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "Loading Taxonomy Cache" ); // // Copy the backup on top of the working file. // { MPC::wstring strFileBack = m_strFile; strFileBack += l_szBackup; (void)MPC::DeleteFile( m_strFile ); (void)MPC::CopyFile ( strFileBack, m_strFile ); } if(FAILED(m_disk.Exists())) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_disk.Create()); } __MPC_EXIT_IF_METHOD_FAILS(hr, m_disk.GetChild( l_szIndex, child, STGM_READWRITE, 0 )); if(child) { CComPtr stream; __MPC_EXIT_IF_METHOD_FAILS(hr, child->GetStream( stream )); if(stream) { MPC::Serializer_IStream streamReal( stream ); MPC::Serializer_Buffering streamBuf ( streamReal ); DWORD dwVer; __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf >> dwVer ); if(dwVer != l_dwVersion) __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf >> m_iLastSequence); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf >> m_lTopNode ); DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "Loading Taxonomy Cache : nodes" ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf >> m_setNodes ); DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "Loading Taxonomy Cache : queries" ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf >> m_setQueries ); } } DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "Loaded Taxonomy Cache" ); hr = S_OK; __HCP_FUNC_CLEANUP; if(FAILED(hr)) Clean(); __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::Save() { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::Save" ); HRESULT hr; MPC::StorageObject* child; if(FAILED(m_disk.Exists())) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_disk.Create()); } __MPC_EXIT_IF_METHOD_FAILS(hr, m_disk.GetChild( l_szIndex, child, STGM_READWRITE, STGTY_STREAM )); if(child) { CComPtr stream; __MPC_EXIT_IF_METHOD_FAILS(hr, child->GetStream( stream )); if(stream) { MPC::Serializer_IStream streamReal( stream ); MPC::Serializer_Buffering streamBuf ( streamReal ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf << l_dwVersion ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf << m_iLastSequence); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf << m_lTopNode ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf << m_setNodes ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf << m_setQueries ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamBuf.Flush()); } } #if 0 //////////////////////////////////////////////////////////////////////////////// // // DEBUG CODE // { USES_CONVERSION; HHK::Writer writer; CHAR rgBuf[1024]; QueryEntryIter it; SortEntries Pr; SortedEntryVec vec; SortedEntryIter it2; strFile += L".debug"; __MPC_EXIT_IF_METHOD_FAILS(hr, writer.Init( strFile.c_str() )); for(it = m_setQueries.begin(); it != m_setQueries.end(); it++) { vec.push_back( &(*it) ); } std::sort( vec.begin(), vec.end(), Pr ); for(it2 = vec.begin(); it2 != vec.end(); it2++) { QueryEntry* en = *it2; sprintf( rgBuf, "%80s: %1d %3d %5d %1d %5.12g\n", W2A( en->m_strID.c_str() ), en->m_iType , en->m_iSequence , (int)en->m_dwSize , (int)en->m_fRemoved , en->m_dLastUsed ); __MPC_EXIT_IF_METHOD_FAILS(hr, writer.OutputLine( rgBuf )); } } // // DEBUG CODE // //////////////////////////////////////////////////////////////////////////////// #endif hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::EnsureInSync( /*[in]*/ bool fForceSave ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::EnsureInSync" ); HRESULT hr; if(m_fLoaded == false) { m_fDirty = false; m_disk = m_strFile.c_str(); __MPC_EXIT_IF_METHOD_FAILS(hr, Load()); m_dLastSaved = MPC::GetSystemTime(); m_fLoaded = true; } if(m_fDirty) { DATE dNow = MPC::GetSystemTime(); if(fForceSave == false) { if(dNow - m_dLastSaved > l_dSaveThreshold) { fForceSave = true; } } if(fForceSave) { __MPC_EXIT_IF_METHOD_FAILS(hr, Save()); m_dLastSaved = dNow; m_fDirty = false; } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT Taxonomy::Cache::CachedHelpSet::GenerateDefaultQueries( /*[in]*/ Taxonomy::Settings& ts , /*[in]*/ Taxonomy::Updater& updater , /*[in]*/ long ID , /*[in]*/ long lLevel ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::GenerateDefaultQueries" ); HRESULT hr; CComPtr coll; MPC::wstring strPath; __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &coll )); __MPC_EXIT_IF_METHOD_FAILS(hr, BuildNodePath( ID, strPath, /*fParent*/false )); // // Build the queries. // __MPC_EXIT_IF_METHOD_FAILS(hr, updater.LookupNode ( strPath.c_str(), coll )); coll->Erase(); __MPC_EXIT_IF_METHOD_FAILS(hr, updater.LookupSubNodes ( strPath.c_str(), /*fVisibleOnly*/true, coll )); coll->Erase(); __MPC_EXIT_IF_METHOD_FAILS(hr, updater.LookupNodesAndTopics( strPath.c_str(), /*fVisibleOnly*/true, coll )); coll->Erase(); __MPC_EXIT_IF_METHOD_FAILS(hr, updater.LookupTopics ( strPath.c_str(), /*fVisibleOnly*/true, coll )); coll->Erase(); // // Recurse for sub-levels. // if(lLevel++ < 3) { MatchSet res; MatchIter it; __MPC_EXIT_IF_METHOD_FAILS(hr, LocateSubNodes( ID, /*fRecurse*/false, /*fOnlyVisible*/true, res )); for(it=res.begin(); it!=res.end(); it++) { __MPC_EXIT_IF_METHOD_FAILS(hr, GenerateDefaultQueries( ts, updater, *it, lLevel )); } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::GenerateDefaultQueries( /*[in]*/ Taxonomy::Settings& ts , /*[in]*/ Taxonomy::Updater& updater ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::GenerateDefaultQueries" ); HRESULT hr; static WCHAR* c_rgNodes[] = { L"", L"_SYSTEM_", L"UNMAPPED" }; for(int i=0; ic_str(), it2 )) break; id = it2->m_rs_data.m_ID_node; } if(it == vec.end()) { __MPC_EXIT_IF_METHOD_FAILS(hr, GenerateDefaultQueries( ts, updater, id, 0 )); } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::PrePopulate( /*[in]*/ Cache* parent ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::PrePopulate" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); // // This opens the database, walks through all the nodes in the taxonomy and makes a copy in the cache. // DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "PrePopulating Taxonomy Cache" ); { Taxonomy::Settings ts( m_ths ); JetBlue::SessionHandle handle; JetBlue::Database* db; Taxonomy::Updater updater; Taxonomy::RS_Taxonomy* rs; bool fFound; __MPC_EXIT_IF_METHOD_FAILS(hr, ts.GetDatabase( handle, db, /*fReadOnly*/true )); __MPC_EXIT_IF_METHOD_FAILS(hr, updater.Init ( ts, db, parent )); __MPC_EXIT_IF_METHOD_FAILS(hr, updater.GetTaxonomy( &rs )); m_lTopNode = -1; m_setNodes.clear(); __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveFirst, &fFound )); while(fFound) { NodeEntry en; en.m_rs_data = *rs; if(!en.m_rs_data.m_fValid__ID_parent) { en.m_rs_data.m_ID_parent = -1; m_lTopNode = en.m_rs_data.m_ID_node; } m_setNodes.insert( en ); __MPC_EXIT_IF_METHOD_FAILS(hr, rs->Move( 0, JET_MoveNext, &fFound )); } m_fDirty = true; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync( true )); DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "PrePopulating Taxonomy Cache : Nodes done" ); // // Walk through the first 3 level of nodes and pre-generate the queries. // // We disable offline flushing of the root index, because it gets changed too often... // __MPC_EXIT_IF_METHOD_FAILS(hr, OfflineCache::Root::s_GLOBAL->DisableSave( )); __MPC_EXIT_IF_METHOD_FAILS(hr, GenerateDefaultQueries ( ts, updater )); __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync ( true )); __MPC_EXIT_IF_METHOD_FAILS(hr, OfflineCache::Root::s_GLOBAL->EnableSave ( )); } DEBUG_AppendPerf( DEBUG_PERF_CACHE_L1, "PrePopulated Taxonomy Cache : Nodes done" ); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::Erase() { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::Erase" ); HRESULT hr; Clean(); { MPC::wstring strFileBack = m_strFile; strFileBack += l_szBackup; (void)MPC::DeleteFile( m_strFile , /*fForce*/true, /*fDelayed*/false ); (void)MPC::DeleteFile( strFileBack, /*fForce*/true, /*fDelayed*/false ); } // // Propagate change to the offline cache. // { OfflineCache::Handle handle; if(SUCCEEDED(OfflineCache::Root::s_GLOBAL->Locate( m_ths, handle ))) { if(SUCCEEDED(handle->RemoveQueries())) { (void)OfflineCache::Root::s_GLOBAL->Flush(); } } } hr = S_OK; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::PrepareToLoad() { m_fMarkedForLoad = true; return S_OK; } HRESULT Taxonomy::Cache::CachedHelpSet::LoadIfMarked() { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::LoadIfMarked" ); HRESULT hr; if(m_fMarkedForLoad) { m_fMarkedForLoad = false; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); // // Load the JetBlue database. // { Taxonomy::Settings ts( m_ths ); JetBlue::SessionHandle handle; JetBlue::Database* db; __MPC_EXIT_IF_METHOD_FAILS(hr, ts.GetDatabase( handle, db, /*fReadOnly*/true )); } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::MRU() { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::MRU" ); HRESULT hr; QueryEntryIter it; DWORD dwSizeTot = 0; // // Get total cache size. // for(it = m_setQueries.begin(); it != m_setQueries.end(); it++) { if(it->m_fRemoved == false) { dwSizeTot += it->m_dwSize; } } // // If the total size of the cache is bigger than a certain value, start to purge from the least used query. // if(dwSizeTot > l_dwSizeThresholdHIGH) { SortEntries Pr; SortedEntryVec vec; SortedEntryIter it2; for(it = m_setQueries.begin(); it != m_setQueries.end(); it++) { if(it->m_fRemoved == false) { vec.push_back( &(*it) ); } } std::sort( vec.begin(), vec.end(), Pr ); for(it2 = vec.begin(); it2 != vec.end(); it2++) { QueryEntry* en = *it2; __MPC_EXIT_IF_METHOD_FAILS(hr, en->Release( m_disk )); m_fDirty = true; dwSizeTot -= en->m_dwSize; if(dwSizeTot < l_dwSizeThresholdLOW) break; } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// bool Taxonomy::Cache::CachedHelpSet::LocateNode( /*[in] */ long ID_parent , /*[in] */ LPCWSTR szEntry , /*[out]*/ NodeEntryIter& it ) { NodeEntry en; en.m_rs_data.m_ID_parent = ID_parent; en.m_rs_data.m_strEntry = SAFEWSTR(szEntry); it = m_setNodes.find( en ); return (it != m_setNodes.end()); } HRESULT Taxonomy::Cache::CachedHelpSet::LocateNode( /*[in] */ long ID_parent , /*[in] */ LPCWSTR szEntry , /*[out]*/ RS_Data_Taxonomy& rs_data ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::LocateNode" ); HRESULT hr; NodeEntryIter it; rs_data.m_ID_node = -1; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); if(LocateNode( ID_parent, szEntry, it ) == false) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } rs_data = it->m_rs_data; hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::LocateSubNodes( /*[in] */ long ID_node , /*[in] */ bool fRecurse , /*[in] */ bool fOnlyVisible , /*[out]*/ MatchSet& res ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::LocateSubNodes" ); HRESULT hr; NodeEntry en; NodeEntryIterConst it; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); en.m_rs_data.m_ID_parent = ID_node; it = m_setNodes.lower_bound( en ); while(it != m_setNodes.end() && it->m_rs_data.m_ID_parent == ID_node) { if(fOnlyVisible == false || it->m_rs_data.m_fVisible) { res.insert( it->m_rs_data.m_ID_node ); if(fRecurse) { __MPC_EXIT_IF_METHOD_FAILS(hr, LocateSubNodes( it->m_rs_data.m_ID_node, /*fRecurse*/true, fOnlyVisible, res )); } } it++; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::LocateNodesFromURL( /*[in] */ LPCWSTR szURL , /*[out]*/ MatchSet& res ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::LocateNodesFromURL" ); HRESULT hr; NodeEntryIterConst it; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); for(it = m_setNodes.begin(); it != m_setNodes.end(); it++) { if(!MPC::StrICmp( szURL, it->m_rs_data.m_strDescriptionURI )) { res.insert( it->m_rs_data.m_ID_node ); } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::CachedHelpSet::BuildNodePath( /*[in] */ long ID , /*[out]*/ MPC::wstring& strPath , /*[in] */ bool fParent ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::BuildNodePath" ); HRESULT hr; NodeEntryIterConst it; MPC::wstring strTmp; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); strTmp .reserve( 1024 ); strPath.reserve( 1024 ); strPath = L""; while(ID != m_lTopNode) { NodeEntry::MatchNode cmp( ID ); it = std::find_if( m_setNodes.begin(), m_setNodes.end(), cmp ); if(it == m_setNodes.end()) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } if(fParent == false) { strTmp = it->m_rs_data.m_strEntry; if(strPath.size()) { strTmp += L"/"; strTmp += strPath; } strPath = strTmp; } else { fParent = true; } ID = it->m_rs_data.m_ID_parent; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT Taxonomy::Cache::CachedHelpSet::LocateQuery( /*[in] */ LPCWSTR szID , /*[in] */ int iType , /*[out]*/ QueryEntry* &pEntry , /*[in] */ bool fCreate ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::CachedHelpSet::LocateQuery" ); HRESULT hr; QueryEntry en; QueryEntryIter it; pEntry = NULL; __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureInSync()); en.m_strID = SAFEWSTR(szID); en.m_iType = iType; it = m_setQueries.find( en ); if(it == m_setQueries.end()) { if(fCreate == false) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } it = m_setQueries.insert( en ).first; it->m_iSequence = m_iLastSequence++; } else { if(fCreate == false) { if(it->m_fRemoved) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } } } pEntry = &(*it); pEntry->m_fRemoved = false; m_fDirty = true; hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Taxonomy::Cache::Cache() { // CacheSet m_skus; (void)MPC::_MPC_Module.RegisterCallback( this, (void (Taxonomy::Cache::*)())Shutdown ); } Taxonomy::Cache::~Cache() { MPC::_MPC_Module.UnregisterCallback( this ); Shutdown(); } //////////////////// Taxonomy::Cache* Taxonomy::Cache::s_GLOBAL( NULL ); HRESULT Taxonomy::Cache::InitializeSystem() { if(s_GLOBAL == NULL) { s_GLOBAL = new Taxonomy::Cache; } return s_GLOBAL ? S_OK : E_OUTOFMEMORY; } void Taxonomy::Cache::FinalizeSystem() { if(s_GLOBAL) { delete s_GLOBAL; s_GLOBAL = NULL; } } //////////////////// void Taxonomy::Cache::Shutdown() { m_skus.clear(); } HRESULT Taxonomy::Cache::Locate( /*[in] */ const Taxonomy::HelpSet& ths , /*[out]*/ CacheIter& it ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::Locate" ); HRESULT hr; CachedHelpSet hs; MPC::SmartLock<_ThreadModel> lock( this ); hs.m_ths = ths; it = m_skus.find( hs ); if(it == m_skus.end()) { WCHAR rgTmp[MAX_PATH]; _snwprintf( rgTmp, MAXSTRLEN(rgTmp), L"%s\\%s_%ld.dat", l_szBase, hs.m_ths.GetSKU(), hs.m_ths.GetLanguage() ); it = m_skus.insert( hs ).first; __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SubstituteEnvVariables( it->m_strFile = rgTmp )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MakeDir ( it->m_strFile )); it->m_fDirty = true; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT Taxonomy::Cache::PrePopulate( /*[in]*/ const Taxonomy::HelpSet& ths ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::PrePopulate" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->PrePopulate( this )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::Erase( /*[in]*/ const Taxonomy::HelpSet& ths ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::Erase" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->Erase()); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::PrepareToLoad( /*[in]*/ const Taxonomy::HelpSet& ths ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::PrepareToLoad" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->PrepareToLoad()); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::LoadIfMarked( /*[in]*/ const Taxonomy::HelpSet& ths ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::LoadIfMarked" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LoadIfMarked()); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT Taxonomy::Cache::LocateNode( /*[in] */ const Taxonomy::HelpSet& ths , /*[in] */ long ID_parent , /*[in] */ LPCWSTR szEntry , /*[out]*/ RS_Data_Taxonomy& rs_data ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::LocateNode" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LocateNode( ID_parent, szEntry, rs_data )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::LocateSubNodes( /*[in] */ const Taxonomy::HelpSet& ths , /*[in] */ long ID_node , /*[in] */ bool fRecurse , /*[in] */ bool fOnlyVisible , /*[out]*/ MatchSet& res ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::LocateSubNodes" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LocateSubNodes( ID_node, fRecurse, fOnlyVisible, res )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::LocateNodesFromURL( /*[in] */ const Taxonomy::HelpSet& ths , /*[in] */ LPCWSTR szURL , /*[out]*/ MatchSet& res ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::LocateNodesFromURL" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LocateNodesFromURL( szURL, res )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::BuildNodePath( /*[in] */ const Taxonomy::HelpSet& ths , /*[in] */ long ID , /*[out]*/ MPC::wstring& strPath , /*[in] */ bool fParent ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::BuildNodePath" ); HRESULT hr; CacheIter it; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->BuildNodePath( ID, strPath, fParent )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT Taxonomy::Cache::StoreQuery( /*[in]*/ const Taxonomy::HelpSet& ths , /*[in]*/ LPCWSTR szID , /*[in]*/ int iType , /*[in]*/ const CPCHQueryResultCollection* pColl ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::StoreQuery" ); HRESULT hr; CacheIter it; QueryEntry* pEntry; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_NOTNULL(pColl); __MPC_PARAMCHECK_END(); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LocateQuery( szID, iType, pEntry, true )); __MPC_EXIT_IF_METHOD_FAILS(hr, pEntry->Store( it->m_disk, pColl )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->MRU ()); __MPC_EXIT_IF_METHOD_FAILS(hr, it->EnsureInSync()); // // Propagate change to the offline cache. // { OfflineCache::Handle handle; if(SUCCEEDED(OfflineCache::Root::s_GLOBAL->Locate( it->m_ths, handle ))) { if(handle->AreYouInterested( szID, iType )) { if(SUCCEEDED(handle->Store( szID, iType, pColl ))) { (void)OfflineCache::Root::s_GLOBAL->Flush(); } } } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT Taxonomy::Cache::RetrieveQuery( /*[in]*/ const Taxonomy::HelpSet& ths , /*[in]*/ LPCWSTR szID , /*[in]*/ int iType , /*[in]*/ CPCHQueryResultCollection* pColl ) { __HCP_FUNC_ENTRY( "Taxonomy::Cache::Retrieve" ); HRESULT hr; CacheIter it; QueryEntry* pEntry; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_NOTNULL(pColl); __MPC_PARAMCHECK_END(); __MPC_EXIT_IF_METHOD_FAILS(hr, Locate( ths, it )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->LocateQuery( szID, iType, pEntry, false )); __MPC_EXIT_IF_METHOD_FAILS(hr, pEntry->Retrieve( it->m_disk, pColl )); __MPC_EXIT_IF_METHOD_FAILS(hr, it->EnsureInSync()); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); }