--- /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 <MPbkContactNameFormat.h>
+
+// 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<TEntry> 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