phonebookui/Phonebook/Engine/src/CPbkEntryCache.cpp
changeset 0 e686773b3f54
--- /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