kernel/eka/euser/us_htab.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 137 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2005-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 "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:
// e32/euser/us_htab.cpp
// 
//

#include "us_std.h"
#include <e32hashtab.h>

const TUint KDefaultIndexBits = 4;
const TUint KMaxIndexBits = 28;

extern TUint32 DefaultIntegerHash(const TAny*);
extern TUint32 DefaultStringHash(const TUint8*, TInt);
extern TUint32 DefaultWStringHash(const TUint16*, TInt);

#define	_DEBUG_HASH_TABLE
#ifndef	_DEBUG
#undef	_DEBUG_HASH_TABLE
#endif

#define __PANIC(x) Panic(x)

EXPORT_C RHashTableBase::RHashTableBase(TGeneralHashFunction32 aHash, TGeneralIdentityRelation aId, TInt aElementSize, TInt aKeyOffset)
	:	iHashFunc(aHash),
		iIdFunc(aId),
		iIndexBits(TUint8(KDefaultIndexBits)),
		iGeneration(EGen0),
		iPad0(0),
		iElements(0),
		iCount(0),
		iPad1(0),
		iPad2(0)
	{
	__ASSERT_ALWAYS(aHash!=NULL, __PANIC(EHashTableNoHashFunc));
	__ASSERT_ALWAYS(aId!=NULL, __PANIC(EHashTableNoIdentityRelation));
	__ASSERT_ALWAYS(aElementSize>0, __PANIC(EHashTableBadElementSize));
	__ASSERT_ALWAYS(aKeyOffset==0 || TUint(aKeyOffset-4)<(TUint)Min(252,aElementSize-4), __PANIC(EHashTableBadKeyOffset));
	iElementSize = aElementSize;
	iKeyOffset = (TUint8)aKeyOffset;	// 0 means ptr at offset 4
	iEmptyCount = 0;
	SetThresholds();
	}

void RHashTableBase::SetThresholds()
	{
	TUint32 max = 1u << iIndexBits;
	if (iIndexBits == KMaxIndexBits)
		iUpperThreshold = KMaxTUint;
	else
		iUpperThreshold = (max>>1) + (max>>2);	// 3/4 of max
	if (iIndexBits == KDefaultIndexBits)
		iLowerThreshold = 0;
	else
		iLowerThreshold = max >> 2;				// 1/4 of max

	// clean table if <1/8 of entries empty
	iCleanThreshold = max>>3;
	}

EXPORT_C void RHashTableBase::Close()
	{
	User::Free(iElements);
	new (this) RHashTableBase(iHashFunc, iIdFunc, iElementSize, iKeyOffset);
	}

EXPORT_C TInt RHashTableBase::Count() const
	{
	return (TInt)iCount;
	}

EXPORT_C TAny* RHashTableBase::Find(const TAny* aKey, TInt aOffset) const
	{
	if (!iElements)
		return NULL;
	TUint32 hash = (*iHashFunc)(aKey);
	TUint32 ix = hash >> (32 - iIndexBits);		// top bits of hash used as initial index
	hash = (hash &~ EStateMask) | iGeneration;
	TUint32 mask = (1u << iIndexBits) - 1;		// iIndexBits 1's
	TUint32 step = (hash >> 1) & mask;			// iIndexBits-1 LSBs of hash followed by 1
	FOREVER
		{
		const SElement* e = ElementC(ix);
		if (e->iHash==hash && (*iIdFunc)(aKey, GetKey(e)))
			{
			if (aOffset >= 0)
				return ((TUint8*)e) + aOffset;
			return *(TAny**)((TUint8*)e - aOffset);
			}
		if (e->IsEmpty())
			break;
		ix = (ix + step) & mask;
		}
	return NULL;
	}

EXPORT_C TAny* RHashTableBase::FindL(const TAny* aKey, TInt aOffset) const
	{
	TAny* p = Find(aKey, aOffset);
	if (!p)
		User::Leave(KErrNotFound);
	return p;
	}

