//+--------------------------------------------------------------------------- // // Microsoft Windows NT Security // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: lru.cpp // // Contents: LRU cache implementation // // History: 24-Dec-97 kirtd Created // //---------------------------------------------------------------------------- #include //+--------------------------------------------------------------------------- // // Member: CLruEntry::CLruEntry, public // // Synopsis: Constructor // //---------------------------------------------------------------------------- CLruEntry::CLruEntry ( IN PCLRUCACHE pCache, IN PCRYPT_DATA_BLOB pIdentifier, IN LPVOID pvData, OUT BOOL& rfResult ) { rfResult = TRUE; m_cRefs = 1; m_pPrevEntry = NULL; m_pNextEntry = NULL; m_Usage = 0; m_pCache = pCache; m_pvData = pvData; m_pBucket = pCache->BucketFromIdentifier( pIdentifier ); if ( pCache->Flags() & LRU_CACHE_NO_COPY_IDENTIFIER ) { m_Identifier = *pIdentifier; } else { m_Identifier.cbData = pIdentifier->cbData; m_Identifier.pbData = new BYTE [ pIdentifier->cbData ]; if ( m_Identifier.pbData != NULL ) { memcpy( m_Identifier.pbData, pIdentifier->pbData, pIdentifier->cbData ); } else { rfResult = FALSE; SetLastError( (DWORD) E_OUTOFMEMORY ); return; } } assert( m_pBucket != NULL ); assert( m_Identifier.pbData != NULL ); } //+--------------------------------------------------------------------------- // // Member: CLruEntry::~CLruEntry, public // // Synopsis: Destructor // //---------------------------------------------------------------------------- CLruEntry::~CLruEntry () { m_pCache->FreeEntryData( m_pvData ); if ( !( m_pCache->Flags() & LRU_CACHE_NO_COPY_IDENTIFIER ) ) { delete m_Identifier.pbData; } } //+--------------------------------------------------------------------------- // // Member: CLruCache::CLruCache, public // // Synopsis: Constructor // //---------------------------------------------------------------------------- CLruCache::CLruCache ( IN PLRU_CACHE_CONFIG pConfig, OUT BOOL& rfResult ) { rfResult = TRUE; m_Config.dwFlags = LRU_CACHE_NO_SERIALIZE; m_cEntries = 0; m_aBucket = new LRU_CACHE_BUCKET [ pConfig->cBuckets ]; if ( m_aBucket == NULL ) { rfResult = FALSE; SetLastError( (DWORD) E_OUTOFMEMORY ); return; } memset( m_aBucket, 0, sizeof( LRU_CACHE_BUCKET ) * pConfig->cBuckets ); if ( !( pConfig->dwFlags & LRU_CACHE_NO_SERIALIZE ) ) { if (! Pki_InitializeCriticalSection( &m_Lock )) { rfResult = FALSE; return; } } m_Config = *pConfig; m_UsageClock = 0; m_cLruDisabled = 0; } //+--------------------------------------------------------------------------- // // Member: CLruCache::~CLruCache, public // // Synopsis: Destructor // //---------------------------------------------------------------------------- CLruCache::~CLruCache () { if ( m_cEntries > 0 ) { PurgeAllEntries( 0, NULL ); } if ( !( m_Config.dwFlags & LRU_CACHE_NO_SERIALIZE ) ) { DeleteCriticalSection( &m_Lock ); } delete m_aBucket; } //+--------------------------------------------------------------------------- // // Member: CLruCache::EnableLruOfEntries, public // // Synopsis: enable LRU of entries and purge anything over the watermark // //---------------------------------------------------------------------------- VOID CLruCache::EnableLruOfEntries (IN OPTIONAL LPVOID pvLruRemovalContext) { LockCache(); assert( m_cLruDisabled > 0 ); if ( m_cLruDisabled == 0 ) { return; } m_cLruDisabled -= 1; if ( m_cLruDisabled == 0 ) { while ( m_cEntries > m_Config.MaxEntries ) { PurgeLeastRecentlyUsed( pvLruRemovalContext ); } } UnlockCache(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::DisableLruOfEntries, public // // Synopsis: disable LRU of entries // //---------------------------------------------------------------------------- VOID CLruCache::DisableLruOfEntries () { LockCache(); m_cLruDisabled += 1; UnlockCache(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::InsertEntry, public // // Synopsis: insert an entry into the cache // //---------------------------------------------------------------------------- VOID CLruCache::InsertEntry ( IN PCLRUENTRY pEntry, IN OPTIONAL LPVOID pvLruRemovalContext ) { assert( pEntry->PrevPointer() == NULL ); assert( pEntry->NextPointer() == NULL ); pEntry->AddRef(); LockCache(); if ( ( m_cEntries == m_Config.MaxEntries ) && ( m_Config.MaxEntries != 0 ) && ( m_cLruDisabled == 0 ) ) { PurgeLeastRecentlyUsed( pvLruRemovalContext ); } assert( ( m_cEntries < m_Config.MaxEntries ) || ( m_Config.MaxEntries == 0 ) || ( m_cLruDisabled > 0 ) ); pEntry->SetNextPointer( pEntry->Bucket()->pList ); if ( pEntry->Bucket()->pList != NULL ) { pEntry->Bucket()->pList->SetPrevPointer( pEntry ); } pEntry->Bucket()->pList = pEntry; m_cEntries += 1; TouchEntryNoLock( pEntry, 0 ); UnlockCache(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::RemoveEntry, public // // Synopsis: remove an entry from the cache // //---------------------------------------------------------------------------- VOID CLruCache::RemoveEntry ( IN PCLRUENTRY pEntry, IN DWORD dwFlags, IN OPTIONAL LPVOID pvRemovalContext ) { LockCache(); RemoveEntryFromBucket( pEntry->Bucket(), pEntry, dwFlags, pvRemovalContext ); UnlockCache(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::TouchEntry, public // // Synopsis: touch the entry // //---------------------------------------------------------------------------- VOID CLruCache::TouchEntry (IN PCLRUENTRY pEntry, IN DWORD dwFlags) { LockCache(); TouchEntryNoLock( pEntry, dwFlags ); UnlockCache(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::FindEntry, public // // Synopsis: find the entry matching the given identifier // //---------------------------------------------------------------------------- PCLRUENTRY CLruCache::FindEntry (IN PCRYPT_DATA_BLOB pIdentifier, IN BOOL fTouchEntry) { PLRU_CACHE_BUCKET pBucket; pBucket = BucketFromIdentifier( pIdentifier ); assert( pBucket != NULL ); return( FindNextMatchingEntryInBucket( pBucket, NULL, pIdentifier, fTouchEntry ) ); } //+--------------------------------------------------------------------------- // // Member: CLruCache::NextMatchingEntry, public // // Synopsis: find the next matching entry to pPrevEntry // //---------------------------------------------------------------------------- PCLRUENTRY CLruCache::NextMatchingEntry (IN PCLRUENTRY pPrevEntry, IN BOOL fTouchEntry) { PCLRUENTRY pNextEntry; pNextEntry = FindNextMatchingEntryInBucket( NULL, pPrevEntry, NULL, fTouchEntry ); pPrevEntry->Release(); return( pNextEntry ); } //+--------------------------------------------------------------------------- // // Member: CLruCache::WalkEntries, public // // Synopsis: walk the entries // //---------------------------------------------------------------------------- VOID CLruCache::WalkEntries (IN PFN_WALK_ENTRIES pfnWalk, IN LPVOID pvParameter) { DWORD cCount; PCLRUENTRY pEntry; PCLRUENTRY pNextEntry; for ( cCount = 0; cCount < m_Config.cBuckets; cCount++ ) { pEntry = m_aBucket[ cCount ].pList; while ( pEntry != NULL ) { pNextEntry = pEntry->NextPointer(); if ( ( *pfnWalk )( pvParameter, pEntry ) == FALSE ) { return; } pEntry = pNextEntry; } } } //+--------------------------------------------------------------------------- // // Member: CLruCache::RemoveEntryFromBucket, public // // Synopsis: remove entry from bucket // //---------------------------------------------------------------------------- VOID CLruCache::RemoveEntryFromBucket ( IN PLRU_CACHE_BUCKET pBucket, IN PCLRUENTRY pEntry, IN DWORD dwFlags, IN OPTIONAL LPVOID pvRemovalContext ) { if ( pEntry->PrevPointer() != NULL ) { pEntry->PrevPointer()->SetNextPointer( pEntry->NextPointer() ); } else { assert( pBucket->pList == pEntry ); pBucket->pList = pEntry->NextPointer(); } if ( pEntry->NextPointer() != NULL ) { pEntry->NextPointer()->SetPrevPointer( pEntry->PrevPointer() ); } pEntry->SetPrevPointer( NULL ); pEntry->SetNextPointer( NULL ); m_cEntries -= 1; if ( ( m_Config.pfnOnRemoval != NULL ) && !( dwFlags & LRU_SUPPRESS_REMOVAL_NOTIFICATION ) ) { ( *m_Config.pfnOnRemoval )( pEntry->Data(), pvRemovalContext ); } pEntry->Release(); } //+--------------------------------------------------------------------------- // // Member: CLruCache::FindNextMatchingEntryInBucket, public // // Synopsis: find the next matching entry in the given bucket. If pCurrent // is non NULL then start from there, the bucket is not needed and // pIdentifier is ignored. If pCurrent is NULL then pIdentifier // and the bucket must both be non NULL // //---------------------------------------------------------------------------- PCLRUENTRY CLruCache::FindNextMatchingEntryInBucket ( IN PLRU_CACHE_BUCKET pBucket, IN PCLRUENTRY pCurrent, IN PCRYPT_DATA_BLOB pIdentifier, IN BOOL fTouchEntry ) { LockCache(); if ( pCurrent == NULL ) { pCurrent = pBucket->pList; } else { pIdentifier = pCurrent->Identifier(); pCurrent = pCurrent->NextPointer(); } while ( pCurrent != NULL ) { if ( ( pIdentifier->cbData == pCurrent->Identifier()->cbData ) && ( memcmp( pIdentifier->pbData, pCurrent->Identifier()->pbData, pIdentifier->cbData ) == 0 ) ) { break; } pCurrent = pCurrent->NextPointer(); } if ( pCurrent != NULL ) { pCurrent->AddRef(); if ( fTouchEntry == TRUE ) { TouchEntryNoLock( pCurrent, 0 ); } } UnlockCache(); return( pCurrent ); } //+--------------------------------------------------------------------------- // // Member: CLruCache::PurgeLeastRecentlyUsed, public // // Synopsis: find and remove the least recently used entry // //---------------------------------------------------------------------------- VOID CLruCache::PurgeLeastRecentlyUsed (IN OPTIONAL LPVOID pvLruRemovalContext) { DWORD cCount; PLRU_CACHE_BUCKET pBucket; PCLRUENTRY pEntry; PCLRUENTRY pLRU; assert( m_cEntries > 0 ); for ( cCount = 0; cCount < m_Config.cBuckets; cCount++ ) { if ( m_aBucket[cCount].pList != NULL ) { break; } } pBucket = &m_aBucket[cCount]; cCount += 1; for ( ; cCount < m_Config.cBuckets; cCount++ ) { if ( ( m_aBucket[cCount].pList != NULL ) && ( m_aBucket[cCount].Usage < pBucket->Usage ) ) { pBucket = &m_aBucket[cCount]; } } assert( pBucket != NULL ); assert( pBucket->pList != NULL ); pLRU = pBucket->pList; pEntry = pLRU->NextPointer(); while ( pEntry != NULL ) { if ( pEntry->Usage() < pLRU->Usage() ) { pLRU = pEntry; } pEntry = pEntry->NextPointer(); } RemoveEntryFromBucket( pBucket, pLRU, 0, pvLruRemovalContext ); } //+--------------------------------------------------------------------------- // // Member: CLruCache::PurgeAllEntries, public // // Synopsis: remove all entries from the cache // //---------------------------------------------------------------------------- VOID CLruCache::PurgeAllEntries ( IN DWORD dwFlags, IN OPTIONAL LPVOID pvRemovalContext ) { DWORD cCount; for ( cCount = 0; cCount < m_Config.cBuckets; cCount++ ) { while ( m_aBucket[cCount].pList != NULL ) { RemoveEntryFromBucket( &m_aBucket[cCount], m_aBucket[cCount].pList, dwFlags, pvRemovalContext ); } } assert( m_cEntries == 0 ); } //+--------------------------------------------------------------------------- // // Function: I_CryptCreateLruCache // // Synopsis: create an LRU cache area // //---------------------------------------------------------------------------- BOOL WINAPI I_CryptCreateLruCache ( IN PLRU_CACHE_CONFIG pConfig, OUT HLRUCACHE* phCache ) { BOOL fResult = FALSE; PCLRUCACHE pCache; pCache = new CLruCache( pConfig, fResult ); if ( pCache == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); } if ( fResult == FALSE ) { delete pCache; return( FALSE ); } *phCache = (HLRUCACHE)pCache; return( TRUE ); } //+--------------------------------------------------------------------------- // // Function: I_CryptFlushLruCache // // Synopsis: flush the cache // //---------------------------------------------------------------------------- VOID WINAPI I_CryptFlushLruCache ( IN HLRUCACHE hCache, IN OPTIONAL DWORD dwFlags, IN OPTIONAL LPVOID pvRemovalContext ) { ( (PCLRUCACHE)hCache )->LockCache(); ( (PCLRUCACHE)hCache )->PurgeAllEntries( dwFlags, pvRemovalContext ); ( (PCLRUCACHE)hCache )->UnlockCache(); } //+--------------------------------------------------------------------------- // // Function: I_CryptFreeLruCache // // Synopsis: free the LRU cache area // //---------------------------------------------------------------------------- VOID WINAPI I_CryptFreeLruCache ( IN HLRUCACHE hCache, IN DWORD dwFlags, IN OPTIONAL LPVOID pvRemovalContext ) { if ( hCache == NULL ) { return; } if ( dwFlags != 0 ) { ( (PCLRUCACHE)hCache )->PurgeAllEntries( dwFlags, pvRemovalContext ); } delete (PCLRUCACHE)hCache; } //+--------------------------------------------------------------------------- // // Function: I_CryptCreateLruEntry // // Synopsis: create an LRU entry // //---------------------------------------------------------------------------- BOOL WINAPI I_CryptCreateLruEntry ( IN HLRUCACHE hCache, IN PCRYPT_DATA_BLOB pIdentifier, IN LPVOID pvData, OUT HLRUENTRY* phEntry ) { BOOL fResult = FALSE; PCLRUENTRY pEntry; pEntry = new CLruEntry( (PCLRUCACHE)hCache, pIdentifier, pvData, fResult ); if ( pEntry == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); } if ( fResult == FALSE ) { delete pEntry; return( FALSE ); } *phEntry = (HLRUENTRY)pEntry; return( TRUE ); } //+--------------------------------------------------------------------------- // // Function: I_CryptGetLruEntryIdentifier // // Synopsis: return the identifier for the entry // //---------------------------------------------------------------------------- PCRYPT_DATA_BLOB WINAPI I_CryptGetLruEntryIdentifier ( IN HLRUENTRY hEntry ) { return( ( (PCLRUENTRY)hEntry )->Identifier() ); } //+--------------------------------------------------------------------------- // // Function: I_CryptGetLruEntryData // // Synopsis: get the data associated with the entry // //---------------------------------------------------------------------------- LPVOID WINAPI I_CryptGetLruEntryData ( IN HLRUENTRY hEntry ) { return( ( (PCLRUENTRY)hEntry )->Data() ); } //+--------------------------------------------------------------------------- // // Function: I_CryptAddRefLruEntry // // Synopsis: add a reference to the entry // //---------------------------------------------------------------------------- VOID WINAPI I_CryptAddRefLruEntry ( IN HLRUENTRY hEntry ) { ( (PCLRUENTRY)hEntry )->AddRef(); } //+--------------------------------------------------------------------------- // // Function: I_CryptReleaseLruEntry // // Synopsis: remove a reference from the entry // //---------------------------------------------------------------------------- VOID WINAPI I_CryptReleaseLruEntry ( IN HLRUENTRY hEntry ) { ( (PCLRUENTRY)hEntry )->Release(); } //+--------------------------------------------------------------------------- // // Function: I_CryptInsertLruEntry // // Synopsis: insert the entry into its associated cache // //---------------------------------------------------------------------------- VOID WINAPI I_CryptInsertLruEntry ( IN HLRUENTRY hEntry, IN OPTIONAL LPVOID pvLruRemovalContext ) { PCLRUENTRY pEntry = (PCLRUENTRY)hEntry; pEntry->Cache()->InsertEntry( pEntry, pvLruRemovalContext ); } //+--------------------------------------------------------------------------- // // Function: I_CryptRemoveLruEntry // // Synopsis: remove the entry from its associated cache // //---------------------------------------------------------------------------- VOID WINAPI I_CryptRemoveLruEntry ( IN HLRUENTRY hEntry, IN DWORD dwFlags, IN LPVOID pvLruRemovalContext ) { PCLRUENTRY pEntry = (PCLRUENTRY)hEntry; pEntry->Cache()->RemoveEntry( pEntry, dwFlags, pvLruRemovalContext ); } //+--------------------------------------------------------------------------- // // Function: I_CryptTouchLruEntry // // Synopsis: touch the entry // //---------------------------------------------------------------------------- VOID WINAPI I_CryptTouchLruEntry ( IN HLRUENTRY hEntry, IN DWORD dwFlags ) { PCLRUENTRY pEntry = (PCLRUENTRY)hEntry; pEntry->Cache()->TouchEntry( pEntry, dwFlags ); } //+--------------------------------------------------------------------------- // // Function: I_CryptFindLruEntry // // Synopsis: find the entry with the given identifier // //---------------------------------------------------------------------------- HLRUENTRY WINAPI I_CryptFindLruEntry ( IN HLRUCACHE hCache, IN PCRYPT_DATA_BLOB pIdentifier ) { PCLRUCACHE pCache = (PCLRUCACHE)hCache; return( pCache->FindEntry( pIdentifier, FALSE ) ); } //+--------------------------------------------------------------------------- // // Function: I_CryptFindLruEntryData // // Synopsis: find the entry with the given identifier // //---------------------------------------------------------------------------- LPVOID WINAPI I_CryptFindLruEntryData ( IN HLRUCACHE hCache, IN PCRYPT_DATA_BLOB pIdentifier, OUT HLRUENTRY* phEntry ) { PCLRUCACHE pCache = (PCLRUCACHE)hCache; PCLRUENTRY pEntry; pEntry = pCache->FindEntry( pIdentifier, TRUE ); *phEntry = (HLRUENTRY)pEntry; if ( pEntry != NULL ) { return( pEntry->Data() ); } return( NULL ); } //+--------------------------------------------------------------------------- // // Function: I_CryptEnumMatchingLruEntries // // Synopsis: get the next matching entry // //---------------------------------------------------------------------------- HLRUENTRY WINAPI I_CryptEnumMatchingLruEntries ( IN HLRUENTRY hPrevEntry ) { PCLRUCACHE pCache = ( (PCLRUENTRY)hPrevEntry )->Cache(); PCLRUENTRY pNextEntry; pNextEntry = pCache->NextMatchingEntry( (PCLRUENTRY)hPrevEntry, FALSE ); return( (HLRUENTRY)pNextEntry ); } //+--------------------------------------------------------------------------- // // Function: I_CryptEnableLruOfEntries // // Synopsis: enable LRU of entries // //---------------------------------------------------------------------------- VOID WINAPI I_CryptEnableLruOfEntries ( IN HLRUCACHE hCache, IN OPTIONAL LPVOID pvLruRemovalContext ) { ( (PCLRUCACHE)hCache )->EnableLruOfEntries( pvLruRemovalContext); } //+--------------------------------------------------------------------------- // // Function: I_CryptDisableLruOfEntries // // Synopsis: disable LRU of entries // //---------------------------------------------------------------------------- VOID WINAPI I_CryptDisableLruOfEntries ( IN HLRUCACHE hCache ) { ( (PCLRUCACHE)hCache )->DisableLruOfEntries(); } //+--------------------------------------------------------------------------- // // Function: I_CryptWalkAllLruCacheEntries // // Synopsis: walk the LRU cache entries // //---------------------------------------------------------------------------- VOID WINAPI I_CryptWalkAllLruCacheEntries ( IN HLRUCACHE hCache, IN PFN_WALK_ENTRIES pfnWalk, IN LPVOID pvParameter ) { ( (PCLRUCACHE)hCache )->WalkEntries( pfnWalk, pvParameter ); }