--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/iphook/inhook6/src/dstcache.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,526 @@
+// Copyright (c) 2004-2009 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:
+// dstcache.cpp - Destination cache implementation
+// Destination cache implementation
+//
+
+
+
+/**
+ @file dstcache.cpp
+*/
+
+#include <in6_dstcache.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <in6_dstcache_internal.h>
+#endif
+
+#ifdef NONSHARABLE_CLASS
+ NONSHARABLE_CLASS(CDestinationCache);
+#endif
+
+class CDestinationCache : public MDestinationCache, public CBase
+ {
+public:
+ CDestinationCache(TInt aKeyMode);
+// ~CDestinationCache(); // No explicit destructor needed, as far as I see
+
+ void ConstructL();
+
+ virtual void StoreL(const TInetAddr &aAddr, TCacheInfo &aInfo);
+
+ virtual const TCacheInfo *Find(const TInetAddr &aAddr);
+
+ virtual void RemoveL(const TInetAddr &aAddr);
+
+ virtual void SetL(const TInetAddr &aAddr, TUint aParIndex, TUint32 aValue);
+
+ virtual void Cleanup();
+
+ virtual inline void RemoveAll()
+ { iStorage.RemoveAll(); }
+
+ virtual inline void SetLifetime(TUint aLifetime) { iLifetime = aLifetime; }
+
+ virtual inline void SetMaxSize(TUint aMaxSize) { iMaxSize = aMaxSize; }
+
+ friend TBool IfIsExpired(const TCacheInfo &aCinfo, void *aCachePtr);
+
+ virtual TBool Match(const TInetAddr& aAddrA, const TInetAddr& aAddrB) const;
+
+private:
+ TBool IsExpired(const TCacheInfo &aCinfo) const;
+
+ TCacheHash iStorage; //< Hash table that stores the destination cache entries
+ TUint iLifetime; //< Lifetime of a destination cache entry (seconds)
+ TUint iMaxSize; //< Maximum memory used for cache entries (bytes)
+ TUint iMemUsed; //< Current total memory used by cache entries
+
+ // Key Mode selected when instantiating this class
+ THashKeyIp6::TKeyMode iKeyMode;
+ };
+
+
+EXPORT_C MDestinationCache *MDestinationCache::CreateDstCache(TInt aKeyMode)
+ {
+ CDestinationCache *dcache = new CDestinationCache(aKeyMode);
+ if (dcache)
+ {
+ TInt err = KErrNone;
+ // coverity[leave_without_push]
+ // if ConstructL() leaves, the leave code is trapped in "err" and proper cleanup is done by freeing the memory alocated to "dcache"
+ TRAP(err, dcache->ConstructL());
+ if (err != KErrNone)
+ {
+ delete dcache;
+ dcache = NULL;
+ }
+ }
+ return dcache;
+ }
+
+
+CDestinationCache::CDestinationCache(TInt aKeyMode) : CBase(),
+ iStorage(KCacheHashSize),
+ iLifetime(KDstCacheLifetime),
+ iMaxSize(KDstCacheMaxSize)
+ {
+ switch (aKeyMode)
+ {
+ case 2:
+ iKeyMode = THashKeyIp6::EPerNet;
+ break;
+
+ default:
+ iKeyMode = THashKeyIp6::EPerHost;
+ break;
+
+ // EPerIface not yet implemented
+ }
+ }
+
+
+void CDestinationCache::ConstructL()
+ {
+ iStorage.ConstructL();
+ }
+
+
+void CDestinationCache::StoreL(const TInetAddr &aAddr, TCacheInfo &aInfo)
+ {
+ aInfo.iStoreTime.UniversalTime();
+ THashKeyIp6 k(iKeyMode, aAddr);
+
+ // Check that hash size does not exceed the given limit.
+ // If it is about to do so, clean up expired entries and try again.
+ // If it still fails, StoreL fails.
+ if (iMemUsed >= iMaxSize)
+ {
+ Cleanup(); // This reduces iMemUsed if there was expired entries
+ if (iMemUsed >= iMaxSize)
+ {
+ User::Leave(KErrNoMemory); // TODO: change error code
+ }
+ }
+
+ // Check if there is a non-expired entry in the cache. If incoming aInfo has any
+ // non-null entries, they replace the earlier cache entries. Entries that were
+ // not updated with this call remain unmodified in the cache.
+ // However, the store time is refreshed.
+ TCacheInfo *oldent = iStorage.Find(k);
+ if (oldent && !IsExpired(*oldent))
+ {
+ for (TUint i = 0; i < KNumCacheMetrics; i++)
+ {
+ if (aInfo.iMetrics[i] != 0)
+ oldent->iMetrics[i] = aInfo.iMetrics[i];
+ }
+ oldent->iStoreTime.UniversalTime();
+
+ return; // Hash-store method is not needed this time.
+ }
+
+ TUint mem = iStorage.StoreL(k, aInfo);
+ iMemUsed += mem;
+ }
+
+
+void CDestinationCache::SetL(const TInetAddr &aAddr, TUint aParIndex, TUint32 aValue)
+ {
+ TCacheInfo *infoptr;
+
+ // Overwrite existing value if it exists, otherwise create a new entry in cache
+ THashKeyIp6 k(iKeyMode, aAddr);
+ infoptr = iStorage.Find(k);
+
+ if (infoptr && !IsExpired(*infoptr))
+ {
+ infoptr->iMetrics[aParIndex] = aValue;
+ infoptr->iStoreTime.UniversalTime();
+ }
+ else
+ {
+ TCacheInfo newinfo;
+ newinfo.ClearAll();
+ newinfo.iMetrics[aParIndex] = aValue;
+ StoreL(aAddr, newinfo);
+ }
+ }
+
+
+const TCacheInfo *CDestinationCache::Find(const TInetAddr &aAddr)
+ {
+ THashKeyIp6 k(iKeyMode, aAddr);
+ TCacheInfo *cinfo = iStorage.Find(k);
+
+ // Check whether the cache entry is recent enough. If not, return NULL.
+ // However, we don't delete an expired entry from cache. It remains as a placeholder
+ // for future equivalent entries.
+ if (!cinfo || IsExpired(*cinfo))
+ {
+ return NULL;
+ }
+
+ return cinfo;
+ }
+
+
+void CDestinationCache::RemoveL(const TInetAddr &aAddr)
+ {
+ THashKeyIp6 k(iKeyMode, aAddr);
+ TUint mem = iStorage.RemoveL(k);
+ iMemUsed -= mem;
+ }
+
+
+// Same as CDestinationCache::IsExpired(), but this is to be used with RemoveIf() call.
+TBool IfIsExpired(const TCacheInfo &aCinfo, void *aCachePtr)
+ {
+ if (!aCachePtr)
+ {
+ return EFalse;
+ }
+ return ((CDestinationCache *)aCachePtr)->IsExpired(aCinfo);
+ }
+
+
+void CDestinationCache::Cleanup()
+ {
+ iMemUsed -= iStorage.RemoveIf(IfIsExpired, this);
+ }
+
+
+TBool CDestinationCache::IsExpired(const TCacheInfo &aCinfo) const
+ {
+ TTime now;
+ TTimeIntervalSeconds secs;
+
+ now.UniversalTime();
+ if (now.SecondsFrom(aCinfo.iStoreTime, secs) != KErrNone)
+ {
+ // Overflow or something. Consider it as expired entry.
+ return ETrue;
+ }
+
+ if ((TUint)secs.Int() > iLifetime)
+ {
+ // Expired
+ return ETrue;
+ }
+
+ return EFalse;
+ }
+
+
+TBool CDestinationCache::Match(const TInetAddr& aAddrA, const TInetAddr& aAddrB) const
+ {
+ THashKeyIp6 ka(iKeyMode, aAddrA);
+ THashKeyIp6 kb(iKeyMode, aAddrB);
+
+ return ka.IsEqual(kb);
+ }
+
+
+// --- THashTable methods ---
+
+template <class K, class V>
+THashTable<K,V>::THashTable(TUint aSize) : iTable(NULL), iSize(aSize)
+ {
+ }
+
+
+template <class K, class V>
+THashTable<K,V>::~THashTable()
+ {
+ RemoveAll();
+ delete [] iTable;
+ }
+
+
+template <class K, class V>
+void THashTable<K,V>::ConstructL()
+ {
+ iTable = new TChain<K,V>[iSize];
+ if (!iTable)
+ {
+ User::Leave(KErrNoMemory);
+ }
+
+ for (TUint i = 0; i < iSize; i++)
+ {
+ iTable[i].iNext = NULL;
+ }
+ }
+
+
+template <class K, class V>
+TUint THashTable<K,V>::StoreL(MHashKey& aKey, V& aValue)
+ {
+ if (!iTable)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ TUint memused = 0;
+ TInt idx = aKey.ToInt() % iSize;
+ TChain<K,V> *ptr = &iTable[idx], *newitem = NULL;
+ while (ptr->iNext)
+ {
+ if (ptr->iNext->iKey.IsEqual(aKey))
+ {
+ // The conventional mode would be:
+ // User::Leave(KErrAlreadyExists)
+ // ...however, due to the lazy garbage collection mode adopted, we overwrite
+ // the existing entry
+ newitem = ptr->iNext;
+ break;
+ }
+ ptr = ptr->iNext;
+ }
+
+ // If the key wasn't stored earlier, allocate space for new data item.
+ // Allocations from heap is not always a very popular idea, but these events are
+ // expected to occur quite rarely.
+ if (!newitem)
+ {
+ newitem = new(ELeave) TChain<K,V>;
+
+ if (!newitem)
+ {
+ User::Leave(KErrNoMemory);
+ }
+
+ newitem->iKey = *((K*) &aKey);
+ newitem->iNext = NULL;
+ ptr->iNext = newitem;
+ memused = sizeof(TChain<K,V>);
+ }
+
+ newitem->iValue = aValue;
+
+ /* Dynamically allocated memory newitem has been assigned to ptr->iNext and managed through the list.
+ So CleanupStack::PopAndDestroy() will deallocate that memory. But, Coverity has misinterpreted it an issue.*/
+ // coverity [SYMBIAN.CLEANUP STACK]
+ // coverity [memory_leak]
+ return memused;
+ }
+
+
+template <class K, class V>
+V* THashTable<K,V>::Find(MHashKey& aKey)
+ {
+ if (!iTable) return NULL;
+
+ TInt idx = aKey.ToInt() % iSize;
+ TChain<K,V> *ptr = &iTable[idx];
+ while (ptr->iNext)
+ {
+ if (ptr->iNext->iKey.IsEqual(aKey))
+ {
+ return &ptr->iNext->iValue;
+ }
+ ptr = ptr->iNext;
+ }
+
+ // Not found
+ return NULL;
+ }
+
+
+template <class K, class V>
+TUint THashTable<K,V>::RemoveL(MHashKey& aKey)
+ {
+ if (!iTable)
+ {
+ User::Leave(KErrGeneral);
+ }
+
+ TInt idx = aKey.ToInt() % iSize;
+ TChain<K,V> *ptr = &iTable[idx];
+ while (ptr->iNext)
+ {
+ if (ptr->iNext->iKey.IsEqual(aKey))
+ {
+ TChain<K,V> *removed = ptr->iNext;
+ ptr->iNext = removed->iNext;
+ delete removed;
+ return sizeof(TChain<K,V>);
+ }
+ }
+
+ // Key was not found
+ User::Leave(KErrNotFound);
+ return 0;
+ }
+
+
+template <class K, class V>
+TUint THashTable<K,V>::Length()
+ {
+ if (!iTable) return 0;
+
+ TUint length = 0;
+
+ for (TInt idx = 0; idx < iSize; idx++)
+ {
+ TChain<K,V> *ptr = &iTable[idx];
+ while (ptr->iNext)
+ {
+ length++;
+ ptr = ptr->iNext;
+ }
+ }
+ return length;
+ }
+
+
+template <class K, class V>
+TUint THashTable<K,V>::RemoveIf( TBool (*aRemoveCriteria)(const V&, void *),
+ void *aDataObject)
+ {
+ if (!iTable)
+ {
+ return 0;
+ }
+
+ TUint memfreed = 0;
+ TChain<K,V> *ptr = NULL;
+ for (TUint idx = 0; idx < iSize; idx++)
+ {
+ ptr = &iTable[idx];
+ while (ptr->iNext)
+ {
+ if (aRemoveCriteria(ptr->iNext->iValue, aDataObject))
+ {
+ TChain<K,V> *removed = ptr->iNext;
+ ptr->iNext = removed->iNext;
+ delete removed;
+ memfreed += sizeof(TChain<K,V>);
+ }
+ ptr = ptr->iNext;
+ }
+ }
+
+ return memfreed;
+ }
+
+
+template <class K, class V>
+void THashTable<K,V>::RemoveAll()
+ {
+ if (iTable != NULL)
+ {
+ TChain<K,V> *ptr = NULL, *last = NULL;
+ for (TUint i = 0; i < iSize; i++)
+ {
+ // Remember that the first chain entry in &iTable[i] does not contain data.
+ // The data items start from the linked entries.
+ ptr = &iTable[i];
+ while (ptr)
+ {
+ ptr = ptr->iNext;
+ if (last) delete last;
+ last = ptr;
+ }
+ }
+ }
+ }
+
+
+THashKeyIp6::THashKeyIp6(TKeyMode aKeyMode, const TInetAddr& aAddr)
+ {
+ // TODO: retrieve key mode, and assign only a prefix instead of full address.
+ // Simple prefix length is not enough, because I'd like to use the same cache for
+ // both IPv4 and IPv6
+ iKeyMode = aKeyMode;
+ iAddress = aAddr.Ip6Address();
+ iScopeId = aAddr.Scope();
+ }
+
+
+TUint THashKeyIp6::ToInt()
+ {
+ if (iKeyMode == EPerNet)
+ {
+ if (iAddress.IsV4Mapped())
+ {
+ // IPv4: Network is considered a /24
+ return iAddress.u.iAddr32[3] >> 8;
+ }
+ else
+ {
+ // IPv6: Network is considered a /64.
+ // Return the least significant 32 bits from the network part.
+ return iAddress.u.iAddr32[1];
+ }
+ }
+ else // EPerHost and others (EPerIface not yet implemented)
+ {
+ // The least significant 32 bits is used as the int index
+ return iAddress.u.iAddr32[3];
+ }
+ }
+
+
+TBool THashKeyIp6::IsEqual(const MHashKey& aKey)
+ {
+ // casting is always risky business, but the user is required
+ // to use this method properly
+ const TIp6Addr addr2 = ((const THashKeyIp6&)aKey).Ip6Address();
+
+ // If scope id is different, match will always fail
+ // (until per interface mode is implemented)
+ if (iScopeId != ((const THashKeyIp6&)aKey).ScopeId())
+ {
+ return EFalse;
+ }
+
+ if (iKeyMode == EPerNet)
+ {
+ TInt match = iAddress.Match(addr2);
+ if (iAddress.IsV4Mapped() && match >= 96 + 24)
+ {
+ return ETrue;
+ }
+ else if (match >= 64)
+ {
+ return ETrue;
+ }
+ return EFalse;
+ }
+ else // EPerHost and others (EPerIface not yet implemented)
+ {
+ return iAddress.IsEqual(addr2);
+ }
+ }