TInt RHashTableBase::Insert(const TAny* aKey, TAny*& aElement)
	{
	TInt r = KErrNone;
	TUint32 max = 1u << iIndexBits;
	if (!iElements)
		{
		iElements = User::AllocZ(max * iElementSize);
		if (!iElements)
			return KErrNoMemory;
		iEmptyCount = max;
		}
	else if (iCount > iUpperThreshold)
		{
		r = ExpandTable(iIndexBits+1);
		if (iEmptyCount>1)
			r = KErrNone;	// doesn't matter if expand fails unless there is only one empty slot left
		max = 1u << iIndexBits;
		}
	else if (iEmptyCount < iCleanThreshold)
		ReformTable(iIndexBits);

	TUint32 hash = (*iHashFunc)(aKey);
	TUint32 ix = hash >> (32 - iIndexBits);
	TUint32 mask = max - 1;
	hash = (hash &~ EStateMask) | iGeneration;
	TUint32 step = (hash >> 1) & mask;			// iIndexBits-1 LSBs of hash followed by 1
	SElement* e = 0;
	SElement* d = 0;
	FOREVER
		{
		e = Element(ix);
		if (e->IsEmpty())
			break;
		if (e->IsDeleted())
			{
			if (!d)
				d = e;
			}
		else if (e->iHash==hash && (*iIdFunc)(aKey, GetKey(e)))
			{
			aElement = e;
			return KErrNone;	// duplicate so always succeed
			}
		ix = (ix + step) & mask;
		}
	if (d)
		e = d;		// if we can reuse a deleted slot, always succeed
	else
		{
		if (r!=KErrNone)
			return r;	// new slot needed - if we failed to expand, fail the request here
		--iEmptyCount;
		}
	e->iHash = hash;
	aElement = e;
	++iCount;
	return KErrNone;
	}

EXPORT_C TInt RHashTableBase::PtrInsert(const TAny* aKey, const TAny* aValue)
	{
	const TAny** e;
	TInt r = Insert(aKey, (TAny*&)e);
	if (r==KErrNone)
		{
		e[1] = aKey;
		if (iElementSize>=12)
			e[2] = aValue;
		}
	return r;
	}

EXPORT_C void RHashTableBase::PtrInsertL(const TAny* aKey, const TAny* aValue)
	{
	const TAny** e;
	User::LeaveIfError(Insert(aKey, (TAny*&)e));
	e[1] = aKey;
	if (iElementSize>=12)
		e[2] = aValue;
	}

EXPORT_C TInt RHashTableBase::ValueInsert(const TAny* aKey, TInt aKeySize, const TAny* aValue, TInt aValueOffset, TInt aValueSize)
	{
	TUint8* e;
	TInt r = Insert(aKey, (TAny*&)e);
	if (r==KErrNone)
		{
		memcpy(e+iKeyOffset, aKey, aKeySize);
		if (aValue)
			memcpy(e+aValueOffset, aValue, aValueSize);
		}
	return r;
	}

EXPORT_C void RHashTableBase::ValueInsertL(const TAny* aKey, TInt aKeySize, const TAny* aValue, TInt aValueOffset, TInt aValueSize)
	{
	TUint8* e;
	User::LeaveIfError(Insert(aKey, (TAny*&)e));
	memcpy(e+iKeyOffset, aKey, aKeySize);
	if (aValue)
		memcpy(e+aValueOffset, aValue, aValueSize);
	}

EXPORT_C TInt RHashTableBase::Remove(const TAny* aKey)
	{
	SElement* e = (SElement*)Find(aKey);
	if (!e)
		return KErrNotFound;
	e->SetDeleted();
	if (--iCount == 0)
		{
		Close();
		return KErrNone;
		}
	if (iCount < iLowerThreshold)
		ShrinkTable();
	return KErrNone;
	}

