diff -r 000000000000 -r af10295192d8 networkprotocols/iphook/inhook6/src/dstcache.cpp --- /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 +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#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 +THashTable::THashTable(TUint aSize) : iTable(NULL), iSize(aSize) + { + } + + +template +THashTable::~THashTable() + { + RemoveAll(); + delete [] iTable; + } + + +template +void THashTable::ConstructL() + { + iTable = new TChain[iSize]; + if (!iTable) + { + User::Leave(KErrNoMemory); + } + + for (TUint i = 0; i < iSize; i++) + { + iTable[i].iNext = NULL; + } + } + + +template +TUint THashTable::StoreL(MHashKey& aKey, V& aValue) + { + if (!iTable) + { + User::Leave(KErrGeneral); + } + + TUint memused = 0; + TInt idx = aKey.ToInt() % iSize; + TChain *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; + + if (!newitem) + { + User::Leave(KErrNoMemory); + } + + newitem->iKey = *((K*) &aKey); + newitem->iNext = NULL; + ptr->iNext = newitem; + memused = sizeof(TChain); + } + + 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 +V* THashTable::Find(MHashKey& aKey) + { + if (!iTable) return NULL; + + TInt idx = aKey.ToInt() % iSize; + TChain *ptr = &iTable[idx]; + while (ptr->iNext) + { + if (ptr->iNext->iKey.IsEqual(aKey)) + { + return &ptr->iNext->iValue; + } + ptr = ptr->iNext; + } + + // Not found + return NULL; + } + + +template +TUint THashTable::RemoveL(MHashKey& aKey) + { + if (!iTable) + { + User::Leave(KErrGeneral); + } + + TInt idx = aKey.ToInt() % iSize; + TChain *ptr = &iTable[idx]; + while (ptr->iNext) + { + if (ptr->iNext->iKey.IsEqual(aKey)) + { + TChain *removed = ptr->iNext; + ptr->iNext = removed->iNext; + delete removed; + return sizeof(TChain); + } + } + + // Key was not found + User::Leave(KErrNotFound); + return 0; + } + + +template +TUint THashTable::Length() + { + if (!iTable) return 0; + + TUint length = 0; + + for (TInt idx = 0; idx < iSize; idx++) + { + TChain *ptr = &iTable[idx]; + while (ptr->iNext) + { + length++; + ptr = ptr->iNext; + } + } + return length; + } + + +template +TUint THashTable::RemoveIf( TBool (*aRemoveCriteria)(const V&, void *), + void *aDataObject) + { + if (!iTable) + { + return 0; + } + + TUint memfreed = 0; + TChain *ptr = NULL; + for (TUint idx = 0; idx < iSize; idx++) + { + ptr = &iTable[idx]; + while (ptr->iNext) + { + if (aRemoveCriteria(ptr->iNext->iValue, aDataObject)) + { + TChain *removed = ptr->iNext; + ptr->iNext = removed->iNext; + delete removed; + memfreed += sizeof(TChain); + } + ptr = ptr->iNext; + } + } + + return memfreed; + } + + +template +void THashTable::RemoveAll() + { + if (iTable != NULL) + { + TChain *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); + } + }