diff -r 000000000000 -r e686773b3f54 phonebookui/Phonebook/Engine/src/CPbkEntryCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookui/Phonebook/Engine/src/CPbkEntryCache.cpp Tue Feb 02 10:12:17 2010 +0200 @@ -0,0 +1,332 @@ +/* +* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* Fixed-size cache of TPbkContactEntry instances +* +*/ + + +// INCLUDE FILES +#include "CPbkEntryCache.h" +#include "CPbkContactEngine.h" +#include "CPbkContactItem.h" +#include + +// MACROS +#ifdef _DEBUG + #define DEBUG_INC(var) ++(var) +#else + #define DEBUG_INC(var) +#endif + +/// Unnamed namespace for local definitions +namespace { + +// LOCAL CONSTANTS AND MACROS + +#ifdef _DEBUG +enum TPanicCode + { + EPanicPostCond_Constructor = 1, + EPanicPreCond_ConstructL, + EPanicPostCond_ConstructL, + EPanicPostCond_GetEntryL, + EPanicPostCond_PreLoadL, + EPanicPostCond_Find, + EPanicInvariant1, + EPanicInvariant2, + EPanicInvariant3, + EPanicInvariant4 + }; + +void Panic(TInt aReason) + { + _LIT(KPanicText, "CPbkEntryCache"); + User::Panic(KPanicText, aReason); + } +#endif // _DEBUG + +} // namespace + +// ==================== MEMBER FUNCTIONS ==================== + +inline CPbkEntryCache::CPbkEntryCache + (CPbkContactEngine& aEngine) : + iEntryQueue(_FOFF(TEntry,iLink)), + iEngine(aEngine) + // Other members are reset by CBase::operator new + { + iEntryLoader = this; + __ASSERT_DEBUG(!iEntries && iEntryLoader, + Panic(EPanicPostCond_Constructor)); + } + +inline void CPbkEntryCache::ConstructL + (TInt aCapacity) + { + __ASSERT_DEBUG(aCapacity > 0, Panic(EPanicPreCond_ConstructL)); + __ASSERT_DEBUG(!iEntries, Panic(EPanicPreCond_ConstructL)); + + // Allocate one array block with all the cache entries + iEntries = new(ELeave) TEntry[aCapacity]; + iCapacity = aCapacity; + + // Enqueue entries + TEntry* const end = iEntries + iCapacity; + for (TEntry* entry=iEntries; entry!=end; ++entry) + { + iEntryQueue.AddLast(*entry); + } + + __ASSERT_DEBUG(iEntries, Panic(EPanicPostCond_ConstructL)); + __ASSERT_DEBUG(iCapacity == aCapacity, Panic(EPanicPostCond_ConstructL)); + __TEST_INVARIANT; + } + +EXPORT_C CPbkEntryCache* CPbkEntryCache::NewL + (CPbkContactEngine& aEngine, + TInt aCapacity) + { + CPbkEntryCache* self = new(ELeave) CPbkEntryCache(aEngine); + CleanupStack::PushL(self); + self->ConstructL(aCapacity); + CleanupStack::Pop(self); + return self; + } + +CPbkEntryCache::~CPbkEntryCache() + { + __TEST_INVARIANT; + for (TInt i = iCapacity-1; i >= 0; --i) + { + iEntries[i].iEntry.Destroy(); + } + delete [] iEntries; + } + +EXPORT_C TInt CPbkEntryCache::Capacity() const + { + __TEST_INVARIANT; + return iCapacity; + } + +EXPORT_C const TPbkContactEntry& CPbkEntryCache::GetEntryL + (TContactItemId aId) + { + __TEST_INVARIANT; + + TPbkContactEntry* entry = Find(aId); + if (!entry) + { + TEntry* victim = iEntryQueue.Last(); + entry = &victim->iEntry; + iEntryLoader->LoadEntryL(*entry, iEngine, aId); + Touch(*victim); + DEBUG_INC(iPerfStats.iMissCount); + } + else + { + DEBUG_INC(iPerfStats.iHitCount); + } + + __TEST_INVARIANT; + __ASSERT_DEBUG(entry == DebugFind(aId), Panic(EPanicPostCond_GetEntryL)); + return (*entry); + } + +EXPORT_C void CPbkEntryCache::PreloadL + (TContactItemId aId) + { + __TEST_INVARIANT; + + if (!Find(aId)) + { + TEntry* victim = iEntryQueue.Last(); + iEntryLoader->LoadEntryL(victim->iEntry, iEngine, aId); + Touch(*victim); + DEBUG_INC(iPerfStats.iMissCount); + } + else + { + DEBUG_INC(iPerfStats.iHitCount); + } + + __ASSERT_DEBUG(DebugFind(aId), Panic(EPanicPostCond_PreLoadL)); + __TEST_INVARIANT; + } + +EXPORT_C void CPbkEntryCache::Unload + (TContactItemId aId) + { + __TEST_INVARIANT; + + TEntry* entry = FindEntry(aId); + if (entry) + { + entry->iEntry.Reset(); + // Move entry to the end of the queue + entry->iLink.Deque(); + iEntryQueue.AddLast(*entry); + } + } + +EXPORT_C void CPbkEntryCache::Flush() + { + TEntry* const end = iEntries + iCapacity; + for (TEntry* entry = iEntries; entry != end; ++entry) + { + entry->iEntry.Reset(); + } + } + +void CPbkEntryCache::LoadEntryL + (TPbkContactEntry& aEntry, + CPbkContactEngine& aEngine, + TContactItemId aContactId) + { + CPbkContactItem* contact = aEngine.ReadMinimalContactLC(aContactId); + + // Reserve space for name + MPbkContactNameFormat& nameFormatter = aEngine.ContactNameFormat(); + const TInt titleLength = nameFormatter.ContactListTitleLength(*contact); + aEntry.ReserveNameMaxLengthL(titleLength); + + // Get the contact icon id + const TPbkIconId iconId = contact->ContactIconIdL(); + + // All resources have been pre-allocated -> assign data + aEntry.SetContactItemId(aContactId); + if (titleLength > 0) + { + TPtr namePtr = aEntry.NameBuf(); + nameFormatter.GetContactListTitle(*contact,namePtr); + } + else + { + aEntry.ResetName(); + } + aEntry.SetIconId(iconId); + + // Cleanup + CleanupStack::PopAndDestroy(contact); + } + +/** + * Returns a cache entry if present, NULL otherwise. + */ +TPbkContactEntry* CPbkEntryCache::Find + (TContactItemId aId) + { + __TEST_INVARIANT; + + TPbkContactEntry* entry = NULL; + TEntry* queueEntry = FindEntry(aId); + if (queueEntry) + { + // Entry found + Touch(*queueEntry); + entry = &(queueEntry->iEntry); + } + + __ASSERT_DEBUG(!entry || + (entry->ContactId()==aId && (&(iEntryQueue.First()->iEntry) == entry)), + Panic(EPanicPostCond_Find)); + __TEST_INVARIANT; + return entry; + } + +/** + * Call when an entry is accessed (="touched"). Increases the entry's priority + * for keeping it in this cache. + */ +void CPbkEntryCache::Touch(TEntry& aEntry) + { + aEntry.iLink.Deque(); + iEntryQueue.AddFirst(aEntry); + } + +CPbkEntryCache::TEntry* CPbkEntryCache::FindEntry + (TContactItemId aId) const + { + // Search the cache brute-force. Searching in usage order using + // iEntryQueue is probably slower in modern cached processors due to + // nonlocality of access. + TEntry* const end = iEntries + iCapacity; + for (TEntry* entry = iEntries; entry != end; ++entry) + { + if (entry->iEntry.ContactId() == aId) + { + return entry; + } + } + return NULL; + } + +#ifdef _DEBUG + +const TPbkContactEntry* CPbkEntryCache::DebugFind + (TContactItemId aId) const + { + const TPbkContactEntry* entry = NULL; + const TEntry* queueEntry = FindEntry(aId); + if (queueEntry) + { + entry = &(queueEntry->iEntry); + } + return entry; + } + +void CPbkEntryCache::__DbgTestInvariant() const + { + if (!iEntries) + { + // Partial construction + return; + } + + __ASSERT_ALWAYS(iCapacity > 0, Panic(EPanicInvariant1)); + + TInt count = 0; + TDblQueIter iter(CONST_CAST(CPbkEntryCache*,this)->iEntryQueue); + const TEntry* queueEntry; + while (TInt(queueEntry = iter++)) + { + ++count; + // Each entry should be in iEntries array + __ASSERT_ALWAYS(queueEntry >= iEntries && + queueEntry < iEntries+iCapacity, + Panic(EPanicInvariant2)); + } + + // All the entries should be in the queue + __ASSERT_ALWAYS(count == iCapacity, Panic(EPanicInvariant3)); + + // There should not be duplicate non-null entries + for (TInt i=0; i < iCapacity; ++i) + { + const TContactItemId cid = iEntries[i].iEntry.ContactId(); + if (cid != KNullContactId) + { + for (TInt j=i+1; j < iCapacity; ++j) + { + __ASSERT_ALWAYS(iEntries[j].iEntry.ContactId() != cid, + Panic(EPanicInvariant4)); + } + } + } + } + +#endif // _DEBUG + +// End of File