networkprotocols/iphook/inhook6/src/dstcache.cpp
changeset 0 af10295192d8
--- /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);
+		}
+	}