void RHashTableBase::ReformTable(TUint aNewIndexBits)
	{
	if (!iElements)
		return;
	TUint32 max = 1u << iIndexBits;
	TUint32 newmax = 1u << aNewIndexBits;
	TUint32 newmask = newmax - 1;
	TUint32 ix = 0;
	TUint32 newsh = 32 - aNewIndexBits;
	iGeneration ^= 1;	// change generation so we know which entries have been updated
	for (; ix < max; ++ix)
		{
		SElement* e = Element(ix);
		if (e->IsEmpty())
			continue;		// skip empty entries
		if (e->IsDeleted())
			{
			e->SetEmpty();	// mark deleted entries as empty
			continue;
			}
		if ((e->iHash & EStateMask) == iGeneration)	// entry has been processed so leave it alone
			continue;
		TUint32 pos = e->iHash >> newsh;
		if (pos == ix)
			{
			e->iHash ^= 1;		// entry is in first position for its hash so leave it there
			continue;
			}
		TUint32 step = (e->iHash >> 1) & newmask;
		FOREVER
			{
			SElement* d = Element(pos);
			if (d->IsEmptyOrDeleted())
				{
				memcpy(d, e, iElementSize);
				d->iHash &= ~EStateMask;
				d->iHash |= iGeneration;	// mark it as processed
				e->SetEmpty();				// remove old entry
				break;
				}
			if ((d->iHash & EStateMask) != iGeneration)
				{
				if (pos == ix)
					{
					e->iHash ^= 1;		// entry is already in correct position so leave it there
					break;
					}
				if ((d->iHash >> newsh) == pos)
					{
					// candidate for replacement is in correct position so leave it and look elsewhere
					d->iHash ^= 1;
					}
				else
					{
					Mem::Swap(d, e, iElementSize);	// switch entries
					d->iHash ^= 1;		// mark entry as processed
					--ix;				// process current position again
					break;
					}
				}
			pos = (pos + step) & newmask;
			}
		}
	iIndexBits = (TUint8)aNewIndexBits;
	iEmptyCount = newmax - iCount;
	SetThresholds();
#ifdef _DEBUG_HASH_TABLE
	VerifyReform();
#endif
	}

#ifdef _DEBUG_HASH_TABLE
void RHashTableBase::VerifyReform()
	{
	TUint32 dcount;
	ConsistencyCheck(&dcount);
	__ASSERT_ALWAYS(dcount==0, __PANIC(EHashTableDeletedEntryAfterReform));
	}
#endif

EXPORT_C void RHashTableBase::ConsistencyCheck(TUint32* aDeleted, TUint32* aComparisons, TUint32 aChainLimit, TUint32* aChainInfo)
	{
#ifdef _DEBUG_HASH_TABLE
	TUint32 count = 0;
	TUint32 dcount = 0;
	TUint32 ecount = 0;
	TUint32 max = 1u << iIndexBits;
	TUint32 mask = max - 1;
	TUint32 sh = 32 - iIndexBits;
	TUint32 ix = 0;
	TUint32 cmp = 0;
	if (aChainInfo)
		memclr(aChainInfo, aChainLimit*sizeof(TUint32));
	if (iElements)
		{
		for (ix = 0; ix < max; ++ix)
			{
			SElement* e = Element(ix);
			if (e->IsEmpty())
				{
				++ecount;
				continue;
				}
			if (e->IsDeleted())
				{
				++dcount;
				continue;
				}
			++count;
			__ASSERT_ALWAYS((e->iHash & EStateMask) == iGeneration, __PANIC(EHashTableBadGeneration));
			TUint32 hash = (*iHashFunc)(GetKey(e));
			hash = (hash &~ EStateMask) | iGeneration;
			__ASSERT_ALWAYS(e->iHash == hash, __PANIC(EHashTableBadHash));

			TUint32 pos = hash >> sh;
			TUint32 step = (hash >> 1) & mask;
			SElement* f = 0;
			TUint32 cl = 0;
			FOREVER
				{
				f = Element(pos);
				if (f->IsEmpty())
					{
					f = 0;
					break;
					}
				++cl;
				if (!f->IsDeleted() && f->iHash==hash)
					{
					++cmp;
					if (e==f || (*iIdFunc)(GetKey(e), GetKey(f)))
						break;
					}
				pos = (pos + step) & mask;
				}
			__ASSERT_ALWAYS(e==f, __PANIC(EHashTableEntryLost));
			if (aChainInfo && cl<aChainLimit)
				++aChainInfo[cl];
			}
		}
	if (aDeleted)
		*aDeleted = dcount;
	if (aComparisons)
		*aComparisons = cmp;
	__ASSERT_ALWAYS(iCount==count, __PANIC(EHashTableCountWrong));
	__ASSERT_ALWAYS(iEmptyCount==ecount, __PANIC(EHashTableEmptyCountWrong));
#else
	if (aDeleted)
		*aDeleted = KMaxTUint;
	if (aComparisons)
		*aComparisons = KMaxTUint;
	if (aChainInfo)
		memclr(aChainInfo, aChainLimit*sizeof(TUint32));
#endif
	}

