phonebookui/Phonebook/Engine/src/CPbkEntryCache.cpp
changeset 0 e686773b3f54
equal deleted inserted replaced
-1:000000000000 0:e686773b3f54
       
     1 /*
       
     2 * Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *		Fixed-size cache of TPbkContactEntry instances
       
    16 *
       
    17 */
       
    18 
       
    19 
       
    20 // INCLUDE FILES
       
    21 #include "CPbkEntryCache.h"
       
    22 #include "CPbkContactEngine.h"
       
    23 #include "CPbkContactItem.h"
       
    24 #include <MPbkContactNameFormat.h>
       
    25 
       
    26 // MACROS
       
    27 #ifdef _DEBUG
       
    28     #define DEBUG_INC(var) ++(var)
       
    29 #else
       
    30     #define DEBUG_INC(var)
       
    31 #endif
       
    32 
       
    33 /// Unnamed namespace for local definitions
       
    34 namespace {
       
    35 
       
    36 // LOCAL CONSTANTS AND MACROS
       
    37 
       
    38 #ifdef _DEBUG
       
    39 enum TPanicCode
       
    40     {
       
    41     EPanicPostCond_Constructor = 1,
       
    42     EPanicPreCond_ConstructL,
       
    43     EPanicPostCond_ConstructL,
       
    44     EPanicPostCond_GetEntryL,
       
    45     EPanicPostCond_PreLoadL,
       
    46     EPanicPostCond_Find,
       
    47     EPanicInvariant1,
       
    48     EPanicInvariant2,
       
    49     EPanicInvariant3,
       
    50     EPanicInvariant4
       
    51     };
       
    52 
       
    53 void Panic(TInt aReason)
       
    54     {
       
    55     _LIT(KPanicText, "CPbkEntryCache");
       
    56     User::Panic(KPanicText, aReason);
       
    57     }
       
    58 #endif // _DEBUG
       
    59 
       
    60 } // namespace
       
    61 
       
    62 // ==================== MEMBER FUNCTIONS ====================
       
    63 
       
    64 inline CPbkEntryCache::CPbkEntryCache
       
    65         (CPbkContactEngine& aEngine) : 
       
    66     iEntryQueue(_FOFF(TEntry,iLink)), 
       
    67     iEngine(aEngine)
       
    68     // Other members are reset by CBase::operator new
       
    69     {
       
    70     iEntryLoader = this;
       
    71     __ASSERT_DEBUG(!iEntries && iEntryLoader, 
       
    72         Panic(EPanicPostCond_Constructor));
       
    73     }
       
    74 
       
    75 inline void CPbkEntryCache::ConstructL
       
    76         (TInt aCapacity)
       
    77     {
       
    78     __ASSERT_DEBUG(aCapacity > 0, Panic(EPanicPreCond_ConstructL));
       
    79     __ASSERT_DEBUG(!iEntries, Panic(EPanicPreCond_ConstructL));
       
    80 
       
    81     // Allocate one array block with all the cache entries
       
    82     iEntries = new(ELeave) TEntry[aCapacity];
       
    83     iCapacity = aCapacity;
       
    84 
       
    85     // Enqueue entries
       
    86     TEntry* const end = iEntries + iCapacity;
       
    87     for (TEntry* entry=iEntries; entry!=end; ++entry)
       
    88         {
       
    89         iEntryQueue.AddLast(*entry);
       
    90         }
       
    91 
       
    92     __ASSERT_DEBUG(iEntries, Panic(EPanicPostCond_ConstructL));
       
    93     __ASSERT_DEBUG(iCapacity == aCapacity, Panic(EPanicPostCond_ConstructL));
       
    94     __TEST_INVARIANT;
       
    95     }
       
    96 
       
    97 EXPORT_C CPbkEntryCache* CPbkEntryCache::NewL
       
    98         (CPbkContactEngine& aEngine, 
       
    99         TInt aCapacity)
       
   100     {
       
   101     CPbkEntryCache* self = new(ELeave) CPbkEntryCache(aEngine);
       
   102     CleanupStack::PushL(self);
       
   103     self->ConstructL(aCapacity);
       
   104     CleanupStack::Pop(self);
       
   105     return self;
       
   106     }
       
   107 
       
   108 CPbkEntryCache::~CPbkEntryCache()
       
   109     {
       
   110     __TEST_INVARIANT;
       
   111     for (TInt i = iCapacity-1; i >= 0; --i)
       
   112         {
       
   113         iEntries[i].iEntry.Destroy();
       
   114         }
       
   115     delete [] iEntries;
       
   116     }
       
   117 
       
   118 EXPORT_C TInt CPbkEntryCache::Capacity() const
       
   119     {
       
   120     __TEST_INVARIANT;
       
   121     return iCapacity;
       
   122     }
       
   123 
       
   124 EXPORT_C const TPbkContactEntry& CPbkEntryCache::GetEntryL
       
   125         (TContactItemId aId)
       
   126     {
       
   127     __TEST_INVARIANT;
       
   128 
       
   129     TPbkContactEntry* entry = Find(aId);
       
   130     if (!entry)
       
   131         {
       
   132         TEntry* victim = iEntryQueue.Last();
       
   133         entry = &victim->iEntry;
       
   134         iEntryLoader->LoadEntryL(*entry, iEngine, aId);
       
   135         Touch(*victim);
       
   136         DEBUG_INC(iPerfStats.iMissCount);
       
   137         }
       
   138     else
       
   139         {
       
   140         DEBUG_INC(iPerfStats.iHitCount);
       
   141         }
       
   142 
       
   143     __TEST_INVARIANT;
       
   144     __ASSERT_DEBUG(entry == DebugFind(aId), Panic(EPanicPostCond_GetEntryL));
       
   145     return (*entry);
       
   146     }
       
   147 
       
   148 EXPORT_C void CPbkEntryCache::PreloadL
       
   149         (TContactItemId aId)
       
   150     {
       
   151     __TEST_INVARIANT;
       
   152 
       
   153     if (!Find(aId))
       
   154         {
       
   155         TEntry* victim = iEntryQueue.Last();
       
   156         iEntryLoader->LoadEntryL(victim->iEntry, iEngine, aId);
       
   157         Touch(*victim);
       
   158         DEBUG_INC(iPerfStats.iMissCount);
       
   159         }
       
   160     else
       
   161         {
       
   162         DEBUG_INC(iPerfStats.iHitCount);
       
   163         }
       
   164 
       
   165     __ASSERT_DEBUG(DebugFind(aId), Panic(EPanicPostCond_PreLoadL));
       
   166     __TEST_INVARIANT;
       
   167     }
       
   168 
       
   169 EXPORT_C void CPbkEntryCache::Unload
       
   170         (TContactItemId aId)
       
   171     {
       
   172     __TEST_INVARIANT;
       
   173 
       
   174     TEntry* entry = FindEntry(aId);
       
   175     if (entry)
       
   176         {
       
   177         entry->iEntry.Reset();
       
   178         // Move entry to the end of the queue
       
   179         entry->iLink.Deque();
       
   180         iEntryQueue.AddLast(*entry);
       
   181         }
       
   182     }
       
   183 
       
   184 EXPORT_C void CPbkEntryCache::Flush()
       
   185     {
       
   186     TEntry* const end = iEntries + iCapacity;
       
   187     for (TEntry* entry = iEntries; entry != end; ++entry)
       
   188         {
       
   189         entry->iEntry.Reset();
       
   190         }
       
   191     }
       
   192 
       
   193 void CPbkEntryCache::LoadEntryL
       
   194             (TPbkContactEntry& aEntry, 
       
   195             CPbkContactEngine& aEngine, 
       
   196             TContactItemId aContactId)
       
   197     {
       
   198     CPbkContactItem* contact = aEngine.ReadMinimalContactLC(aContactId);
       
   199 
       
   200     // Reserve space for name
       
   201     MPbkContactNameFormat& nameFormatter = aEngine.ContactNameFormat();
       
   202     const TInt titleLength = nameFormatter.ContactListTitleLength(*contact);
       
   203     aEntry.ReserveNameMaxLengthL(titleLength);
       
   204 
       
   205     // Get the contact icon id
       
   206     const TPbkIconId iconId = contact->ContactIconIdL();
       
   207 
       
   208     // All resources have been pre-allocated -> assign data
       
   209     aEntry.SetContactItemId(aContactId);
       
   210     if (titleLength > 0)
       
   211         {
       
   212         TPtr namePtr = aEntry.NameBuf();
       
   213         nameFormatter.GetContactListTitle(*contact,namePtr);
       
   214         }
       
   215     else
       
   216         {
       
   217         aEntry.ResetName();
       
   218         }
       
   219     aEntry.SetIconId(iconId);
       
   220 
       
   221     // Cleanup
       
   222     CleanupStack::PopAndDestroy(contact);
       
   223     }
       
   224 
       
   225 /**
       
   226  * Returns a cache entry if present, NULL otherwise.
       
   227  */
       
   228 TPbkContactEntry* CPbkEntryCache::Find
       
   229         (TContactItemId aId)
       
   230     {
       
   231     __TEST_INVARIANT;
       
   232 
       
   233     TPbkContactEntry* entry = NULL;
       
   234     TEntry* queueEntry = FindEntry(aId);
       
   235     if (queueEntry)
       
   236         {
       
   237         // Entry found
       
   238         Touch(*queueEntry);
       
   239         entry = &(queueEntry->iEntry);
       
   240         }
       
   241 
       
   242     __ASSERT_DEBUG(!entry || 
       
   243         (entry->ContactId()==aId && (&(iEntryQueue.First()->iEntry) == entry)),
       
   244         Panic(EPanicPostCond_Find));
       
   245     __TEST_INVARIANT;
       
   246     return entry;
       
   247     }
       
   248 
       
   249 /**
       
   250  * Call when an entry is accessed (="touched"). Increases the entry's priority
       
   251  * for keeping it in this cache.
       
   252  */
       
   253 void CPbkEntryCache::Touch(TEntry& aEntry)
       
   254     {
       
   255     aEntry.iLink.Deque();
       
   256     iEntryQueue.AddFirst(aEntry);
       
   257     }
       
   258 
       
   259 CPbkEntryCache::TEntry* CPbkEntryCache::FindEntry
       
   260         (TContactItemId aId) const
       
   261     {
       
   262     // Search the cache brute-force. Searching in usage order using 
       
   263     // iEntryQueue is probably slower in modern cached processors due to
       
   264     // nonlocality of access.
       
   265     TEntry* const end = iEntries + iCapacity;
       
   266     for (TEntry* entry = iEntries; entry != end; ++entry)
       
   267         {
       
   268         if (entry->iEntry.ContactId() == aId)
       
   269             {
       
   270             return entry;
       
   271             }
       
   272         }
       
   273     return NULL;
       
   274     }
       
   275 
       
   276 #ifdef _DEBUG
       
   277 
       
   278 const TPbkContactEntry* CPbkEntryCache::DebugFind
       
   279         (TContactItemId aId) const
       
   280     {
       
   281     const TPbkContactEntry* entry = NULL;
       
   282     const TEntry* queueEntry = FindEntry(aId);
       
   283     if (queueEntry)
       
   284         {
       
   285         entry = &(queueEntry->iEntry);
       
   286         }
       
   287     return entry;
       
   288     }
       
   289 
       
   290 void CPbkEntryCache::__DbgTestInvariant() const
       
   291     {
       
   292     if (!iEntries)
       
   293         {
       
   294         // Partial construction
       
   295         return;
       
   296         }
       
   297 
       
   298     __ASSERT_ALWAYS(iCapacity > 0, Panic(EPanicInvariant1));
       
   299 
       
   300     TInt count = 0;
       
   301     TDblQueIter<TEntry> iter(CONST_CAST(CPbkEntryCache*,this)->iEntryQueue);
       
   302     const TEntry* queueEntry;
       
   303     while (TInt(queueEntry = iter++))
       
   304         {
       
   305         ++count;
       
   306         // Each entry should be in iEntries array
       
   307         __ASSERT_ALWAYS(queueEntry >= iEntries && 
       
   308             queueEntry < iEntries+iCapacity, 
       
   309             Panic(EPanicInvariant2));
       
   310         }
       
   311     
       
   312     // All the entries should be in the queue
       
   313     __ASSERT_ALWAYS(count == iCapacity, Panic(EPanicInvariant3));
       
   314 
       
   315     // There should not be duplicate non-null entries
       
   316     for (TInt i=0; i < iCapacity; ++i)
       
   317         {
       
   318         const TContactItemId cid = iEntries[i].iEntry.ContactId();
       
   319         if (cid != KNullContactId)
       
   320             {
       
   321             for (TInt j=i+1; j < iCapacity; ++j)
       
   322                 {
       
   323                 __ASSERT_ALWAYS(iEntries[j].iEntry.ContactId() != cid,
       
   324                     Panic(EPanicInvariant4));
       
   325                 }
       
   326             }
       
   327         }
       
   328     }
       
   329 
       
   330 #endif // _DEBUG
       
   331 
       
   332 // End of File