sl@0: // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of the License "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // f32\sfat\sl_dir_cache.cpp
sl@0: //
sl@0: //
sl@0: 
sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0: //!!
sl@0: //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
sl@0: //!!
sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0: 
sl@0: 
sl@0: #include "sl_std.h"
sl@0: #include "sl_dir_cache.h"
sl@0: 
sl@0: //======================================================================
sl@0: TDynamicDirCachePage::~TDynamicDirCachePage()
sl@0:     {
sl@0:     }
sl@0: 
sl@0: /**
sl@0: The static cache page creation function.
sl@0: Cache page objects are not supposed to be created on the stack, so this factory function is required.
sl@0: */
sl@0: TDynamicDirCachePage* TDynamicDirCachePage::NewL(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr)
sl@0:     {
sl@0:     return new(ELeave) TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr);
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Cache page constructor.
sl@0: @param  aOwnerCache pointer of the cache that owns this page
sl@0: @param  aStartMedPos    the start address on the media that this page caches
sl@0: @param  aStartRamAddr   the start address in the ram that this page content lives
sl@0: */
sl@0: TDynamicDirCachePage::TDynamicDirCachePage(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr)
sl@0: :iStartMedPos(aStartMedPos),
sl@0: iStartRamAddr(aStartRamAddr),
sl@0: iOwnerCache(aOwnerCache),
sl@0: iValid(EFalse),
sl@0: iLocked(EFalse)
sl@0:     {
sl@0: //  __PRINT3(_L("TDynamicDirCachePage::TDynamicDirCachePage(aStartMedPos=%lx, aStartRamAddr=0x%X, aPageSize=%u)"), aStartMedPos, aStartRamAddr, PageSizeInBytes());
sl@0:     iType = EUnknown;
sl@0:     }
sl@0: 
sl@0: /////////////////////////////// class CDynamicDirCache::TLookupEntry ///////////////////////////
sl@0: /**
sl@0: Required by RHashSet<TLookupEntry> to identify individual hash set entries.
sl@0: @see    RHashSet
sl@0: */
sl@0: TBool IdentityFunction(const TLookupEntry& aEntry1, const TLookupEntry& aEntry2)
sl@0:     {
sl@0:     // only check starting med pos for hash searching
sl@0:     return aEntry1.iPos == aEntry2.iPos;
sl@0:     }
sl@0: /**
sl@0: Required by RHashSet<TLookupEntry> to generate hash value.
sl@0: @see    RHashSet
sl@0: */
sl@0: TUint32 HashFunction(const TLookupEntry& aEntry)
sl@0:     {
sl@0:     return (DefaultHash::Integer(I64HIGH(aEntry.iPos)) + DefaultHash::Integer(I64LOW(aEntry.iPos)));
sl@0:     }
sl@0: 
sl@0: /////////////////////////////// class CDynamicDirCache ///////////////////////////
sl@0: CDynamicDirCache::~CDynamicDirCache()
sl@0:     {
sl@0: //  __PRINT(_L("CDynamicDirCache::~CDynamicDirCache()"));
sl@0: 
sl@0:     // we should never decommit locked pages
sl@0:     while (!iLockedQ.IsEmpty())
sl@0:         {
sl@0:         TDynamicDirCachePage* page = iLockedQ.Last();
sl@0:         DeQueue(page);      // remove from queue
sl@0:         LookupTblRemove(page->StartPos());  // remove from lookuptable
sl@0:         delete page;
sl@0:         }
sl@0:     ASSERT(iLockedQCount == 0);
sl@0: 
sl@0:     while (!iUnlockedQ.IsEmpty())
sl@0:         {
sl@0:         TDynamicDirCachePage* page = iUnlockedQ.Last();
sl@0:         DeQueue(page);      // remove from queue
sl@0:         LookupTblRemove(page->StartPos());  // remove from lookuptable
sl@0:         DecommitPage(page); // inform cache client to decommit page memory
sl@0:         delete page;
sl@0:         }
sl@0:     ASSERT(iUnlockedQCount == 0);
sl@0: 
sl@0:     delete iActivePage;
sl@0: 
sl@0:     ASSERT(iLookupTable.Count() == 0);
sl@0:     iLookupTable.Close();
sl@0:     if (iCacheMemoryClient)
sl@0:         iCacheMemoryClient->Reset();
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Constructor of CDynamicDirCache.
sl@0: @param  aDrive  local drive interface to read/write media
sl@0: @param  aMinPageNum the minimum page number for the cache, includes iActive page and locked pages.
sl@0: @param  aMaxPageNum the maximum page number for the cache, includes iActive page, locked pages and unlocked pages.
sl@0: @param  aPageSizeInBytesLog2    the log2 value of page size in bytes, assumes page size is always a power of two
sl@0: */
sl@0: CDynamicDirCache::CDynamicDirCache(TFatDriveInterface& aDrive, TUint32 aMinPageNum, TUint32 aMaxPageNum, TUint32 aPageSizeInBytesLog2)
sl@0: :iPageSizeLog2(aPageSizeInBytesLog2),
sl@0: iMinSizeInPages(aMinPageNum),
sl@0: iMaxSizeInPages(aMaxPageNum),
sl@0: iDrive(aDrive),
sl@0: iLockedQ(_FOFF(TDynamicDirCachePage, iLink)),
sl@0: iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)),
sl@0: iLockedQCount(0),
sl@0: iUnlockedQCount(0),
sl@0: iHashFunction(HashFunction),
sl@0: iIdentityFunction(IdentityFunction),
sl@0: iLookupTable(iHashFunction, iIdentityFunction)
sl@0:     {
sl@0:     iPageSizeInBytes = 1 << aPageSizeInBytesLog2;
sl@0:     iCacheDisabled = EFalse;
sl@0:     iMinCacheSizeInBytes = aMinPageNum << aPageSizeInBytesLog2;
sl@0:     iMaxCacheSizeInBytes = aMaxPageNum << aPageSizeInBytesLog2;
sl@0:     ASSERT(iPageSizeInBytes && iPageSizeInBytes <= iMinCacheSizeInBytes && iMinCacheSizeInBytes <= iMaxCacheSizeInBytes);
sl@0:     // initial value, will be reset from outside
sl@0:     iCacheBasePos = 0;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Second phase constructor of CDynamicDirCache.
sl@0: @param  aClientName the identification of cache memeory client this cache connects
sl@0: */
sl@0: void CDynamicDirCache::ConstructL(const TDesC& aClientName)
sl@0:     {
sl@0: //    __PRINT3(_L("CDynamicDirCache::ConstructL(Min=%u, Max=%u, page=%u)"), iMinCacheSizeInBytes, iMaxCacheSizeInBytes, iPageSizeInBytes);
sl@0:     CCacheMemoryManager* manager = CCacheMemoryManagerFactory::CacheMemoryManager();
sl@0:     if (manager)
sl@0:         {
sl@0:         // client will register itself onto cache memory manager when created
sl@0:         // note this operation may leave under OOM condition
sl@0:         iCacheMemoryClient = manager->ConnectClientL(aClientName, iMinSizeInPages * PageSizeInSegs(), iMaxSizeInPages * PageSizeInSegs());
sl@0:         }
sl@0:     else
sl@0:         {
sl@0:         User::Leave(KErrNotSupported);
sl@0:         }
sl@0: 
sl@0:     ASSERT(iCacheMemoryClient);
sl@0:     if (!iCacheMemoryClient)
sl@0:         {
sl@0:         User::Leave(KErrNoMemory);
sl@0:         }
sl@0: 
sl@0:     // reserve active page
sl@0:     iActivePage = AllocateAndLockNewPageL(0);
sl@0:     ASSERT(iActivePage);
sl@0:     if (!iActivePage)
sl@0:         {
sl@0:         User::Leave(KErrNoMemory);
sl@0:         }
sl@0:     iActivePage->SetPageType(TDynamicDirCachePage::EActivePage);
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Static factory function of CDynamicDirCache
sl@0: */
sl@0: CDynamicDirCache* CDynamicDirCache::NewL(TFatDriveInterface& aDrive, TUint32 aMinPageNum, TUint32 aMaxPageNum, TUint32 aPageSizeLog2, const TDesC& aClientName)
sl@0:     {
sl@0: //    __PRINT3(_L("CDynamicDirCache::NewL(MinPageNum=%u, MaxPageNum=%u, page=%u)"), aMinPageNum, aMaxPageNum, 1<<aPageSizeLog2);
sl@0:     CDynamicDirCache* pSelf = new (ELeave) CDynamicDirCache(aDrive, aMinPageNum, aMaxPageNum, aPageSizeLog2);
sl@0:     CleanupStack::PushL(pSelf);
sl@0:     pSelf->ConstructL(aClientName);
sl@0:     CleanupStack::Pop();
sl@0:     return pSelf;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Insert an unlocked page into the last position of the locked queue, may squeeze the original last page into
sl@0: the unlocked queue.
sl@0: This function is used on last visited but 'unlocked' pages to avoid excessive lock/unlock calls to cache memory
sl@0: manager as contiguous entry reading/writing often happens on the same page.
sl@0: @param  aPage   the page to be inserted.
sl@0: @pre    the page type of aPage should only be TDynamicDirCachePage::EUnknown
sl@0: */
sl@0: void CDynamicDirCache::MakePageLastLocked(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     // this function should not be called on active pages
sl@0:     ASSERT(aPage->iType == TDynamicDirCachePage::EUnknown);
sl@0: 
sl@0:     if (iLockedQ.IsEmpty())
sl@0:         {
sl@0:         // if locked queue is empty, add it onto the locked queue directly
sl@0:         AddFirstOntoQueue(aPage, TDynamicDirCachePage::ELocked);
sl@0:         }
sl@0:     else
sl@0:         {
sl@0:         // otherwise, we squeeze for the last position on locked queue
sl@0:         while (iLockedQCount + 1 >= iMinSizeInPages)
sl@0:             {
sl@0:             TDynamicDirCachePage* last = iLockedQ.Last();
sl@0:             DeQueue(last);
sl@0:             UnlockPage(last);
sl@0:             AddFirstOntoQueue(last, TDynamicDirCachePage::EUnlocked);
sl@0:             }
sl@0: 
sl@0:         // iLockedQCount + 1 < iMinSizeInPages
sl@0:         iLockedQ.AddLast(*aPage);
sl@0:         aPage->SetPageType(TDynamicDirCachePage::ELocked);
sl@0:         iLockedQCount++;
sl@0:         }
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page
sl@0: first.
sl@0: @param  aPos    the starting position of the media address to be read.
sl@0: @param  aLength the length of the content to be read.
sl@0: @param  aDes    the descriptor to contain the content.
sl@0: @pre    aLength should be no more than page size.
sl@0: */
sl@0: void CDynamicDirCache::ReadDataFromSinglePageL(TInt64 aPos, TInt aLength, TDes8& aDes)
sl@0:     {
sl@0:     //-- the data section is in the cache page entirely, take data directly from the cache
sl@0:     TDynamicDirCachePage* pPage = FindPageByPos(aPos);
sl@0:     if (pPage)
sl@0:         {
sl@0:         // lock page before reading,
sl@0:         if (LockPage(pPage) != NULL)
sl@0:             {
sl@0:             // read data
sl@0:             aDes.Copy(pPage->PtrInPage(aPos), aLength);
sl@0: 
sl@0:             // if page is from unlocked queue, insert it onto the last page of the locked
sl@0:             //  queue. this is to avoid excessive locking and unlocking operations that is
sl@0:             //  highly likely to happen for following reads.
sl@0:             if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
sl@0:                 {
sl@0:                 DeQueue(pPage);
sl@0:                 MakePageLastLocked(pPage);
sl@0:                 }
sl@0:             }
sl@0:         else    // page locking failed
sl@0:             {
sl@0:             ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked);
sl@0:             DeQueue(pPage);
sl@0:             LookupTblRemove(pPage->StartPos());
sl@0:             DecommitPage(pPage);
sl@0:             delete pPage;
sl@0:             pPage = NULL;
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     if (!pPage)
sl@0:         {
sl@0:         // if page not found or page data not valid anymore, use active page to read data in
sl@0:         pPage = UpdateActivePageL(aPos);
sl@0:         // read data
sl@0:         aDes.Copy(pPage->PtrInPage(aPos), aLength);
sl@0:         }
sl@0: 
sl@0:     }
sl@0: 
sl@0: //====================================================================
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::ReadL()
sl@0: */
sl@0: void CDynamicDirCache::ReadL(TInt64 aPos, TInt aLength, TDes8& aDes)
sl@0:     {
sl@0: #ifdef _DEBUG
sl@0:     if(iCacheDisabled)
sl@0:         {
sl@0:         // cache is disabled for debug purposes
sl@0:         __PRINT(_L("CDynamicDirCache disabled"));
sl@0:         User::LeaveIfError(iDrive.ReadNonCritical(aPos, aLength, aDes));
sl@0:         return;
sl@0:         }
sl@0: #endif //_DEBUG
sl@0: 
sl@0:     aDes.Zero();
sl@0:     const TUint32 PageSz = iPageSizeInBytes;//-- cache page size
sl@0: 
sl@0:     TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0:     const TUint32 bytesToPageEnd = (TUint32)(pageStartMedPos + PageSz - aPos); //-- number of bytes from aPos to the end of the page
sl@0: 
sl@0: //    __PRINT5(_L("CDynamicDirCache::ReadL: aPos=%lx, aLength=%x, page:%lx, pageSz:%x, bytesToPageEnd=%x"), aPos, aLength, pageStartMedPos, PageSz, bytesToPageEnd);
sl@0:     // if all data needed is on a single page
sl@0:     if((TUint32)aLength <= bytesToPageEnd)
sl@0:         {
sl@0:         ReadDataFromSinglePageL(aPos, aLength, aDes);
sl@0:         }
sl@0:     // or data to be read cross cache page boundary or probably we have more than 1 page to read
sl@0:     else
sl@0:         {
sl@0:         __PRINT(_L("CDynamicDirCache::ReadL() CROSS PAGE!"));
sl@0:         TUint32 dataLen(aLength);   //-- current data length
sl@0:         TInt64  currMediaPos(aPos); //-- current media position
sl@0: 
sl@0:         //-- 1. read data that are already in the current page
sl@0:         ReadDataFromSinglePageL(currMediaPos, bytesToPageEnd, aDes);
sl@0:         dataLen -= bytesToPageEnd;
sl@0:         currMediaPos += bytesToPageEnd;
sl@0: 
sl@0:         TPtr8 dataNext = aDes.MidTPtr(aDes.Length());
sl@0: 
sl@0:         //-- 2. read whole pages of data
sl@0:         while (dataLen >= PageSz)
sl@0:             {
sl@0:             //-- find out if currMediaPos is in cache. If not, find a spare page and read data there
sl@0:             ReadDataFromSinglePageL(currMediaPos, PageSz, dataNext);
sl@0:             currMediaPos += PageSz;
sl@0:             dataLen -= PageSz;
sl@0:             dataNext = dataNext.MidTPtr(dataNext.Length());
sl@0:             }
sl@0: 
sl@0:         //-- 3. read the rest of the data
sl@0:         if(dataLen > 0)
sl@0:             {
sl@0:             ReadDataFromSinglePageL(currMediaPos, dataLen, dataNext);
sl@0:             }
sl@0:         } //else((TUint32)aLength <= bytesToPageEnd)
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Write data through a single page. If the page is not found or not valid anymore, read media onto iActive page
sl@0: first, then write data through iActive page.
sl@0: @param  aPos    the starting position of the media address to be write.
sl@0: @param  aData   the starting address that the writing content lives in the ram.
sl@0: @param  aDataLen    the length of the content to be written.
sl@0: @pre    aDataLen    should be no more than page size.
sl@0: */
sl@0: void CDynamicDirCache::WriteDataOntoSinglePageL(TInt64 aPos, const TUint8* aData, TUint32 aDataLen)
sl@0:     {
sl@0:     ASSERT(aDataLen <= iPageSizeInBytes);
sl@0:     //-- the data section is in the cache page entirely, take data directly from the cache
sl@0:     TDynamicDirCachePage* pPage = FindPageByPos(aPos);
sl@0:     if (pPage)
sl@0:         {
sl@0:         // lock page before writing,
sl@0:         if (LockPage(pPage) != NULL)
sl@0:             {
sl@0:             //-- update cache
sl@0:             Mem::Copy(pPage->PtrInPage(aPos), aData, aDataLen);
sl@0:             }
sl@0:         else
sl@0:             {
sl@0:             ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked);
sl@0:             DeQueue(pPage);
sl@0:             LookupTblRemove(pPage->StartPos());
sl@0:             DecommitPage(pPage);
sl@0:             delete pPage;
sl@0:             pPage = NULL;
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     // if page not found or page data not valid anymore, use active page to read data in
sl@0:     if (!pPage)
sl@0:         {
sl@0:         pPage = UpdateActivePageL(aPos);
sl@0:         //-- update cache
sl@0:         Mem::Copy(pPage->PtrInPage(aPos), aData, aDataLen);
sl@0:         }
sl@0: 
sl@0:     // make sure the page is unlocked after use
sl@0:     if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
sl@0:         {
sl@0:         UnlockPage(pPage);
sl@0:         }
sl@0: 
sl@0:     // always make writting events MRU
sl@0:     MakePageMRU(aPos);
sl@0:     return;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::WriteL()
sl@0: */
sl@0: void CDynamicDirCache::WriteL(TInt64 aPos,const TDesC8& aDes)
sl@0:     {
sl@0: #ifdef _DEBUG
sl@0:     if(iCacheDisabled)
sl@0:         {
sl@0:         // cache is disabled for debug purposes
sl@0:         __PRINT(_L("CDynamicDirCache disabled"));
sl@0:         User::LeaveIfError(iDrive.WriteCritical(aPos,aDes));
sl@0:         return;
sl@0:         }
sl@0: #endif //_DEBUG
sl@0: 
sl@0:     TUint32 dataLen = aDes.Size();
sl@0:     const TUint8* pData   = aDes.Ptr();
sl@0:     const TUint32 PageSz  = iPageSizeInBytes; //-- cache page size
sl@0: 
sl@0:     TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0:     TUint32 bytesToPageEnd = (TUint32)(pageStartMedPos + PageSz - aPos);
sl@0: 
sl@0: //    __PRINT5(_L("CDynamicDirCache::WriteL: aPos=%lx, aLength=%x, page:%lx, pageSz:%x, bytesToPageEnd=%x"), aPos, dataLen, pageStartMedPos, PageSz, bytesToPageEnd);
sl@0: 
sl@0:     if(dataLen <= bytesToPageEnd)
sl@0:         {
sl@0:         WriteDataOntoSinglePageL(aPos, pData, dataLen);
sl@0:         }
sl@0:     else
sl@0:         {
sl@0:         __PRINT(_L("CDynamicDirCache::WriteL() CROSS PAGE!"));
sl@0: 
sl@0:         //-- Data to be written cross cache page boundary or probably we have more than 1 page to write
sl@0:         TInt64  currMediaPos(aPos);
sl@0: 
sl@0:         //-- 1. update the current page
sl@0:         WriteDataOntoSinglePageL(currMediaPos, pData, bytesToPageEnd);
sl@0: 
sl@0:         pData += bytesToPageEnd;
sl@0:         currMediaPos += bytesToPageEnd;
sl@0:         dataLen -= bytesToPageEnd;
sl@0: 
sl@0:         //-- 2. write whole pages of data to the cache
sl@0:         while (dataLen >= PageSz)
sl@0:             {
sl@0:             WriteDataOntoSinglePageL(currMediaPos, pData, PageSz);
sl@0: 
sl@0:             pData += PageSz;
sl@0:             currMediaPos += PageSz;
sl@0:             dataLen -= PageSz;
sl@0:             }
sl@0: 
sl@0:         //-- 3. write the rest of the data
sl@0:         if(dataLen > 0)
sl@0:             {
sl@0:             WriteDataOntoSinglePageL(currMediaPos, pData, dataLen);
sl@0:             }
sl@0:         }// else(dataLen <= bytesToPageEnd)
sl@0: 
sl@0: 
sl@0:     //-- write data to the media
sl@0:     const TInt nErr = iDrive.WriteCritical(aPos,aDes);
sl@0:     if(nErr != KErrNone)
sl@0:         {//-- some serious problem occured during writing, invalidate cache.
sl@0:         InvalidateCache();
sl@0:         User::Leave(nErr);
sl@0:         }
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::InvalidateCache()
sl@0: */
sl@0: void CDynamicDirCache::InvalidateCache(void)
sl@0:     {
sl@0:     __PRINT2(_L("CDynamicDirCache::InvalidateCache(locked=%d, unlocked=%d)"), iLockedQCount, iUnlockedQCount);
sl@0:     // we should never decommit locked pages as they needs to be reserved anyway
sl@0:     // the overhead of unnecessary page committing operations
sl@0:     while(!iLockedQ.IsEmpty())
sl@0:         {
sl@0:         TDynamicDirCachePage* page = iLockedQ.Last();
sl@0:         DeQueue(page);                      // remove from queue
sl@0:         LookupTblRemove(page->StartPos());  // remove from lookuptable
sl@0:         delete page;
sl@0:         }
sl@0:     ASSERT(iLockedQCount == 0);
sl@0: 
sl@0:     // however we should decommit unlocked pages here
sl@0:     while (!iUnlockedQ.IsEmpty())
sl@0:         {
sl@0:         TDynamicDirCachePage* page = iUnlockedQ.Last();
sl@0:         DeQueue(page);                      // remove from queue
sl@0:         LookupTblRemove(page->StartPos());  // remove from lookuptable
sl@0:         DecommitPage(page);                 // inform cache client to decommit page memory
sl@0:         delete page;
sl@0:         }
sl@0:     ASSERT(iUnlockedQCount == 0);
sl@0: 
sl@0:     ASSERT(iLookupTable.Count() == 0);
sl@0:     iLookupTable.Close();
sl@0: 
sl@0:     ASSERT(iCacheMemoryClient);
sl@0: 
sl@0:     // initialize cache state.
sl@0:     // Note that once the client is reset, all pages lose connection with the client
sl@0:     //  including the active page. So we will need to reset and re-allocate active page
sl@0:     //  properly.
sl@0:     if (iCacheMemoryClient)
sl@0:         iCacheMemoryClient->Reset();
sl@0: 
sl@0:     // reset and re-allocate active page
sl@0:     ResetPagePos(iActivePage);              // reset start media position (0), invalidate page content
sl@0:     TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs());
sl@0:     // this should always succeed as the client has just been reset and there are always reserved pages
sl@0:     ASSERT(startRamAddr);
sl@0:     iActivePage->SetStartPtr(startRamAddr); // set RAM address
sl@0:     }
sl@0: 
sl@0: 
sl@0: /** this method isn't implemented*/
sl@0: void CDynamicDirCache::InvalidateCachePage(TUint64 /*aPos*/)
sl@0:     {
sl@0:     ASSERT(0);
sl@0:     }
sl@0: 
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::PosCached()
sl@0: */
sl@0: TUint32 CDynamicDirCache::PosCached(const TInt64& aPos, TInt64& aCachedPosStart)
sl@0:     {
sl@0:     const TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0: 
sl@0:     // only search the page in lookup table
sl@0:     // NOTE: we don't count the active page into acount here,
sl@0:     // this is to avoid pulling next pages recursively
sl@0:     TDynamicDirCachePage* pPage = LookupTblFind(pageStartMedPos);
sl@0: 
sl@0:     // then check if page is still valid if page is on Unlocked Page Queue
sl@0:     if (pPage && pPage->PageType() == TDynamicDirCachePage::EUnlocked)
sl@0:         {
sl@0:         if (LockPage(pPage) != NULL)
sl@0:             {
sl@0: //          __PRINT1(_L("CDynamicDirCache::PosCached: page(0x%lx) found on Unlocked Queue!"), aPos);
sl@0:             // have to unlock it before returning, otherwise there will be memory leak
sl@0:             UnlockPage(pPage);
sl@0:             aCachedPosStart = pPage->StartPos();
sl@0:             return pPage->PageSizeInBytes();
sl@0:             }
sl@0:         else    // if the unlocked page is not valid anymore, remove it
sl@0:             {
sl@0:             DeQueue(pPage);
sl@0:             LookupTblRemove(pPage->StartPos());
sl@0:             DecommitPage(pPage);
sl@0:             delete pPage;
sl@0:             pPage = NULL;
sl@0:             }
sl@0:         }
sl@0:     // otherwise if page is already locked or valid active page
sl@0:     else if (pPage)
sl@0:         {
sl@0:         __PRINT1(_L("CDynamicDirCache::PosCached: page(0x%lx) on Locked Queue!"), aPos);
sl@0:         aCachedPosStart = pPage->StartPos();
sl@0:         return pPage->PageSizeInBytes();
sl@0:         }
sl@0: 
sl@0:     // page is not found or not valid anymore
sl@0:     return 0;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::CacheSizeInBytes()
sl@0: */
sl@0: TUint32 CDynamicDirCache::CacheSizeInBytes()  const
sl@0:     {
sl@0:     return iMaxCacheSizeInBytes;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::Control()
sl@0: */
sl@0: TInt CDynamicDirCache::Control(TUint32 aFunction, TUint32 aParam1, TAny* aParam2)
sl@0:     {
sl@0:     TInt r = KErrNotSupported;
sl@0: #ifdef _DEBUG
sl@0:     (void)aParam2;
sl@0:     switch(aFunction)
sl@0:         {
sl@0:         // disable / enable cache, for debug
sl@0:         // if aParam1 != 0 cache will be disabled, enabled otherwise
sl@0:         case EDisableCache:
sl@0:             iCacheDisabled = aParam1 ? 1 : 0;
sl@0:             r = KErrNone;
sl@0:         break;
sl@0: 
sl@0:         // dump cache, for debug
sl@0:         case EDumpCache:
sl@0:             {
sl@0:             RFs fs;
sl@0:             fs.Connect();
sl@0:             const TUint32 debugRegister = DebugRegister();
sl@0:             fs.SetDebugRegister(debugRegister|KFSYS);
sl@0:             Dump();
sl@0:             fs.SetDebugRegister(debugRegister);
sl@0:             fs.Close();
sl@0:             break;
sl@0:             }
sl@0:         case ECacheInfo:
sl@0:             {
sl@0:             RFs fs;
sl@0:             fs.Connect();
sl@0:             const TUint32 debugRegister = DebugRegister();
sl@0:             fs.SetDebugRegister(debugRegister|KFSYS);
sl@0:             Info();
sl@0:             fs.SetDebugRegister(debugRegister);
sl@0:             fs.Close();
sl@0:             break;
sl@0:             }
sl@0: 
sl@0:         default:
sl@0:             __PRINT1(_L("CDynamicDirCache::Control() invalid function: %d"), aFunction);
sl@0:             ASSERT(0);
sl@0:         break;
sl@0:         }
sl@0: 
sl@0: #else
sl@0:     (void)aFunction; //-- supress warnings
sl@0:     (void)aParam1;
sl@0:     (void)aParam2;
sl@0:     User::Invariant(); //-- don't call this method in release build
sl@0: #endif //_DEBUG
sl@0: 
sl@0:     return r;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::SetCacheBasePos()
sl@0: */
sl@0: void CDynamicDirCache::SetCacheBasePos(TInt64 aBasePos)
sl@0:     {
sl@0:     iCacheBasePos = aBasePos;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::SetCacheBasePos()
sl@0: */
sl@0: TUint32 CDynamicDirCache::PageSizeInBytesLog2() const
sl@0:     {
sl@0:     return iPageSizeLog2;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Implementation of pure virtual function.
sl@0: @see    MWTCacheInterface::MakePageMRU()
sl@0: */
sl@0: void CDynamicDirCache::MakePageMRU(TInt64 aPos)
sl@0:     {
sl@0:     __PRINT1(_L("MakePageMRU (%lx)"), aPos);
sl@0: //  __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages);
sl@0:     // check the MRU page first, if it is already the MRU page, we can return immediately
sl@0:     TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0:     if (!iLockedQ.IsEmpty())
sl@0:         {
sl@0:         if (iLockedQ.First()->StartPos() == pageStartMedPos)
sl@0:             {
sl@0:             return;
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     TDynamicDirCachePage* pPage = FindPageByPos(aPos);
sl@0:     if (pPage)
sl@0:         {
sl@0:         ASSERT(pPage->IsValid());
sl@0:         // lock page before make it MRU
sl@0:         if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
sl@0:             {
sl@0:             ASSERT(!pPage->IsLocked());
sl@0:             if (LockPage(pPage) == NULL)
sl@0:                 {
sl@0:                 DeQueue(pPage);
sl@0:                 LookupTblRemove(pPage->StartPos());
sl@0:                 DecommitPage(pPage);
sl@0:                 delete pPage;
sl@0:                 pPage = NULL;
sl@0:                 }
sl@0:             }
sl@0:         else
sl@0:             {
sl@0:             // error checking: page should either be locked or active
sl@0:             ASSERT(LockPage(pPage) != NULL);
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     // if page not found or page data not valid anymore, use active page to read data
sl@0:     if (!pPage)
sl@0:         {
sl@0:         TRAPD(err, pPage = UpdateActivePageL(aPos));
sl@0:         if (err != KErrNone)
sl@0:             {
sl@0:             // problem occurred reading active page, return immediately.
sl@0:             return;
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     // by now, the page is either locked or active page
sl@0:     ASSERT(pPage && pPage->IsValid() && pPage->IsLocked());
sl@0: 
sl@0:     switch (pPage->PageType())
sl@0:         {
sl@0:         // if the page is the active page, we will need to find a new active page for replacement
sl@0:         case TDynamicDirCachePage::EActivePage:
sl@0:             {
sl@0:             TDynamicDirCachePage* newAP = NULL;
sl@0:             // if there is more cache room available, try to create a new page first
sl@0:             if (!CacheIsFull())
sl@0:                 {
sl@0:                 // allocate and lock a new page
sl@0:                 TRAPD(err, newAP = AllocateAndLockNewPageL(0));
sl@0:                 // if any error ocurrs, return immediately
sl@0:                 if (err != KErrNone)
sl@0:                     {
sl@0:                     // unlock the page that was originally unlocked before leave
sl@0:                     if (pPage->PageType() == TDynamicDirCachePage::EUnlocked)
sl@0:                         {
sl@0:                         UnlockPage(pPage);
sl@0:                         }
sl@0:                     return;
sl@0:                     }
sl@0: 
sl@0:                 if (newAP)
sl@0:                     {
sl@0:                     // replace the active page with the new page
sl@0:                     newAP->SetPageType(TDynamicDirCachePage::EActivePage);
sl@0:                     iActivePage = newAP;
sl@0:                     }
sl@0:                 }
sl@0: 
sl@0:             // if cache has grown to its max size, or new page allocation failed
sl@0:             if (!newAP)
sl@0:                 {
sl@0:                 // try to lock the LRU page on the unlocked page queque first
sl@0:                 if (!iUnlockedQ.IsEmpty())
sl@0:                     {
sl@0:                     newAP = iUnlockedQ.Last();
sl@0:                     ASSERT(newAP->IsValid());
sl@0:                     if (LockPage(newAP) != NULL)
sl@0:                         {
sl@0:                         // deque, reset pos, set new type
sl@0:                         DeQueue(newAP);
sl@0:                         LookupTblRemove(newAP->StartPos());
sl@0:                         ResetPagePos(newAP);
sl@0:                         newAP->SetPageType(TDynamicDirCachePage::EActivePage);
sl@0:                         // replace active page
sl@0:                         iActivePage = newAP;
sl@0:                         }
sl@0:                     // if falied locking the LRU page from unclocked queque,
sl@0:                     // delete it
sl@0:                     else
sl@0:                         {
sl@0:                         DeQueue(newAP);
sl@0:                         LookupTblRemove(newAP->StartPos());
sl@0:                         DecommitPage(newAP);
sl@0:                         delete newAP;
sl@0:                         newAP = NULL;
sl@0:                         }
sl@0:                     }
sl@0:                 }
sl@0: 
sl@0:             // if still have not found new active page
sl@0:             // grab the LRU page from Locked Page Queue for active page
sl@0:             if (!newAP)
sl@0:                 {
sl@0:                 ASSERT(!iLockedQ.IsEmpty());
sl@0:                 newAP = iLockedQ.Last();
sl@0:                 // deque, reset pos, set new type
sl@0:                 DeQueue(newAP);
sl@0:                 LookupTblRemove(newAP->StartPos());
sl@0:                 ResetPagePos(newAP);
sl@0:                 newAP->SetPageType(TDynamicDirCachePage::EActivePage);
sl@0:                 // replace active page
sl@0:                 iActivePage = newAP;
sl@0:                 }
sl@0: 
sl@0:             // we should always be able to find a locked page for active page
sl@0:             ASSERT(newAP != NULL);
sl@0: 
sl@0:             // make original page (i.e. former active page) MRU
sl@0:             // add onto locked queue
sl@0:             AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked);
sl@0:             // add onto lookuptbl, as active page is not on lookup tbl originally
sl@0:             LookupTblAdd(pPage);
sl@0:             // check cache limit
sl@0:             CheckThresholds();
sl@0:             return;
sl@0:             }
sl@0:         case TDynamicDirCachePage::EUnlocked:
sl@0:             {
sl@0:             // if page was originally on Unlocked Page Queque, remove it from Unlocked Page Queue, add it
sl@0:             // to the Locked Page Queue and make it MRU
sl@0:             DeQueue(pPage);
sl@0:             AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked);
sl@0:             // check cache limit
sl@0:             CheckThresholds();
sl@0:             return;
sl@0:             }
sl@0:         case TDynamicDirCachePage::ELocked:
sl@0:             {
sl@0:             // otherwise the page was on Locked Page Queue, make it MRU
sl@0:             // no need to check cache limit
sl@0:             if (pPage != iLockedQ.First())
sl@0:                 {
sl@0:                 DeQueue(pPage);
sl@0:                 AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked);
sl@0:                 return;
sl@0:                 }
sl@0:             break;
sl@0:             }
sl@0:         default:
sl@0:             ASSERT(0);
sl@0:         }
sl@0:     }
sl@0: 
sl@0: //====================================================================
sl@0: /**
sl@0: Internal query function, to check if aPos is cached or not. iActive page is included in searching.
sl@0: */
sl@0: TDynamicDirCachePage* CDynamicDirCache::FindPageByPos(TInt64 aPos)
sl@0:     {
sl@0:     __PRINT1(_L("CDynamicDirCache::FindPageByPos(aPos=%lx)"), aPos);
sl@0:     // align the page position
sl@0:     TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0: 
sl@0:     if ((iActivePage->StartPos() == pageStartMedPos))
sl@0:         {
sl@0:         ASSERT(iActivePage->IsValid());
sl@0:         return iActivePage;
sl@0:         }
sl@0: 
sl@0:     // search in lookup table
sl@0:     return LookupTblFind(pageStartMedPos);
sl@0:     }
sl@0: 
sl@0: /**
sl@0: read a page length data into iActive page and return iActive page if read is successful.
sl@0: */
sl@0: TDynamicDirCachePage* CDynamicDirCache::UpdateActivePageL(TInt64 aPos)
sl@0:     {
sl@0:     // align the page position
sl@0:     TInt64 pageStartMedPos = CalcPageStartPos(aPos);
sl@0: 
sl@0:     if (iActivePage->StartPos() == pageStartMedPos && iActivePage->IsValid())
sl@0:         {
sl@0:         return iActivePage;
sl@0:         }
sl@0: 
sl@0:     __PRINT2(_L("CDynamicDirCache::UpdateActivePageL(aPos=%lx, active=%lx)"), aPos, iActivePage->StartPos());
sl@0: 
sl@0:     // set start med pos value, no other effects, only available to active page
sl@0:     iActivePage->SetPos(pageStartMedPos);
sl@0: 
sl@0:     // read data, make active page valid
sl@0:     TUint8* data = iActivePage->PtrInPage(iActivePage->iStartMedPos);
sl@0:     TPtr8 dataPtr(data, iPageSizeInBytes);
sl@0:     const TInt nErr = iDrive.ReadNonCritical(iActivePage->iStartMedPos, iPageSizeInBytes, dataPtr);
sl@0:     if(nErr !=KErrNone)
sl@0:         {
sl@0:         // some serious problem occured during reading, invalidate cache.
sl@0:         InvalidateCache();
sl@0:         User::Leave(nErr);
sl@0:         }
sl@0:     iActivePage->SetValid(ETrue);
sl@0: 
sl@0:     return iActivePage;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Check if the number of (locked pages + iActive page) and unlocked pages have exceeded minimum allowed page
sl@0: number and maximum allowed page number respectively.
sl@0: */
sl@0: void CDynamicDirCache::CheckThresholds()
sl@0:     {
sl@0:     while (iLockedQCount + 1 > iMinSizeInPages)
sl@0:         {
sl@0:         TDynamicDirCachePage* movePage = iLockedQ.Last();
sl@0:         UnlockPage(movePage);
sl@0:         DeQueue(movePage);
sl@0:         TInt err = LookupTblRemove(movePage->StartPos());
sl@0:         ASSERT(err == KErrNone);
sl@0: 
sl@0:         // if it is a valid page, add onto unlocked queue
sl@0:         if (movePage->StartPos() != 0)
sl@0:             {
sl@0:             ASSERT(movePage->IsValid());
sl@0:             AddFirstOntoQueue(movePage, TDynamicDirCachePage::EUnlocked);
sl@0:             err = LookupTblAdd(movePage);
sl@0:             ASSERT(err == KErrNone);
sl@0:             }
sl@0:         else // reserved page, delete
sl@0:             {
sl@0:             DecommitPage(movePage);
sl@0:             delete movePage;
sl@0:             }
sl@0:         }
sl@0: 
sl@0:     // if unlocked queue exceeds limit, delete LRU page
sl@0:     // note: all pages on unlocked queue should be valid
sl@0:     while (iUnlockedQCount > iMaxSizeInPages - iMinSizeInPages)
sl@0:         {
sl@0:         TDynamicDirCachePage* removePage = iUnlockedQ.Last();
sl@0:         ASSERT(removePage->StartPos() != 0 && removePage->IsValid());
sl@0:         DeQueue(removePage);
sl@0:         LookupTblRemove(removePage->StartPos());
sl@0:         DecommitPage(removePage);
sl@0:         delete removePage;
sl@0:         }
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Try to create a new page and lock the page content when it is created. This function should only be called
sl@0: when creating iActive page or making a page MRU (which might result in page evictions).
sl@0: @return the pointer of the newly created page, or NULL if allocation failed.
sl@0: @param  aStartMedPos    the starting media address of the page to be created.
sl@0: @pre    aStartMedPos should not already be existing in the cache.
sl@0: */
sl@0: TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPageL(TInt64 aStartMedPos)
sl@0:     {
sl@0:     __PRINT1(_L("CDynamicDirCache::AllocateAndLockNewPageL(aStartMedPos=%lx)"), aStartMedPos);
sl@0: 
sl@0:     TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs());
sl@0:     if (startRamAddr)
sl@0:         {
sl@0:         // create new page and return
sl@0:         TDynamicDirCachePage* pPage = TDynamicDirCachePage::NewL(this, aStartMedPos, startRamAddr);
sl@0:         pPage->SetLocked(ETrue);
sl@0:         pPage->SetValid(EFalse);
sl@0:         return pPage;
sl@0:         }
sl@0: 
sl@0:     return NULL;
sl@0:     }
sl@0: 
sl@0: #ifdef _DEBUG
sl@0: /**
sl@0: Dump cache information, only enabled in debug mode.
sl@0: @see CDynamicDirCache::Control()
sl@0: */
sl@0: void CDynamicDirCache::Info() const
sl@0:     {
sl@0:     __PRINT(_L("======== CDynamicDirCache::Info ========="));
sl@0:     const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2();
sl@0:     // page size
sl@0:     __PRINT1(_L("=== Pages size:               [%d Bytes]"), iPageSizeInBytes);
sl@0:     __PRINT1(_L("=== Segment size:             [%d Bytes]"), 1 << SegmentSizeInBytesLog2);
sl@0: 
sl@0:     // data size:
sl@0:     __PRINT1(_L("=== Min data size:            [%d Bytes]"), iMinSizeInPages << iPageSizeLog2);
sl@0:     __PRINT1(_L("=== Max data size:            [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2);
sl@0: 
sl@0:     // memory size:
sl@0:     const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2;
sl@0:     __PRINT1(_L("=== Min memory size:          [%d Bytes]"), iMinSizeInPages << pageMemSizeLog2);
sl@0:     __PRINT1(_L("=== Max memory size:          [%d Bytes]"), iMaxSizeInPages << pageMemSizeLog2);
sl@0: 
sl@0:     // reserved pages
sl@0:     __PRINT1(_L("=== Number of pages reserved: [%d]"), iMinSizeInPages);
sl@0:     __PRINT1(_L("=== Reserved memory:          [%d Bytes]"), (iMinSizeInPages * PageSizeInSegs()) << SegmentSizeInBytesLog2);
sl@0:     // locked page num
sl@0:     __PRINT1(_L("=== Number of pages locked:   [%d]"), iLockedQCount);
sl@0:     __PRINT1(_L("=== Locked memory:            [%d Bytes]"), (iLockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2);
sl@0:     // unlocked page num
sl@0:     __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount);
sl@0:     __PRINT1(_L("=== Unlocked memory:          [%d Bytes]"), (iUnlockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2);
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Dump cache content, only enabled in debug mode.
sl@0: @see CDynamicDirCache::Control()
sl@0: */
sl@0: void CDynamicDirCache::Dump()
sl@0:     {
sl@0:     __PRINT(_L("======== CDynamicDirCache::Dump ========="));
sl@0:     if (!iLockedQ.IsEmpty())
sl@0:         {
sl@0:         TDblQueIter<TDynamicDirCachePage> q(iLockedQ);
sl@0:         q.SetToFirst();
sl@0:         TInt i = 0;
sl@0:         while((TDynamicDirCachePage*)q)
sl@0:             {
sl@0:             TDynamicDirCachePage* pP = q++;
sl@0:             __PRINT3(_L("=== CDynamicDirCache::iLockedQ\t[%4d](pos=%lx, size=%d)"), i++, pP->StartPos(), pP->PageSizeInBytes());
sl@0:             }
sl@0:         }
sl@0:     if (!iUnlockedQ.IsEmpty())
sl@0:         {
sl@0:         TDblQueIter<TDynamicDirCachePage> q(iUnlockedQ);
sl@0:         q.SetToFirst();
sl@0:         TInt i = 0;
sl@0:         while((TDynamicDirCachePage*)q)
sl@0:             {
sl@0:             TDynamicDirCachePage* pP = q++;
sl@0:             __PRINT3(_L("=== CDynamicDirCache::iUnlockedQ\t[%4d](pos=%lx, size=%u)"), i++, pP->StartPos(), pP->PageSizeInBytes());
sl@0:             }
sl@0:         }
sl@0:     __PRINT2(_L("=== CDynamicDirCache::iActivePage\t[*](pos=%lx, size=%u)"), iActivePage->StartPos(), iActivePage->PageSizeInBytes());
sl@0: 
sl@0:     if (iLookupTable.Count())
sl@0:         {
sl@0:         TInt i = 0;
sl@0:         THashSetIter<TLookupEntry> iter(iLookupTable);
sl@0:         TLookupEntry* pEntry;
sl@0:         pEntry = (TLookupEntry*) iter.Next();
sl@0:         while(pEntry)
sl@0:             {
sl@0:             TDynamicDirCachePage* pP = pEntry->iPage;
sl@0:             __PRINT3(_L("=== CDynamicDirCache::iLookupTable\t[%4d](pos=%lx, size=%u)"), i++, pP->StartPos(), pP->PageSizeInBytes());
sl@0:             pEntry = (TLookupEntry*) iter.Next();
sl@0:             };
sl@0:         }
sl@0:     __PRINT(_L("===========================================\n"));
sl@0:     }
sl@0: #endif //_DEBUG
sl@0: 
sl@0: /**
sl@0: Lock an unlocked page, or do nothing if the page is already locked.
sl@0: @return TUint8* pointer of the page to be locked, if locking is successful, otherwise return NULL.
sl@0: @param  aPage   the pointer of the page to be locked.
sl@0: */
sl@0: TUint8* CDynamicDirCache::LockPage(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage != NULL);
sl@0:     if (aPage->IsLocked())
sl@0:         return aPage->StartPtr();
sl@0: 
sl@0:     TInt r = iCacheMemoryClient->LockSegments(aPage->StartPtr(), PageSizeInSegs());
sl@0:     if (r == KErrNone)
sl@0:         {
sl@0:         aPage->SetLocked(ETrue);
sl@0:         return aPage->StartPtr();
sl@0:         }
sl@0: 
sl@0:     return NULL;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Unlock a locked page.
sl@0: @return TInt    KErrNone if unlocking was successful, otherwise system-wide error code.
sl@0: @param  aPage   the pointer of the page to be unlocked.
sl@0: */
sl@0: TInt CDynamicDirCache::UnlockPage(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage != NULL);
sl@0:     __PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos());
sl@0:     TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs());
sl@0:     if (r == KErrNone)
sl@0:         {
sl@0:         aPage->SetLocked(EFalse);
sl@0:         }
sl@0:     return r;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Decommit a locked or unlocked page.
sl@0: @return TInt    KErrNone if decommition was successful, otherwise system-wide error code.
sl@0: @param  aPage   the pointer of the page to be decommitted.
sl@0: */
sl@0: TInt CDynamicDirCache::DecommitPage(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage != NULL);
sl@0:     __PRINT1(_L("CDynamicDirCache::DecommitPage(%lx)"), aPage->StartPos());
sl@0:     if (aPage)
sl@0:         {
sl@0:         TInt r = iCacheMemoryClient->DecommitSegments(aPage->StartPtr(), PageSizeInSegs());
sl@0:         if (r == KErrNone)
sl@0:             {
sl@0:             aPage->SetLocked(EFalse);
sl@0:             aPage->SetValid(EFalse);
sl@0:             }
sl@0:         return r;
sl@0:         }
sl@0:     return KErrArgument;
sl@0:     }
sl@0: 
sl@0: /////////////////////////// aluxiliary functions //////////////////////////////////
sl@0: /**
sl@0: Calculate the page size in segments. Segment size is the size of the kernel memory unit that cache memory manager manages.
sl@0: We are making assumption here about the page size: page size should always be either less than segment size
sl@0: or multiple times of segment size
sl@0: @return TUint32 the page size in segments.
sl@0: */
sl@0: TUint32 CDynamicDirCache::PageSizeInSegs() const
sl@0:     {
sl@0:     // initialize cache memory manager as all file systems have mounted by now
sl@0:     ASSERT(CCacheMemoryManagerFactory::CacheMemoryManager());
sl@0:     const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2();
sl@0: 
sl@0:     // Page size should be non-zero
sl@0:     ASSERT(iPageSizeInBytes);
sl@0: 
sl@0:     TUint32 segs = iPageSizeInBytes >> SegmentSizeInBytesLog2;
sl@0:     return segs > 0 ? segs : 1;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Deque the page from locked queue or unlocked queue. All pages are managed through these two queues, expect iActive
sl@0: page.
sl@0: @param  aPage   the pointer of the page to be dequeued
sl@0: @return TInt    KErrArgument if aPage is invalid, otherwise KErrNone.
sl@0: */
sl@0: TInt CDynamicDirCache::DeQueue(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage);
sl@0:     if (!aPage)
sl@0:         return KErrArgument;
sl@0: 
sl@0:     if (aPage->iType == TDynamicDirCachePage::ELocked)
sl@0:         {
sl@0:         aPage->Deque();
sl@0:         aPage->SetPageType(TDynamicDirCachePage::EUnknown);
sl@0:         --iLockedQCount;
sl@0:         }
sl@0:     else if (aPage->iType == TDynamicDirCachePage::EUnlocked)
sl@0:         {
sl@0:         aPage->Deque();
sl@0:         aPage->SetPageType(TDynamicDirCachePage::EUnknown);
sl@0:         --iUnlockedQCount;
sl@0:         }
sl@0:     else
sl@0:         {
sl@0:         ASSERT(0);
sl@0:         return KErrArgument;
sl@0:         }
sl@0:     return KErrNone;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Insert a page to the first position of locked queue or unlocked queue.
sl@0: @param  aPage   the pointer of the page to be inserted.
sl@0: @param  aType   the type of the queue to be inserted.
sl@0: @return TInt    KErrArgument if aPage is invalid, otherwise KErrNone.
sl@0: */
sl@0: TInt CDynamicDirCache::AddFirstOntoQueue(TDynamicDirCachePage* aPage, TDynamicDirCachePage::TPageType aType)
sl@0:     {
sl@0:     ASSERT(aPage);
sl@0:     if (!aPage)
sl@0:         return KErrArgument;
sl@0: 
sl@0:     // page must be dequed first or it is active page
sl@0:     if (aPage->iType != TDynamicDirCachePage::EActivePage && aPage->iType != TDynamicDirCachePage::EUnknown)
sl@0:         {
sl@0:         ASSERT(0);
sl@0:         return KErrArgument;
sl@0:         }
sl@0: 
sl@0:     if (aType == TDynamicDirCachePage::ELocked)
sl@0:         {
sl@0:         iLockedQ.AddFirst(*aPage);
sl@0:         aPage->SetPageType(TDynamicDirCachePage::ELocked);
sl@0:         ++iLockedQCount;
sl@0:         }
sl@0:     else if (aType == TDynamicDirCachePage::EUnlocked)
sl@0:         {
sl@0:         iUnlockedQ.AddFirst(*aPage);
sl@0:         aPage->SetPageType(TDynamicDirCachePage::EUnlocked);
sl@0:         ++iUnlockedQCount;
sl@0:         }
sl@0:     else
sl@0:         {
sl@0:         ASSERT(0);
sl@0:         return KErrArgument;
sl@0:         }
sl@0: 
sl@0:     return KErrNone;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Remove a page from the lookup table, indexed by the starting media address of the page content.
sl@0: @param  aPagePos    the starting media position of the page to be removed.
sl@0: */
sl@0: TInt CDynamicDirCache::LookupTblRemove(TInt64 aPagePos)
sl@0:     {
sl@0:     if (aPagePos == 0)
sl@0:         {
sl@0:         return KErrNone;
sl@0:         }
sl@0: 
sl@0:     TInt r = iLookupTable.Remove(TLookupEntry(aPagePos, 0, NULL));
sl@0:     return r;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Insert a page to the lookup table, indexed by the starting media address of the page content.
sl@0: @param  aPagePos    the starting media position of the page to be inserted.
sl@0: */
sl@0: TInt CDynamicDirCache::LookupTblAdd(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage);
sl@0:     if (!aPage)
sl@0:         return KErrArgument;
sl@0: 
sl@0:     if (aPage->StartPos() == 0)
sl@0:         {
sl@0:         return KErrNone;
sl@0:         }
sl@0: 
sl@0:     TInt r = iLookupTable.Insert(TLookupEntry(aPage->StartPos(), iPageSizeInBytes, aPage));
sl@0:     return r;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Reset the media address of the page to 0, also invalidate the page.
sl@0: @param  aPage   the pointer of the page to be reset.
sl@0: */
sl@0: TInt CDynamicDirCache::ResetPagePos(TDynamicDirCachePage* aPage)
sl@0:     {
sl@0:     ASSERT(aPage);
sl@0:     if (!aPage)
sl@0:         return KErrArgument;
sl@0: 
sl@0:     aPage->ResetPos();
sl@0:     return KErrNone;
sl@0:     }
sl@0: 
sl@0: /**
sl@0: Search the lookup table to find the page start with a specific media address.
sl@0: @param  aPos    the starting media address to be searched.
sl@0: */
sl@0: TDynamicDirCachePage* CDynamicDirCache::LookupTblFind(TInt64 aPos)
sl@0:     {
sl@0:     if (aPos == 0)
sl@0:         {
sl@0:         ASSERT(0);
sl@0:         return NULL;
sl@0:         }
sl@0: 
sl@0:     TLookupEntry* entry = iLookupTable.Find(TLookupEntry(aPos, 0, NULL));
sl@0:     if(entry)
sl@0:         {
sl@0:         ASSERT(entry->iPage->IsValid());
sl@0:         return entry->iPage;
sl@0:         }
sl@0: 
sl@0:     return NULL;
sl@0:     }