sl@0: // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32/euser/us_htab.cpp sl@0: // sl@0: // sl@0: sl@0: #include "us_std.h" sl@0: #include sl@0: sl@0: const TUint KDefaultIndexBits = 4; sl@0: const TUint KMaxIndexBits = 28; sl@0: sl@0: extern TUint32 DefaultIntegerHash(const TAny*); sl@0: extern TUint32 DefaultStringHash(const TUint8*, TInt); sl@0: extern TUint32 DefaultWStringHash(const TUint16*, TInt); sl@0: sl@0: #define _DEBUG_HASH_TABLE sl@0: #ifndef _DEBUG sl@0: #undef _DEBUG_HASH_TABLE sl@0: #endif sl@0: sl@0: #define __PANIC(x) Panic(x) sl@0: sl@0: EXPORT_C RHashTableBase::RHashTableBase(TGeneralHashFunction32 aHash, TGeneralIdentityRelation aId, TInt aElementSize, TInt aKeyOffset) sl@0: : iHashFunc(aHash), sl@0: iIdFunc(aId), sl@0: iIndexBits(TUint8(KDefaultIndexBits)), sl@0: iGeneration(EGen0), sl@0: iPad0(0), sl@0: iElements(0), sl@0: iCount(0), sl@0: iPad1(0), sl@0: iPad2(0) sl@0: { sl@0: __ASSERT_ALWAYS(aHash!=NULL, __PANIC(EHashTableNoHashFunc)); sl@0: __ASSERT_ALWAYS(aId!=NULL, __PANIC(EHashTableNoIdentityRelation)); sl@0: __ASSERT_ALWAYS(aElementSize>0, __PANIC(EHashTableBadElementSize)); sl@0: __ASSERT_ALWAYS(aKeyOffset==0 || TUint(aKeyOffset-4)<(TUint)Min(252,aElementSize-4), __PANIC(EHashTableBadKeyOffset)); sl@0: iElementSize = aElementSize; sl@0: iKeyOffset = (TUint8)aKeyOffset; // 0 means ptr at offset 4 sl@0: iEmptyCount = 0; sl@0: SetThresholds(); sl@0: } sl@0: sl@0: void RHashTableBase::SetThresholds() sl@0: { sl@0: TUint32 max = 1u << iIndexBits; sl@0: if (iIndexBits == KMaxIndexBits) sl@0: iUpperThreshold = KMaxTUint; sl@0: else sl@0: iUpperThreshold = (max>>1) + (max>>2); // 3/4 of max sl@0: if (iIndexBits == KDefaultIndexBits) sl@0: iLowerThreshold = 0; sl@0: else sl@0: iLowerThreshold = max >> 2; // 1/4 of max sl@0: sl@0: // clean table if <1/8 of entries empty sl@0: iCleanThreshold = max>>3; sl@0: } sl@0: sl@0: EXPORT_C void RHashTableBase::Close() sl@0: { sl@0: User::Free(iElements); sl@0: new (this) RHashTableBase(iHashFunc, iIdFunc, iElementSize, iKeyOffset); sl@0: } sl@0: sl@0: EXPORT_C TInt RHashTableBase::Count() const sl@0: { sl@0: return (TInt)iCount; sl@0: } sl@0: sl@0: EXPORT_C TAny* RHashTableBase::Find(const TAny* aKey, TInt aOffset) const sl@0: { sl@0: if (!iElements) sl@0: return NULL; sl@0: TUint32 hash = (*iHashFunc)(aKey); sl@0: TUint32 ix = hash >> (32 - iIndexBits); // top bits of hash used as initial index sl@0: hash = (hash &~ EStateMask) | iGeneration; sl@0: TUint32 mask = (1u << iIndexBits) - 1; // iIndexBits 1's sl@0: TUint32 step = (hash >> 1) & mask; // iIndexBits-1 LSBs of hash followed by 1 sl@0: FOREVER sl@0: { sl@0: const SElement* e = ElementC(ix); sl@0: if (e->iHash==hash && (*iIdFunc)(aKey, GetKey(e))) sl@0: { sl@0: if (aOffset >= 0) sl@0: return ((TUint8*)e) + aOffset; sl@0: return *(TAny**)((TUint8*)e - aOffset); sl@0: } sl@0: if (e->IsEmpty()) sl@0: break; sl@0: ix = (ix + step) & mask; sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: EXPORT_C TAny* RHashTableBase::FindL(const TAny* aKey, TInt aOffset) const sl@0: { sl@0: TAny* p = Find(aKey, aOffset); sl@0: if (!p) sl@0: User::Leave(KErrNotFound); sl@0: return p; sl@0: } sl@0: sl@0: TInt RHashTableBase::Insert(const TAny* aKey, TAny*& aElement) sl@0: { sl@0: TInt r = KErrNone; sl@0: TUint32 max = 1u << iIndexBits; sl@0: if (!iElements) sl@0: { sl@0: iElements = User::AllocZ(max * iElementSize); sl@0: if (!iElements) sl@0: return KErrNoMemory; sl@0: iEmptyCount = max; sl@0: } sl@0: else if (iCount > iUpperThreshold) sl@0: { sl@0: r = ExpandTable(iIndexBits+1); sl@0: if (iEmptyCount>1) sl@0: r = KErrNone; // doesn't matter if expand fails unless there is only one empty slot left sl@0: max = 1u << iIndexBits; sl@0: } sl@0: else if (iEmptyCount < iCleanThreshold) sl@0: ReformTable(iIndexBits); sl@0: sl@0: TUint32 hash = (*iHashFunc)(aKey); sl@0: TUint32 ix = hash >> (32 - iIndexBits); sl@0: TUint32 mask = max - 1; sl@0: hash = (hash &~ EStateMask) | iGeneration; sl@0: TUint32 step = (hash >> 1) & mask; // iIndexBits-1 LSBs of hash followed by 1 sl@0: SElement* e = 0; sl@0: SElement* d = 0; sl@0: FOREVER sl@0: { sl@0: e = Element(ix); sl@0: if (e->IsEmpty()) sl@0: break; sl@0: if (e->IsDeleted()) sl@0: { sl@0: if (!d) sl@0: d = e; sl@0: } sl@0: else if (e->iHash==hash && (*iIdFunc)(aKey, GetKey(e))) sl@0: { sl@0: aElement = e; sl@0: return KErrNone; // duplicate so always succeed sl@0: } sl@0: ix = (ix + step) & mask; sl@0: } sl@0: if (d) sl@0: e = d; // if we can reuse a deleted slot, always succeed sl@0: else sl@0: { sl@0: if (r!=KErrNone) sl@0: return r; // new slot needed - if we failed to expand, fail the request here sl@0: --iEmptyCount; sl@0: } sl@0: e->iHash = hash; sl@0: aElement = e; sl@0: ++iCount; sl@0: return KErrNone; sl@0: } sl@0: sl@0: EXPORT_C TInt RHashTableBase::PtrInsert(const TAny* aKey, const TAny* aValue) sl@0: { sl@0: const TAny** e; sl@0: TInt r = Insert(aKey, (TAny*&)e); sl@0: if (r==KErrNone) sl@0: { sl@0: e[1] = aKey; sl@0: if (iElementSize>=12) sl@0: e[2] = aValue; sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: EXPORT_C void RHashTableBase::PtrInsertL(const TAny* aKey, const TAny* aValue) sl@0: { sl@0: const TAny** e; sl@0: User::LeaveIfError(Insert(aKey, (TAny*&)e)); sl@0: e[1] = aKey; sl@0: if (iElementSize>=12) sl@0: e[2] = aValue; sl@0: } sl@0: sl@0: EXPORT_C TInt RHashTableBase::ValueInsert(const TAny* aKey, TInt aKeySize, const TAny* aValue, TInt aValueOffset, TInt aValueSize) sl@0: { sl@0: TUint8* e; sl@0: TInt r = Insert(aKey, (TAny*&)e); sl@0: if (r==KErrNone) sl@0: { sl@0: memcpy(e+iKeyOffset, aKey, aKeySize); sl@0: if (aValue) sl@0: memcpy(e+aValueOffset, aValue, aValueSize); sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: EXPORT_C void RHashTableBase::ValueInsertL(const TAny* aKey, TInt aKeySize, const TAny* aValue, TInt aValueOffset, TInt aValueSize) sl@0: { sl@0: TUint8* e; sl@0: User::LeaveIfError(Insert(aKey, (TAny*&)e)); sl@0: memcpy(e+iKeyOffset, aKey, aKeySize); sl@0: if (aValue) sl@0: memcpy(e+aValueOffset, aValue, aValueSize); sl@0: } sl@0: sl@0: EXPORT_C TInt RHashTableBase::Remove(const TAny* aKey) sl@0: { sl@0: SElement* e = (SElement*)Find(aKey); sl@0: if (!e) sl@0: return KErrNotFound; sl@0: e->SetDeleted(); sl@0: if (--iCount == 0) sl@0: { sl@0: Close(); sl@0: return KErrNone; sl@0: } sl@0: if (iCount < iLowerThreshold) sl@0: ShrinkTable(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void RHashTableBase::ReformTable(TUint aNewIndexBits) sl@0: { sl@0: if (!iElements) sl@0: return; sl@0: TUint32 max = 1u << iIndexBits; sl@0: TUint32 newmax = 1u << aNewIndexBits; sl@0: TUint32 newmask = newmax - 1; sl@0: TUint32 ix = 0; sl@0: TUint32 newsh = 32 - aNewIndexBits; sl@0: iGeneration ^= 1; // change generation so we know which entries have been updated sl@0: for (; ix < max; ++ix) sl@0: { sl@0: SElement* e = Element(ix); sl@0: if (e->IsEmpty()) sl@0: continue; // skip empty entries sl@0: if (e->IsDeleted()) sl@0: { sl@0: e->SetEmpty(); // mark deleted entries as empty sl@0: continue; sl@0: } sl@0: if ((e->iHash & EStateMask) == iGeneration) // entry has been processed so leave it alone sl@0: continue; sl@0: TUint32 pos = e->iHash >> newsh; sl@0: if (pos == ix) sl@0: { sl@0: e->iHash ^= 1; // entry is in first position for its hash so leave it there sl@0: continue; sl@0: } sl@0: TUint32 step = (e->iHash >> 1) & newmask; sl@0: FOREVER sl@0: { sl@0: SElement* d = Element(pos); sl@0: if (d->IsEmptyOrDeleted()) sl@0: { sl@0: memcpy(d, e, iElementSize); sl@0: d->iHash &= ~EStateMask; sl@0: d->iHash |= iGeneration; // mark it as processed sl@0: e->SetEmpty(); // remove old entry sl@0: break; sl@0: } sl@0: if ((d->iHash & EStateMask) != iGeneration) sl@0: { sl@0: if (pos == ix) sl@0: { sl@0: e->iHash ^= 1; // entry is already in correct position so leave it there sl@0: break; sl@0: } sl@0: if ((d->iHash >> newsh) == pos) sl@0: { sl@0: // candidate for replacement is in correct position so leave it and look elsewhere sl@0: d->iHash ^= 1; sl@0: } sl@0: else sl@0: { sl@0: Mem::Swap(d, e, iElementSize); // switch entries sl@0: d->iHash ^= 1; // mark entry as processed sl@0: --ix; // process current position again sl@0: break; sl@0: } sl@0: } sl@0: pos = (pos + step) & newmask; sl@0: } sl@0: } sl@0: iIndexBits = (TUint8)aNewIndexBits; sl@0: iEmptyCount = newmax - iCount; sl@0: SetThresholds(); sl@0: #ifdef _DEBUG_HASH_TABLE sl@0: VerifyReform(); sl@0: #endif sl@0: } sl@0: sl@0: #ifdef _DEBUG_HASH_TABLE sl@0: void RHashTableBase::VerifyReform() sl@0: { sl@0: TUint32 dcount; sl@0: ConsistencyCheck(&dcount); sl@0: __ASSERT_ALWAYS(dcount==0, __PANIC(EHashTableDeletedEntryAfterReform)); sl@0: } sl@0: #endif sl@0: sl@0: EXPORT_C void RHashTableBase::ConsistencyCheck(TUint32* aDeleted, TUint32* aComparisons, TUint32 aChainLimit, TUint32* aChainInfo) sl@0: { sl@0: #ifdef _DEBUG_HASH_TABLE sl@0: TUint32 count = 0; sl@0: TUint32 dcount = 0; sl@0: TUint32 ecount = 0; sl@0: TUint32 max = 1u << iIndexBits; sl@0: TUint32 mask = max - 1; sl@0: TUint32 sh = 32 - iIndexBits; sl@0: TUint32 ix = 0; sl@0: TUint32 cmp = 0; sl@0: if (aChainInfo) sl@0: memclr(aChainInfo, aChainLimit*sizeof(TUint32)); sl@0: if (iElements) sl@0: { sl@0: for (ix = 0; ix < max; ++ix) sl@0: { sl@0: SElement* e = Element(ix); sl@0: if (e->IsEmpty()) sl@0: { sl@0: ++ecount; sl@0: continue; sl@0: } sl@0: if (e->IsDeleted()) sl@0: { sl@0: ++dcount; sl@0: continue; sl@0: } sl@0: ++count; sl@0: __ASSERT_ALWAYS((e->iHash & EStateMask) == iGeneration, __PANIC(EHashTableBadGeneration)); sl@0: TUint32 hash = (*iHashFunc)(GetKey(e)); sl@0: hash = (hash &~ EStateMask) | iGeneration; sl@0: __ASSERT_ALWAYS(e->iHash == hash, __PANIC(EHashTableBadHash)); sl@0: sl@0: TUint32 pos = hash >> sh; sl@0: TUint32 step = (hash >> 1) & mask; sl@0: SElement* f = 0; sl@0: TUint32 cl = 0; sl@0: FOREVER sl@0: { sl@0: f = Element(pos); sl@0: if (f->IsEmpty()) sl@0: { sl@0: f = 0; sl@0: break; sl@0: } sl@0: ++cl; sl@0: if (!f->IsDeleted() && f->iHash==hash) sl@0: { sl@0: ++cmp; sl@0: if (e==f || (*iIdFunc)(GetKey(e), GetKey(f))) sl@0: break; sl@0: } sl@0: pos = (pos + step) & mask; sl@0: } sl@0: __ASSERT_ALWAYS(e==f, __PANIC(EHashTableEntryLost)); sl@0: if (aChainInfo && cl grow_threshold) sl@0: { sl@0: grow_threshold <<= 1; sl@0: ++new_ixb; sl@0: } sl@0: // Expand the table if it isn't large enough to fit aCount elements in it sl@0: // or if the table hasn't yet been created, create it with ExpandTable sl@0: if (new_ixb > TInt(iIndexBits) || !iElements) sl@0: { sl@0: return ExpandTable(new_ixb); sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: EXPORT_C void RHashTableBase::ReserveL(TInt aCount) sl@0: { sl@0: User::LeaveIfError(Reserve(aCount)); sl@0: } sl@0: sl@0: EXPORT_C THashTableIterBase::THashTableIterBase(const RHashTableBase& aTable) sl@0: : iTbl(aTable), iIndex(-1), iPad1(0), iPad2(0) sl@0: { sl@0: } sl@0: sl@0: EXPORT_C void THashTableIterBase::Reset() sl@0: { sl@0: iIndex = -1; sl@0: } sl@0: sl@0: EXPORT_C const TAny* THashTableIterBase::Next(TInt aOffset) sl@0: { sl@0: TInt max = 1 << iTbl.iIndexBits; sl@0: if (!iTbl.iElements) sl@0: return NULL; sl@0: __ASSERT_DEBUG(iIndex>=-1 && iIndex<=max, __PANIC(EHashTableIterNextBadIndex)); sl@0: if (iIndex < max) sl@0: ++iIndex; sl@0: for(; iIndex < max; ++iIndex) sl@0: { sl@0: const RHashTableBase::SElement* e = iTbl.ElementC(iIndex); sl@0: if (!e->IsEmptyOrDeleted()) sl@0: { sl@0: if (aOffset >= 0) sl@0: return (TUint8*)e + aOffset; sl@0: return *(const TAny**)((TUint8*)e - aOffset); sl@0: } sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: EXPORT_C const TAny* THashTableIterBase::Current(TInt aOffset) const sl@0: { sl@0: TInt max = 1 << iTbl.iIndexBits; sl@0: if (!iTbl.iElements || iIndex<0 || iIndex>=max) sl@0: return NULL; sl@0: const RHashTableBase::SElement* e = iTbl.ElementC(iIndex); sl@0: __ASSERT_DEBUG(!e->IsEmptyOrDeleted(), __PANIC(EHashTableIterCurrentBadIndex)); sl@0: if (aOffset >= 0) sl@0: return (TUint8*)e + aOffset; sl@0: return *(const TAny**)((TUint8*)e - aOffset); sl@0: } sl@0: sl@0: EXPORT_C void THashTableIterBase::RemoveCurrent() sl@0: { sl@0: TInt max = 1 << iTbl.iIndexBits; sl@0: if (!iTbl.iElements || iIndex<0 || iIndex>=max) sl@0: return; sl@0: RHashTableBase& tbl = (RHashTableBase&)iTbl; sl@0: RHashTableBase::SElement* e = tbl.Element(iIndex); sl@0: __ASSERT_DEBUG(!e->IsEmptyOrDeleted(), __PANIC(EHashTableIterCurrentBadIndex)); sl@0: sl@0: // mark entry as deleted but don't shrink the array since that will mess up the iteration sl@0: e->SetDeleted(); sl@0: if (--tbl.iCount == 0) sl@0: { sl@0: memclr(tbl.iElements, max * tbl.iElementSize); sl@0: tbl.iEmptyCount = max; sl@0: tbl.iGeneration = RHashTableBase::EGen0; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Calculate a 32 bit hash from an 8 bit descriptor. sl@0: sl@0: @param aDes The descriptor to be hashed. sl@0: @return The calculated 32 bit hash value. sl@0: */ sl@0: EXPORT_C TUint32 DefaultHash::Des8(const TDesC8& aDes) sl@0: { sl@0: return DefaultStringHash(aDes.Ptr(), aDes.Length()); sl@0: } sl@0: sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Calculate a 32 bit hash from a 16 bit descriptor. sl@0: sl@0: @param aDes The descriptor to be hashed. sl@0: @return The calculated 32 bit hash value. sl@0: */ sl@0: EXPORT_C TUint32 DefaultHash::Des16(const TDesC16& aDes) sl@0: { sl@0: return DefaultWStringHash(aDes.Ptr(), aDes.Size()); sl@0: } sl@0: sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Calculate a 32 bit hash from a TInt pointer. sl@0: sl@0: @param aPtr The TInt pointer to be hashed. sl@0: @return The calculated 32 bit hash value. sl@0: */ sl@0: EXPORT_C TUint32 DefaultHash::IntegerPtr(TInt* const& aPtr) sl@0: { sl@0: return Integer((TInt)aPtr); sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Calculate a 32 bit hash from a TDesC8 pointer. sl@0: sl@0: @param aPtr The TDesC8 pointer to be hashed. sl@0: @return The calculated 32 bit hash value. sl@0: */ sl@0: EXPORT_C TUint32 DefaultHash::Des8Ptr(TDesC8* const& aPtr) sl@0: { sl@0: return Integer((TInt)aPtr); sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Calculate a 32 bit hash from a TDesC16 pointer. sl@0: sl@0: @param aPtr The TDesC16 pointer to be hashed. sl@0: @return The calculated 32 bit hash value. sl@0: */ sl@0: EXPORT_C TUint32 DefaultHash::Des16Ptr(TDesC16* const& aPtr) sl@0: { sl@0: return Integer((TInt)aPtr); sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two integers for equality. sl@0: sl@0: @param aA The first integer to be compared sl@0: @param aB The second integer to be compared sl@0: @return ETrue if the arguments are equal, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::Integer(const TInt& aA, const TInt& aB) sl@0: { sl@0: return aA == aB; sl@0: } sl@0: sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two 8 bit descriptors for exact binary equality. sl@0: sl@0: @param aA The first integer to be compared sl@0: @param aB The second integer to be compared sl@0: @return ETrue if the arguments are identical, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::Des8(const TDesC8& aA, const TDesC8& aB) sl@0: { sl@0: return aA == aB; sl@0: } sl@0: sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two 16 bit descriptors for exact binary equality. sl@0: sl@0: @param aA The first integer to be compared sl@0: @param aB The second integer to be compared sl@0: @return ETrue if the arguments are identical, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::Des16(const TDesC16& aA, const TDesC16& aB) sl@0: { sl@0: return aA == aB; sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two TInt pointers for equality. sl@0: sl@0: @param aA The first pointer to be compared sl@0: @param aB The second pointer to be compared sl@0: @return ETrue if the arguments are equal, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::IntegerPtr(TInt* const& aA,TInt* const& aB) sl@0: { sl@0: return aA == aB; sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two TDesC8 pointers for equality. sl@0: sl@0: @param aA The first pointer to be compared sl@0: @param aB The second pointer to be compared sl@0: @return ETrue if the arguments are equal, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::Des8Ptr(TDesC8* const& aA,TDesC8* const& aB) sl@0: { sl@0: return aA == aB; sl@0: } sl@0: sl@0: /** sl@0: @publishedAll sl@0: @released sl@0: sl@0: Compare two TDesC16 pointers for equality. sl@0: sl@0: @param aA The first pointer to be compared sl@0: @param aB The second pointer to be compared sl@0: @return ETrue if the arguments are equal, EFalse otherwise. sl@0: */ sl@0: EXPORT_C TBool DefaultIdentity::Des16Ptr(TDesC16* const& aA,TDesC16* const& aB) sl@0: { sl@0: return aA == aB; sl@0: }