symport/bafl/src/stringpoolimplementation.cpp
changeset 1 0a7b44b10206
child 2 806186ab5e14
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symport/bafl/src/stringpoolimplementation.cpp	Thu Jun 25 15:59:54 2009 +0100
@@ -0,0 +1,566 @@
+// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Symbian Foundation License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <e32base.h>
+#include "stringpoolimplementation.h"
+
+const TInt KMapGranularity=20;
+
+CStringPoolNode::~CStringPoolNode()
+	{
+	delete iDes;
+	}
+
+CStringPoolImplementation::CStringPoolImplementation() : iStringMapList(KMapGranularity, _FOFF(TStringIdMap, iSourceTableVal)), iStringMapListReverse(KMapGranularity, _FOFF(TStringIdMap, iTargetTableVal))
+	{
+	}
+
+CStringPoolImplementation::~CStringPoolImplementation()
+	{
+	// Look for non-expirable strings
+	TInt i;
+	for (i = 0; i < KHashModulo; i++ )
+		{
+		DeleteUndeletableStrings(iCIHashTable, i);
+		DeleteUndeletableStrings(iCSHashTable, i);
+		}
+#ifdef _DEBUG
+
+	__LOG(_L8(":Closing String Pool.\n"))
+		TBool leaksFound = EFalse;
+	// Check that the string pool is empty, or more accurately that
+	// everything in it is a pre-loaded string
+	for (i = 0; i < KHashModulo; i++ )
+		{
+		if (iCIHashTable[i])
+			{
+			for (TInt j = 0; j < iCIHashTable[i]->Count(); j++)
+				{
+				if (!StringUtils::IsTableEntry(iCIHashTable[i]->At(j).iVal))
+					{
+					if (!leaksFound)
+						{
+						__LOG(_L8("The following strings were leaked through not being Closed:\n"))
+						leaksFound = ETrue;
+						}
+
+					// Get the problem string
+					__LOG((reinterpret_cast<CStringPoolNode*>
+							(iCIHashTable[i]->At(j).iVal & KTokenToNode))->iDes->Des());
+					}
+				}
+			}
+		if (iCSHashTable[i])
+			{
+			for (TInt j = 0; j < iCSHashTable[i]->Count(); j++)
+				{
+				if (!StringUtils::IsTableEntry(iCSHashTable[i]->At(j).iVal))
+					{
+					if (!leaksFound)
+						{
+						__LOG(_L8("The following strings were leaked through not being Closed:\n"))
+						leaksFound = ETrue;
+						}
+
+					// Get the problem string
+					__LOG((reinterpret_cast<CStringPoolNode*>(
+							iCSHashTable[i]->At(j).iVal & KTokenToNode))->iDes->Des());
+					}
+				}
+			}
+		if (leaksFound)
+			__DEBUGGER();
+		}
+
+	if (!leaksFound)
+		__LOG(_L8("No leakages were detected\n"));
+
+#endif //_DEBUG
+	for (TInt ii = 0; ii < KHashModulo; ii++)
+		{
+		delete iCIHashTable[ii];
+		delete iCSHashTable[ii];
+		}
+
+	iTablePtrs.Close();
+	iStringMapList.Close();
+	iStringMapListReverse.Close();
+	iRollbackMapList.Close();
+	iRollbackHashListCS.Close();
+	iRollbackHashListCI.Close();
+	// Notify the external users of the StringPool that the object is getting closed
+	TInt cBCounter = iCallBacks.Count();
+	if(cBCounter>0)
+		{
+		while (--cBCounter>=0)
+			{
+			iCallBacks[cBCounter]->StringPoolClosing();
+			}
+		}
+	iCallBacks.Close();
+	}
+
+// Check for any undeletable string and delete them now
+void CStringPoolImplementation::DeleteUndeletableStrings(CArrayFixSeg<RStringTokenEither>* aArray[KHashModulo], TInt i)
+	{
+	if (aArray[i])
+		{
+		for (TInt j = 0; j < aArray[i]->Count(); ++j)
+			{
+			if (!StringUtils::IsTableEntry(aArray[i]->At(j).iVal))
+				{
+				CStringPoolNode* node= reinterpret_cast<CStringPoolNode*>(aArray[i]->At(j).iVal & KTokenToNode);
+				if (KMarkedForNoDeleted==node->iRefcount)
+					{
+					delete node;
+					aArray[i]->Delete(j);
+					j--;
+					}
+				}
+			}
+		}
+	}
+
+CStringPoolImplementation* CStringPoolImplementation::NewL()
+	{
+	CStringPoolImplementation* table = new (ELeave) CStringPoolImplementation();
+	return table;
+	}
+
+void CStringPoolImplementation::CleanupHashCS(TAny* aImplementation)
+	{
+	CStringPoolImplementation* imp=reinterpret_cast<CStringPoolImplementation*>(aImplementation);
+	CleanUpHash(&imp->iRollbackHashListCS, imp->iCSHashTable);
+	}
+
+void CStringPoolImplementation::CleanupHashCI(TAny* aImplementation)
+	{
+	CStringPoolImplementation* imp=reinterpret_cast<CStringPoolImplementation*>(aImplementation);
+	CleanUpHash(&imp->iRollbackHashListCI, imp->iCIHashTable);
+	}
+
+void CStringPoolImplementation::CleanUpHash(RPointerArray <RStringTokenEither>* aHashCleanup, CArrayFixSeg<RStringTokenEither>* aHash[KHashModulo])
+	{
+	if (aHashCleanup->Count()>0)
+		{
+		RStringTokenEither* token=(*aHashCleanup)[0];	// Get first entry
+		for (TInt i = 0; i < KHashModulo; i++ )
+			{
+			if (aHash[i])
+				{
+				for (TInt j = 0; j < aHash[i]->Count(); j++)
+					{
+					if (!StringUtils::IsTableEntry(aHash[i]->At(j).iVal))
+						{
+						if (aHash[i]->At(j).iVal==token->iVal)
+							{
+							CStringPoolNode* node= reinterpret_cast<CStringPoolNode*>(aHash[i]->At(j).iVal & KTokenToNode);
+							delete node;
+							aHash[i]->Delete(j);
+							aHashCleanup->Remove(0);
+							break;
+							}
+						}
+					}
+				}
+
+			}
+		}
+	}
+
+void CStringPoolImplementation::CleanupIdMap(TAny* aImplementation)
+	{
+	CStringPoolImplementation* imp=reinterpret_cast<CStringPoolImplementation*>(aImplementation);
+	if (imp->iRollbackMapList.Count()>0)
+		{
+		TStringIdMap* map=imp->iRollbackMapList[0];
+		TInt index=imp->iStringMapList.FindInUnsignedKeyOrder(*map);
+		imp->iRollbackMapList.Remove(0);
+		if (index!=KErrNotFound)
+			{
+			imp->iStringMapList.Remove(index);
+			}
+		index=imp->iStringMapListReverse.FindInUnsignedKeyOrder(*map);
+		if (index!=KErrNotFound)
+			{
+			for (TInt count=index;count<imp->iStringMapListReverse.Count();++count)
+				{
+				if (imp->iStringMapListReverse[count].iTargetTableVal==map->iTargetTableVal && imp->iStringMapListReverse[count].iSourceTableVal==map->iSourceTableVal)
+					{
+					imp->iStringMapListReverse.Remove(index);
+					}
+				}
+			}
+		}
+	}
+
+void CStringPoolImplementation::AddTableL(const TStringTable& aTable)
+	{
+	for (TInt count=0;count<iTablePtrs.Count();++count)	// check for adding the same table twice
+		{
+		if (iTablePtrs[count]==&aTable)
+			{
+			return;
+			}
+		}
+	User::LeaveIfError(iTablePtrs.Append(&aTable));	// Add the pointer to this table so we can keep track of the Table IDs. The table ID is the index in this array
+	//Is the table Case Sensitive or not?
+	TBool (*genericValFromIndex)(TInt, TUint16);
+	if (aTable.iCaseSensitive==1)
+		genericValFromIndex = StringUtils::ValFromIndex;
+	else
+		genericValFromIndex = StringUtils::ValFromIndexF;
+
+	CArrayFixSeg<RStringTokenEither>** hashTableToUse =
+	aTable.iCaseSensitive ? iCSHashTable :iCIHashTable ;
+	TInt cleanupCounter=0;
+	for (TUint i = 0; i < aTable.iCount; ++i)
+		{
+		const TStLitC8<1>* string=reinterpret_cast<const TStLitC8<1>* >(aTable.iTable[i]);
+
+		// Try to find the string in memory, maybe as a dynamic string or as a member of an another table
+		RStringTokenEither token=FindDes(*string, !aTable.iCaseSensitive);
+		if (!token.IsNull())
+			{
+			TStringIdMap map;
+			map.iSourceTableVal=StringUtils::ValFromIndex(i, (TInt16)(iTablePtrs.Count()-1),aTable.iCaseSensitive);
+			map.iTargetTableVal=token.iVal;
+
+			// Put on cleanup stack
+			User::LeaveIfError(iRollbackMapList.Append(&map));
+			TCleanupItem cleanup(CleanupIdMap, this);
+			CleanupStack::PushL(cleanup);
+			++cleanupCounter;
+
+			User::LeaveIfError(iStringMapList.InsertInUnsignedKeyOrder(map));
+
+			// Check if this is a link to a dynamic string
+			if (!StringUtils::IsTableEntry(token.iVal))
+				{
+				CStringPoolNode* node = StringUtils::NodePtr(token.iVal);
+				node->iRefcount=KMarkedForNoDeleted; // Make sure this string never gets deleted
+				}
+
+			// Now store the reverse array
+			User::LeaveIfError(iStringMapListReverse.InsertInUnsignedKeyOrderAllowRepeats(map));
+			}
+		else
+			{
+			TUint8 hash = static_cast<TUint8>(Hash(*string));
+			CArrayFixSeg<RStringTokenEither>* collisionList = hashTableToUse[hash];
+			if ( !collisionList )
+				//HashTableToUse now is used as list of all entry with the same hash
+				collisionList = hashTableToUse[hash] =
+				new (ELeave) CArrayFixSeg<RStringTokenEither>( 2 );
+			RStringTokenEither s;
+			s.iVal = genericValFromIndex(i, (TInt16)(iTablePtrs.Count()-1));
+
+			__LOG2(_L8("Table entry being added with hash %d, val %d"), hash, s.iVal);
+			__LOG(*reinterpret_cast<const TStLitC8<1>* >(aTable.iTable[i]));
+			// Put on cleanup stack
+			if (aTable.iCaseSensitive==1)
+				{
+				User::LeaveIfError(iRollbackHashListCS.Append(&s));
+				TCleanupItem cleanup(CleanupHashCS, this);
+				CleanupStack::PushL(cleanup);
+				}
+			else
+				{
+				User::LeaveIfError(iRollbackHashListCI.Append(&s));
+				TCleanupItem cleanup(CleanupHashCI, this);
+				CleanupStack::PushL(cleanup);
+				}
+
+			++cleanupCounter;
+			collisionList->AppendL(s);
+			}
+		}
+	CleanupStack::Pop(cleanupCounter);
+	iRollbackMapList.Reset();
+	iRollbackHashListCS.Reset();
+	iRollbackHashListCI.Reset();
+	}
+
+// Find FirstVal given duplicate val
+TInt32 CStringPoolImplementation::FindFirstValFromDuplicate(TInt32 aDuplicateVal) const
+	{
+	TStringIdMap map;
+	map.iSourceTableVal=aDuplicateVal;
+	TInt index=iStringMapList.FindInUnsignedKeyOrder(map);
+	if (index!=KErrNotFound)
+		return iStringMapList[index].iTargetTableVal;
+	else
+		return KErrNotFound;
+	}
+
+
+
+// Find table index Val given first val & table UID
+TInt CStringPoolImplementation::FindTableIndexFromFirstVal(TInt32 aFirstVal, TInt aTableUid) const
+	{
+	TStringIdMap map;
+	map.iTargetTableVal=aFirstVal;
+	TInt index=iStringMapListReverse.FindInUnsignedKeyOrder(map);
+	if (KErrNotFound==index)
+		return KErrNotFound;
+
+	for (TInt count=index;count<iStringMapListReverse.Count();++count)
+		{
+		if (iStringMapListReverse[count].iTargetTableVal==aFirstVal && StringUtils::TableUid(iStringMapListReverse[count].iSourceTableVal)==aTableUid)
+			{
+			return StringUtils::TableIndex(iStringMapListReverse[count].iSourceTableVal);
+			}
+		}
+		return KErrNotFound;
+	}
+
+
+// Find the UId for a given table
+TInt16 CStringPoolImplementation::TableUid(const TStringTable& aTable) const
+	{
+	for (TInt count=0; count<iTablePtrs.Count(); ++count)
+		{
+		if (iTablePtrs[count]==&aTable)
+			return (TInt16)count;
+		}
+	return KErrNotFound;
+	}
+
+// Find a reference to the table that first added the  string represented by aVal to the pool
+const TStringTable& CStringPoolImplementation::TableRef(TInt32 aVal) const
+	{
+	__ASSERT_DEBUG(aVal!=0, StringPoolPanic::Panic(StringPoolPanic::EIllegalUseOfNullString));
+	TInt16 tableUid=(TInt16)(aVal>>20);
+	const TStringTable* theTableRef=(iTablePtrs[tableUid]);
+	return  *theTableRef;
+	}
+
+// Find the descriptor for a given table and index
+const TDesC8& CStringPoolImplementation::TableLookup(TInt aIndex, TInt aTableUid) const
+	{
+	return *reinterpret_cast<const TStLitC8<1>*>(iTablePtrs[aTableUid]->iTable[aIndex]);
+	}
+
+
+// Lookup with allocating
+//
+RStringTokenEither
+CStringPoolImplementation::OpenL( const TDesC8& aAttributeName,
+								  TBool aCaseInsensitive)
+	{
+	// lookup the attribute
+	RStringTokenEither s(FindDes( aAttributeName , aCaseInsensitive));
+	if (!s.IsNull())
+		{
+		if (!StringUtils::IsTableEntry(s.iVal))
+			{
+
+			CStringPoolNode* node = StringUtils::NodePtr(s.iVal);
+			if (KMarkedForNoDeleted!=node->iRefcount)
+				node->iRefcount++;
+			__LOG1(_L8("String copied (during open). Count is now %d"), node->iRefcount);
+			__LOG(*node->iDes);
+			}
+		return s;
+		}
+
+	// create a new node at the end of the appropriate array
+	CStringPoolNode* newnode = new (ELeave) CStringPoolNode();
+	CleanupStack::PushL( newnode );
+	newnode->iDes = aAttributeName.AllocL();
+	newnode->iRefcount = 1;
+
+	TInt hash = Hash( aAttributeName );
+	CArrayFixSeg<RStringTokenEither>** hashTableToUse =
+		aCaseInsensitive ? iCIHashTable : iCSHashTable;
+	__LOG2(_L8("Newly added with hash value %d, node val 0x%x\n"), hash, newnode)
+	__LOG(aAttributeName);
+
+	newnode->iHash = static_cast<TUint8>(hash);
+	CArrayFixSeg<RStringTokenEither>* collisionList = hashTableToUse[hash];
+	if ( !collisionList )
+		collisionList = hashTableToUse[hash] = new (ELeave) CArrayFixSeg<RStringTokenEither>( 2 );
+
+	s.iVal = reinterpret_cast<TUint32>(newnode);
+	if (aCaseInsensitive)
+		s.iVal += 2;
+	collisionList->AppendL(s);
+
+	CleanupStack::Pop(); // newnode
+
+	return s;
+	}
+
+void CStringPoolImplementation::Close(RStringTokenEither aString)
+	{
+	if (StringUtils::IsTableEntry(aString.iVal))
+		return;
+
+	CStringPoolNode* node = StringUtils::NodePtr(aString.iVal);
+	if (KMarkedForNoDeleted == node->iRefcount)	// -1 means a non-expirable string
+		return;
+	if (--node->iRefcount == 0)
+		{
+		//this is  the last reference of this string
+		CArrayFixSeg<RStringTokenEither>** hashTableToUse =
+		aString.iVal & 2 ? iCIHashTable : iCSHashTable;
+
+		// Delete the node and delete the entry in the relevant collision list
+		CArrayFixSeg<RStringTokenEither>* collisionList = hashTableToUse[node->iHash];
+		TInt count = collisionList->Count();
+		for (TInt i = 0; i < count; i++)
+			{
+			if (collisionList->At(i) == aString)
+				{
+				// Log the fact that a string reference is about to die...
+				__LOG1(_L8("Removing string with hash value %d\n"), node->iHash)
+				__LOG(node->iDes->Des());
+				collisionList->Delete(i);
+				break;
+				}
+			}
+		delete node;
+		}
+	else
+		{
+		__LOG1(_L8("String closed. Count is now %d"),
+			   node->iRefcount);
+		__LOG(node->iDes->Des());
+		}
+	}
+
+void CStringPoolImplementation::IncrementCount(RStringTokenEither aString)
+	{
+	if (StringUtils::IsTableEntry(aString.iVal))
+		return;
+	CStringPoolNode* node = StringUtils::NodePtr(aString.iVal);
+	if (KMarkedForNoDeleted!=node->iRefcount)
+		node->iRefcount++;
+	__LOG1(_L8("String copied. Count is now %d"), node->iRefcount);
+	__LOG(*node->iDes);
+	}
+
+// Very simple case-sensitive comparison. We can assume that the
+// strings are the same length, and we only care if the strings are
+// the same. (Unlike normal comparison functions that also tell you
+// which one is 'smaller')
+TBool CStringPoolImplementation::CompareCS(const TDesC8& s1, const TDesC8& s2)
+	{
+	const TUint8* ptr1 = s1.Ptr();
+	const TUint8* ptr2 = s2.Ptr();
+	const TUint8* stop = &ptr1[s1.Length()];
+	for (; ptr1 < stop; ptr1++,ptr2++)
+		{
+		if (*ptr1 != *ptr2)
+			return EFalse;
+		}
+	return ETrue;
+	}
+
+// Note that the hash function must generate the same hash values for
+// strings that differ by case. If changing the algorithm here make
+// sure this is still true.
+TBool CStringPoolImplementation::CompareCI(const TDesC8& s1, const TDesC8& s2)
+	{
+	const TUint8* ptr1 = s1.Ptr();
+	const TUint8* ptr2 = s2.Ptr();
+	const TUint8* stop = &ptr1[s1.Length()];
+	for (; ptr1 < stop; ptr1++,ptr2++)
+		{
+		if (*ptr1 != *ptr2)
+			{
+			// They're not exactly the same; see if they differ only
+			// by case. If one character is a letter, we can do a
+			// comparison ignoring bit 5 in both cases. If that
+			// matches, they are the same.
+			if (!((*ptr1 & KCaseInsensitive) == (*ptr2 & KCaseInsensitive) &&
+				  (*ptr1 >= 'A' && *ptr1 <= 'Z' ||
+				   *ptr1 >= 'a' && *ptr1 <= 'z')))
+				return EFalse;
+			}
+		}
+	return ETrue;
+	}
+
+// Find the given descriptor in the hash table
+//
+RStringTokenEither
+CStringPoolImplementation::FindDes( const TDesC8& aAttributeName, TBool aCaseInsensitive)
+	{
+	CArrayFixSeg<RStringTokenEither>** hashTableToUse =
+		aCaseInsensitive ? iCIHashTable : iCSHashTable;
+	CArrayFixSeg<RStringTokenEither>* collisionList =hashTableToUse[Hash(aAttributeName)];
+	RStringPool pool;
+	TBool (*compareFunction)(const TDesC8&, const TDesC8&);
+	if (aCaseInsensitive)
+		compareFunction = CompareCI;
+	else
+		compareFunction = CompareCS;
+	pool.iImplementation = this;
+	if ( collisionList )
+		{
+		TInt length=aAttributeName.Length();
+		TInt count = collisionList->Count();
+		for ( TInt i = 0; i < count; i++ )
+			{
+			RStringTokenEither token = collisionList->At(i);
+			RStringEither s(this, token);
+			const TDesC8& string = s.DesC();
+			if ( string.Length()==length &&
+				 (*compareFunction)(aAttributeName, string))
+				return token;
+			}
+		}
+	return RStringTokenEither();
+	}
+
+
+// Generate a hash value
+//
+TUint CStringPoolImplementation::Hash( const TDesC8& aDes ) const
+	{
+	// We ignore bit 5, which is a crude way of making the hash case
+	// insensitive. This means that things that might differ only by
+	// case end up in the same bucket, and we can then worry about
+	// whether they're really the same later.
+	TInt len=aDes.Length();
+	TUint hash = 0;
+	const TUint8* ptr=aDes.Ptr();
+	for ( TInt i = 0; i < len; i++ )
+		hash = 131*hash + (*ptr++ & KCaseInsensitive);
+	return hash % KHashModulo;
+	}
+
+TInt StringUtils::ValFromIndex(TInt aIndex, TUint16 aTableId)
+	{
+	return (aTableId << 20) + (aIndex << 2) + 1;
+	}
+
+TInt StringUtils::ValFromIndexF(TInt aIndex, TUint16 aTableId)
+	{
+	return (aTableId << 20) + (aIndex << 2) + 3;
+	}
+
+TInt StringUtils::ValFromIndex(TInt aIndex, TUint16 aTableId, TBool aCaseSensitive)
+	{
+	if (aCaseSensitive)
+		return ValFromIndex(aIndex, aTableId);
+	else
+		return ValFromIndexF(aIndex, aTableId);
+	}
+void  CStringPoolImplementation::AddCallBackL( MStringPoolCloseCallBack& aCallBack)
+	{
+	User::LeaveIfError(iCallBacks.Append(&aCallBack));
+	}