diff -r c55016431358 -r 0a7b44b10206 symport/bafl/src/stringpoolimplementation.cpp --- /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 +#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 + (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( + 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* 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(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(aImplementation); + CleanUpHash(&imp->iRollbackHashListCS, imp->iCSHashTable); + } + +void CStringPoolImplementation::CleanupHashCI(TAny* aImplementation) + { + CStringPoolImplementation* imp=reinterpret_cast(aImplementation); + CleanUpHash(&imp->iRollbackHashListCI, imp->iCIHashTable); + } + +void CStringPoolImplementation::CleanUpHash(RPointerArray * aHashCleanup, CArrayFixSeg* 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(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(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;countiStringMapListReverse.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** hashTableToUse = + aTable.iCaseSensitive ? iCSHashTable :iCIHashTable ; + TInt cleanupCounter=0; + for (TUint i = 0; i < aTable.iCount; ++i) + { + const TStLitC8<1>* string=reinterpret_cast* >(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(Hash(*string)); + CArrayFixSeg* collisionList = hashTableToUse[hash]; + if ( !collisionList ) + //HashTableToUse now is used as list of all entry with the same hash + collisionList = hashTableToUse[hash] = + new (ELeave) CArrayFixSeg( 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* >(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>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*>(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** 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(hash); + CArrayFixSeg* collisionList = hashTableToUse[hash]; + if ( !collisionList ) + collisionList = hashTableToUse[hash] = new (ELeave) CArrayFixSeg( 2 ); + + s.iVal = reinterpret_cast(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** hashTableToUse = + aString.iVal & 2 ? iCIHashTable : iCSHashTable; + + // Delete the node and delete the entry in the relevant collision list + CArrayFixSeg* 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** hashTableToUse = + aCaseInsensitive ? iCIHashTable : iCSHashTable; + CArrayFixSeg* 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)); + }