void RHashTableBase::ShrinkTable()
	{
	ReformTable(iIndexBits - 1);
	TUint32 max = 1u << iIndexBits;
	iElements = User::ReAlloc(iElements, max * iElementSize);
	}

TInt RHashTableBase::ExpandTable(TInt aNewIndexBits)
	{
	TUint32 newmax = 1u << aNewIndexBits;
	if (!iElements)
		{
		iElements = User::AllocZ(newmax * iElementSize);
		if (!iElements)
			return KErrNoMemory;
		iIndexBits = (TUint8)aNewIndexBits;
		iEmptyCount = newmax;
		SetThresholds();
		return KErrNone;
		}
	TUint32 max = 1u << iIndexBits;
	TAny* p = User::ReAlloc(iElements, newmax * iElementSize);
	if (!p)
		return KErrNoMemory;
	iElements = p;
	memclr(Element(max), (newmax-max)*iElementSize);
	ReformTable(aNewIndexBits);
	return KErrNone;
	}

EXPORT_C TInt RHashTableBase::Reserve(TInt aCount)
	{
	__ASSERT_ALWAYS((TUint)aCount<0x40000000u, __PANIC(EHashTableBadReserveCount));
	TInt new_ixb = iIndexBits;
	TUint grow_threshold = iUpperThreshold;
	while (TUint(aCount) > grow_threshold)
		{
		grow_threshold <<= 1;
		++new_ixb;
		}
	// Expand the table if it isn't large enough to fit aCount elements in it
	// or if the table hasn't yet been created, create it with ExpandTable
	if (new_ixb > TInt(iIndexBits) || !iElements)
		{
		return ExpandTable(new_ixb);
		}
	return KErrNone;
	}

EXPORT_C void RHashTableBase::ReserveL(TInt aCount)
	{
	User::LeaveIfError(Reserve(aCount));
	}

EXPORT_C THashTableIterBase::THashTableIterBase(const RHashTableBase& aTable)
	:	iTbl(aTable), iIndex(-1), iPad1(0), iPad2(0)
	{
	}

EXPORT_C void THashTableIterBase::Reset()
	{
	iIndex = -1;
	}

EXPORT_C const TAny* THashTableIterBase::Next(TInt aOffset)
	{
	TInt max = 1 << iTbl.iIndexBits;
	if (!iTbl.iElements)
		return NULL;
	__ASSERT_DEBUG(iIndex>=-1 && iIndex<=max, __PANIC(EHashTableIterNextBadIndex));
	if (iIndex < max)
		++iIndex;
	for(; iIndex < max; ++iIndex)
		{
		const RHashTableBase::SElement* e = iTbl.ElementC(iIndex);
		if (!e->IsEmptyOrDeleted())
			{
			if (aOffset >= 0)
				return (TUint8*)e + aOffset;
			return *(const TAny**)((TUint8*)e - aOffset);
			}
		}
	return NULL;
	}

EXPORT_C const TAny* THashTableIterBase::Current(TInt aOffset) const
	{
	TInt max = 1 << iTbl.iIndexBits;
	if (!iTbl.iElements || iIndex<0 || iIndex>=max)
		return NULL;
	const RHashTableBase::SElement* e = iTbl.ElementC(iIndex);
	__ASSERT_DEBUG(!e->IsEmptyOrDeleted(), __PANIC(EHashTableIterCurrentBadIndex));
	if (aOffset >= 0)
		return (TUint8*)e + aOffset;
	return *(const TAny**)((TUint8*)e - aOffset);
	}

EXPORT_C void THashTableIterBase::RemoveCurrent()
	{
	TInt max = 1 << iTbl.iIndexBits;
	if (!iTbl.iElements || iIndex<0 || iIndex>=max)
		return;
	RHashTableBase& tbl = (RHashTableBase&)iTbl;
	RHashTableBase::SElement* e = tbl.Element(iIndex);
	__ASSERT_DEBUG(!e->IsEmptyOrDeleted(), __PANIC(EHashTableIterCurrentBadIndex));

	// mark entry as deleted but don't shrink the array since that will mess up the iteration
	e->SetDeleted();
	if (--tbl.iCount == 0)
		{
		memclr(tbl.iElements, max * tbl.iElementSize);
		tbl.iEmptyCount = max;
		tbl.iGeneration = RHashTableBase::EGen0;
		}
	}

/**
@publishedAll
@released

Calculate a 32 bit hash from an 8 bit descriptor.

@param	aDes	The descriptor to be hashed.
@return			The calculated 32 bit hash value.
*/
EXPORT_C TUint32 DefaultHash::Des8(const TDesC8& aDes)
	{
	return DefaultStringHash(aDes.Ptr(), aDes.Length());
	}


/**
@publishedAll
@released

Calculate a 32 bit hash from a 16 bit descriptor.

@param	aDes	The descriptor to be hashed.
@return			The calculated 32 bit hash value.
*/
EXPORT_C TUint32 DefaultHash::Des16(const TDesC16& aDes)
	{
	return DefaultWStringHash(aDes.Ptr(), aDes.Size());
	}


/**
@publishedAll
@released

Calculate a 32 bit hash from a TInt pointer.

@param	aPtr	The TInt pointer to be hashed.
@return			The calculated 32 bit hash value.
*/
EXPORT_C TUint32 DefaultHash::IntegerPtr(TInt* const& aPtr)
	{
	return Integer((TInt)aPtr);
	}

/**
@publishedAll
@released

Calculate a 32 bit hash from a TDesC8 pointer.

@param	aPtr	The TDesC8 pointer to be hashed.
@return			The calculated 32 bit hash value.
*/
EXPORT_C TUint32 DefaultHash::Des8Ptr(TDesC8* const& aPtr)
	{
	return Integer((TInt)aPtr);
	}

/**
@publishedAll
@released

Calculate a 32 bit hash from a TDesC16 pointer.

@param	aPtr	The TDesC16 pointer to be hashed.
@return			The calculated 32 bit hash value.
*/
EXPORT_C TUint32 DefaultHash::Des16Ptr(TDesC16* const& aPtr)
	{
	return Integer((TInt)aPtr);
	}

/**
@publishedAll
@released

Compare two integers for equality.

@param	aA	The first integer to be compared
@param	aB	The second integer to be compared
@return		ETrue if the arguments are equal, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::Integer(const TInt& aA, const TInt& aB)
	{
	return aA == aB;
	}


/**
@publishedAll
@released

Compare two 8 bit descriptors for exact binary equality.

@param	aA	The first integer to be compared
@param	aB	The second integer to be compared
@return		ETrue if the arguments are identical, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::Des8(const TDesC8& aA, const TDesC8& aB)
	{
	return aA == aB;
	}


/**
@publishedAll
@released

Compare two 16 bit descriptors for exact binary equality.

@param	aA	The first integer to be compared
@param	aB	The second integer to be compared
@return		ETrue if the arguments are identical, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::Des16(const TDesC16& aA, const TDesC16& aB)
	{
	return aA == aB;
	}

/**
@publishedAll
@released

Compare two TInt pointers for equality.

@param	aA	The first pointer to be compared
@param	aB	The second pointer to be compared
@return		ETrue if the arguments are equal, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::IntegerPtr(TInt* const& aA,TInt* const& aB)
	{
	return aA == aB;
	}

/**
@publishedAll
@released

Compare two TDesC8 pointers for equality.

@param	aA	The first pointer to be compared
@param	aB	The second pointer to be compared
@return		ETrue if the arguments are equal, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::Des8Ptr(TDesC8* const& aA,TDesC8* const& aB)
	{
	return aA == aB;
	}

/**
@publishedAll
@released

Compare two TDesC16 pointers for equality.

@param	aA	The first pointer to be compared
@param	aB	The second pointer to be compared
@return		ETrue if the arguments are equal, EFalse otherwise.
*/
EXPORT_C TBool DefaultIdentity::Des16Ptr(TDesC16* const& aA,TDesC16* const& aB)
	{
	return aA == aB;
	}