From 8a71f7dbb443c8e7bd313de16a2f0af863da9c53 Mon Sep 17 00:00:00 2001 From: "bsmedberg%covad.net" Date: Thu, 24 Apr 2003 01:54:57 +0000 Subject: [PATCH] Bug 201034 non-const enumeration in nsBaseHashtable, add nsTHashtable::RawRemove. Add a hashtable test-suite, and clean up some codesize issues. Also fixes bug 203030 and hopefully SunOS build bustage. r=jkeiser sr=alecf a=asa git-svn-id: svn://10.0.0.236/trunk@141729 18797224-902f-48f8-a5cc-f745e15eee43 --- .../extensions/cookie/nsPermissionManager.h | 7 +- mozilla/xpcom/ds/nsBaseHashtable.h | 96 ++- mozilla/xpcom/ds/nsClassHashtable.h | 8 +- mozilla/xpcom/ds/nsHashKeys.h | 23 +- mozilla/xpcom/ds/nsInterfaceHashtable.h | 2 +- mozilla/xpcom/ds/nsTHashtable.h | 135 ++-- mozilla/xpcom/tests/Makefile.in | 1 + mozilla/xpcom/tests/TestHashtables.cpp | 728 ++++++++++++++++++ 8 files changed, 901 insertions(+), 99 deletions(-) create mode 100644 mozilla/xpcom/tests/TestHashtables.cpp diff --git a/mozilla/extensions/cookie/nsPermissionManager.h b/mozilla/extensions/cookie/nsPermissionManager.h index 48e9dd9102b..cbe607ab854 100644 --- a/mozilla/extensions/cookie/nsPermissionManager.h +++ b/mozilla/extensions/cookie/nsPermissionManager.h @@ -57,7 +57,7 @@ // This should be a multiple of 4, to make PermissionsAreEmpty() fast #define NUMBER_OF_TYPES (8) -class nsHostEntry : protected PLDHashEntryHdr +class nsHostEntry : public PLDHashEntryHdr { public: // Hash methods @@ -98,10 +98,7 @@ public: return PL_DHashStringKey(nsnull, aKey); } - static PRBool AllowMemMove() - { - return PR_TRUE; - } + enum { ALLOW_MEMMOVE = PR_TRUE }; // Permissions methods inline const nsDependentCString GetHost() const diff --git a/mozilla/xpcom/ds/nsBaseHashtable.h b/mozilla/xpcom/ds/nsBaseHashtable.h index 9c8038a02db..78e56a0e8ba 100644 --- a/mozilla/xpcom/ds/nsBaseHashtable.h +++ b/mozilla/xpcom/ds/nsBaseHashtable.h @@ -136,8 +136,6 @@ public: /** * function type provided by the application for enumeration. - * currently, only "read" enumeration is supported. If you need to change - * or remove entries during enumeration, it might could be arranged. * @param aKey the key being enumerated * @param aData data being enumerated * @parm userArg passed unchanged from Enumerate @@ -146,9 +144,9 @@ public: * @link PLDHashOperator::PL_DHASH_STOP PL_DHASH_STOP @endlink */ typedef PLDHashOperator - (*PR_CALLBACK EnumReadFunction)(KeyType aKey, - DataType aData, - void* userArg); + (*PR_CALLBACK EnumReadFunction)(KeyType aKey, + UserDataType aData, + void* userArg); /** * enumerate entries in the hashtable, without allowing changes @@ -157,7 +155,31 @@ public: * @param enumFunc enumeration callback * @param userArg passed unchanged to the EnumReadFunction */ - void EnumerateRead(EnumReadFunction enumFunc, void* userArg) const; + PRUint32 EnumerateRead(EnumReadFunction enumFunc, void* userArg) const; + + /** + * function type provided by the application for enumeration. + * @param aKey the key being enumerated + * @param aData Reference to data being enumerated, may be altered. e.g. for + * nsInterfaceHashtable this is an nsCOMPtr reference... + * @parm userArg passed unchanged from Enumerate + * @return bitflag combination of + * @link PLDHashOperator::PL_DHASH_REMOVE @endlink, + * @link PLDHashOperator::PL_DHASH_NEXT PL_DHASH_NEXT @endlink, or + * @link PLDHashOperator::PL_DHASH_STOP PL_DHASH_STOP @endlink + */ + typedef PLDHashOperator + (*PR_CALLBACK EnumFunction)(KeyType aKey, + DataType& aData, + void* userArg); + + /** + * enumerate entries in the hashtable, allowing changes. This + * functions write-locks the hashtable. + * @param enumFunc enumeration callback + * @param userArg passed unchanged to the EnumFunction + */ + PRUint32 Enumerate(EnumFunction enumFunc, void* userArg); /** * reset the hashtable, removing all entries @@ -182,6 +204,17 @@ protected: PLDHashEntryHdr *hdr, PRUint32 number, void *arg); + + struct s_EnumArgs + { + EnumFunction func; + void* userArg; + }; + + static PLDHashOperator s_EnumStub(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *arg); }; // @@ -251,7 +284,7 @@ nsBaseHashtable::Get(KeyType aKey, if (mLock) PR_RWLock_Rlock(mLock); - EntryType* ent = GetEntry(KeyClass::KeyToPointer(aKey)); + EntryType* ent = GetEntry(aKey); if (ent) { @@ -278,7 +311,7 @@ nsBaseHashtable::Put(KeyType aKey, if (mLock) PR_RWLock_Wlock(mLock); - EntryType* ent = PutEntry(KeyClass::KeyToPointer(aKey)); + EntryType* ent = PutEntry(aKey); if (!ent) { @@ -303,14 +336,14 @@ nsBaseHashtable::Remove(KeyType aKey) if (mLock) PR_RWLock_Wlock(mLock); - RemoveEntry(KeyClass::KeyToPointer(aKey)); + RemoveEntry(aKey); if (mLock) PR_RWLock_Unlock(mLock); } template -void +PRUint32 nsBaseHashtable::EnumerateRead (EnumReadFunction fEnumCall, void* userArg) const { @@ -321,12 +354,38 @@ nsBaseHashtable::EnumerateRead PR_RWLock_Rlock(mLock); s_EnumReadArgs enumData = { fEnumCall, userArg }; - PL_DHashTableEnumerate(NS_CONST_CAST(PLDHashTable*,&mTable), - s_EnumStub, - &enumData); + PRUint32 count = + PL_DHashTableEnumerate(NS_CONST_CAST(PLDHashTable*,&mTable), + s_EnumReadStub, + &enumData); if (mLock) PR_RWLock_Unlock(mLock); + + return count; +} + +template +PRUint32 +nsBaseHashtable::Enumerate + (EnumFunction fEnumCall, void* userArg) +{ + NS_ASSERTION(mTable.entrySize, + "nsBaseHashtable was not initialized properly."); + + if (mLock) + PR_RWLock_Wlock(mLock); + + s_EnumArgs enumData = { fEnumCall, userArg }; + PRUint32 count = + PL_DHashTableEnumerate(&mTable, + s_EnumStub, + &enumData); + + if (mLock) + PR_RWLock_Unlock(mLock); + + return count; } template @@ -360,4 +419,15 @@ nsBaseHashtable::s_EnumReadStub return PL_DHASH_NEXT; } +template +PLDHashOperator +nsBaseHashtable::s_EnumStub + (PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void* arg) +{ + EntryType* ent = NS_STATIC_CAST(EntryType*, hdr); + s_EnumArgs* eargs = (s_EnumArgs*) arg; + + return (eargs->func)(ent->GetKey(), ent->mData, eargs->userArg); +} + #endif // nsBaseHashtable_h__ diff --git a/mozilla/xpcom/ds/nsClassHashtable.h b/mozilla/xpcom/ds/nsClassHashtable.h index 9fa22c3e7d2..b231475ae75 100644 --- a/mozilla/xpcom/ds/nsClassHashtable.h +++ b/mozilla/xpcom/ds/nsClassHashtable.h @@ -40,6 +40,7 @@ #include "nsBaseHashtable.h" #include "nsHashKeys.h" +#include "nsAutoPtr.h" /** * templated hashtable class maps keys to C++ object pointers. @@ -51,11 +52,11 @@ */ template class nsClassHashtable : - public nsBaseHashtable,T*> + public nsBaseHashtable< KeyClass, nsAutoPtr, T* > { public: typedef typename KeyClass::KeyType KeyType; - typedef typename T* UserDataType; + typedef T* UserDataType; /** * @copydoc nsBaseHashtable::nsBaseHashtable @@ -74,6 +75,7 @@ public: PRBool Get(KeyType aKey, UserDataType* pData) const; }; + // // nsClassHashtable definitions // @@ -86,7 +88,7 @@ nsClassHashtable::Get(KeyType aKey, T** retVal) const PR_RWLock_Rlock(mLock); typename nsBaseHashtable,T*>::EntryType* ent = - GetEntry(KeyClass::KeyToPointer(aKey)); + GetEntry(aKey); if (ent) { diff --git a/mozilla/xpcom/ds/nsHashKeys.h b/mozilla/xpcom/ds/nsHashKeys.h index 22bfde6cc88..41838105aa8 100644 --- a/mozilla/xpcom/ds/nsHashKeys.h +++ b/mozilla/xpcom/ds/nsHashKeys.h @@ -49,19 +49,15 @@ #include NEW_H /** @file nsHashKeys.h - * standard HashKey classes for nsDataHashtable and relatives. Each of these + * standard HashKey classes for nsBaseHashtable and relatives. Each of these * classes follows the nsTHashtable::EntryType specification * - * Lightweight keytypes are provided here: + * Lightweight keytypes provided here: * nsStringHashKey * nsCStringHashKey * nsUint32HashKey * nsISupportsHashKey * nsIDHashKey - * - * Use these keytypes if possible; the templates are already instantiated - * for them, and they can be dynamically linked and therefore reduce code - * size! */ /** @@ -91,7 +87,7 @@ public: { return HashString(*aKey); } - static PRBool AllowMemMove() { return PR_TRUE; } + enum { ALLOW_MEMMOVE = PR_TRUE }; private: const nsString mStr; @@ -122,7 +118,7 @@ public: { return HashString(*aKey); } - static PRBool AllowMemMove() { return PR_TRUE; } + enum { ALLOW_MEMMOVE = PR_TRUE }; private: const nsCString mStr; @@ -149,7 +145,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { return *aKey; } - static PRBool AllowMemMove() { return PR_TRUE; } + enum { ALLOW_MEMMOVE = PR_TRUE }; private: const PRUint32 mValue; @@ -166,7 +162,8 @@ public: typedef nsISupports* KeyType; typedef const nsISupports* KeyTypePointer; - nsISupportsHashKey(nsISupports* key) : mSupports(key) { } + nsISupportsHashKey(const nsISupports* key) : + mSupports(NS_CONST_CAST(nsISupports*,key)) { } nsISupportsHashKey(const nsISupportsHashKey& toCopy) : mSupports(toCopy.mSupports) { } ~nsISupportsHashKey() { } @@ -181,10 +178,10 @@ public: { return NS_PTR_TO_INT32(aKey) >>2; } - static PRBool AllowMemMove() { return PR_TRUE; } + enum { ALLOW_MEMMOVE = PR_TRUE }; private: - const nsCOMPtr mSupports; + nsCOMPtr mSupports; }; /** @@ -208,7 +205,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey); - static PRBool AllowMemMove() { return PR_TRUE; } + enum { ALLOW_MEMMOVE = PR_TRUE }; private: const nsID mID; diff --git a/mozilla/xpcom/ds/nsInterfaceHashtable.h b/mozilla/xpcom/ds/nsInterfaceHashtable.h index 78a93d53e73..4dec6229d97 100644 --- a/mozilla/xpcom/ds/nsInterfaceHashtable.h +++ b/mozilla/xpcom/ds/nsInterfaceHashtable.h @@ -89,7 +89,7 @@ nsInterfaceHashtable::Get PR_RWLock_Rlock(mLock); typename nsBaseHashtable, Interface*>::EntryType* ent = - GetEntry(KeyClass::KeyToPointer(aKey)); + GetEntry(aKey); if (ent) { diff --git a/mozilla/xpcom/ds/nsTHashtable.h b/mozilla/xpcom/ds/nsTHashtable.h index d5ee5316522..c1d56c60692 100644 --- a/mozilla/xpcom/ds/nsTHashtable.h +++ b/mozilla/xpcom/ds/nsTHashtable.h @@ -53,7 +53,7 @@ * and must not declare any virtual functions or derive from classes * with virtual functions. Any vtable pointer would break the * PLDHashTable code. - *
   class EntryType : PLDHashEntryHdr
+ *
   class EntryType : public PLDHashEntryHdr
  *   {
  *   public: or friend nsTHashtable;
  *     // KeyType is what we use when Get()ing or Put()ing this entry
@@ -85,9 +85,9 @@
  *     // HashKey(): calculate the hash number
  *     static PLDHashNumber HashKey(KeyTypePointer aKey);
  *
- *     // AllowMemMove(): can we move this class with memmove(), or do we have
+ *     // ALLOW_MEMMOVE can we move this class with memmove(), or do we have
  *     // to use the copy constructor?
- *     static PRBool AllowMemMove();
+ *     enum { ALLOW_MEMMOVE = PR_(TRUE or FALSE) };
  *   }
* * @see nsInterfaceHashtable @@ -111,13 +111,19 @@ public: ~nsTHashtable(); /** - * Initialize the class. This function must be called before any other + * Initialize the table. This function must be called before any other * class operations. This can fail due to OOM conditions. * @param initSize the initial number of buckets in the hashtable, default 16 * @return PR_TRUE if the class was initialized properly. */ PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE); + /** + * Check whether the table has been initialized. This can be useful for static hashtables. + * @return the initialization state of the class. + */ + PRBool IsInitialized() const { return mTable.entrySize; } + /** * KeyType is typedef'ed for ease of use. */ @@ -134,7 +140,18 @@ public: * @return pointer to the entry class, if the key exists; nsnull if the * key doesn't exist */ - EntryType* GetEntry(KeyTypePointer aKey) const; + EntryType* GetEntry(KeyType aKey) const + { + NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); + + EntryType* entry = + NS_REINTERPRET_CAST(EntryType*, + PL_DHashTableOperate( + NS_CONST_CAST(PLDHashTable*,&mTable), + EntryType::KeyToPointer(aKey), + PL_DHASH_LOOKUP)); + return PL_DHASH_ENTRY_IS_BUSY(entry) ? entry : nsnull; + } /** * Get the entry associated with a key, or create a new entry, @@ -142,13 +159,42 @@ public: * @return pointer to the entry class retreived; nsnull only if memory can't be allocated */ - EntryType* PutEntry(KeyTypePointer aKey); + EntryType* PutEntry(KeyType aKey) + { + NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); + + return NS_STATIC_CAST(EntryType*, + PL_DHashTableOperate( + &mTable, + EntryType::KeyToPointer(aKey), + PL_DHASH_ADD)); + } /** * Remove the entry associated with a key. * @param aKey of the entry to remove */ - void RemoveEntry(KeyTypePointer aKey); + void RemoveEntry(KeyType aKey) + { + NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); + + PL_DHashTableOperate(&mTable, + EntryType::KeyToPointer(aKey), + PL_DHASH_REMOVE); + } + + /** + * Remove the entry associated with a key, but don't resize the hashtable. + * This is a low-level method, and is not recommended unless you know what + * you're doing and you need the extra performance. This method can be used + * during enumeration, while RemoveEntry() cannot. + * @param aEntry the entry-pointer to remove (obtained from GetEntry or + * the enumerator + */ + void RawRemoveEntry(EntryType* aEntry) + { + PL_DHashTableRawRemove(&mTable, aEntry); + } /** * client must provide an Enumerator function for @@ -169,12 +215,23 @@ public: * Enumerator function * @return the number of entries actually enumerated */ - PRUint32 EnumerateEntries(Enumerator enumFunc, void* userArg); + PRUint32 EnumerateEntries(Enumerator enumFunc, void* userArg) + { + NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); + + s_EnumArgs args = { enumFunc, userArg }; + return PL_DHashTableEnumerate(&mTable, s_EnumStub, &args); + } /** * remove all entries, return hashtable to "pristine" state ;) */ - void Clear(); + void Clear() + { + NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); + + PL_DHashTableEnumerate(&mTable, PL_DHashStubEnumRemove, nsnull); + } protected: PLDHashTable mTable; @@ -254,8 +311,11 @@ nsTHashtable::Init(PRUint32 initSize) if (mTable.entrySize) { NS_ERROR("nsTHashtable::Init() should not be called twice."); - return PR_FALSE; + return PR_TRUE; } + + if (!EntryType::ALLOW_MEMMOVE) + sOps.moveEntry = s_CopyEntry; if (!PL_DHashTableInit(&mTable, &sOps, nsnull, sizeof(EntryType), initSize)) { @@ -267,59 +327,6 @@ nsTHashtable::Init(PRUint32 initSize) return PR_TRUE; } -template -EntryType* -nsTHashtable::GetEntry(KeyTypePointer aKey) const -{ - NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); - - return NS_REINTERPRET_CAST(EntryType*, - PL_DHashTableOperate( - NS_CONST_CAST(PLDHashTable*,&mTable), - aKey, - PL_DHASH_LOOKUP)); -} - -template -EntryType* -nsTHashtable::PutEntry(KeyTypePointer aKey) -{ - NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); - - return (EntryType*) PL_DHashTableOperate(&mTable, aKey, PL_DHASH_ADD); -} - -template -void -nsTHashtable::RemoveEntry(KeyTypePointer aKey) -{ - NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); - - PL_DHashTableOperate(&mTable, aKey, PL_DHASH_REMOVE); - - return; -} - -template -PRUint32 -nsTHashtable::EnumerateEntries(Enumerator enumFunc, void* userArg) -{ - NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); - - s_EnumArgs args = { enumFunc, userArg }; - - return PL_DHashTableEnumerate(&mTable, s_EnumStub, &args); -} - -template -void -nsTHashtable::Clear() -{ - NS_ASSERTION(mTable.entrySize, "nsTHashtable was not initialized properly."); - - PL_DHashTableEnumerate(&mTable, PL_DHashStubEnumRemove, nsnull); -} - // static definitions template @@ -331,7 +338,7 @@ nsTHashtable::sOps = s_GetKey, s_HashKey, s_MatchEntry, - EntryType::AllowMemMove() ? ::PL_DHashMoveEntryStub : s_CopyEntry, + PL_DHashMoveEntryStub, s_ClearEntry, ::PL_DHashFinalizeStub, s_InitEntry diff --git a/mozilla/xpcom/tests/Makefile.in b/mozilla/xpcom/tests/Makefile.in index 405b0f714c7..14d5bbf3bd2 100644 --- a/mozilla/xpcom/tests/Makefile.in +++ b/mozilla/xpcom/tests/Makefile.in @@ -47,6 +47,7 @@ CPPSRCS = \ TestCOMPtrEq.cpp \ TestCRT.cpp \ TestFactory.cpp \ + TestHashtables.cpp \ TestID.cpp \ TestObserverService.cpp \ TestPermanentAtoms.cpp \ diff --git a/mozilla/xpcom/tests/TestHashtables.cpp b/mozilla/xpcom/tests/TestHashtables.cpp new file mode 100644 index 00000000000..a5b4ab90e74 --- /dev/null +++ b/mozilla/xpcom/tests/TestHashtables.cpp @@ -0,0 +1,728 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is C++ hashtable templates. + * + * The Initial Developer of the Original Code is + * Benjamin Smedberg. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "nsTHashtable.h" +#include "nsBaseHashtable.h" +#include "nsDataHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsClassHashtable.h" + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsCRT.h" +#include "nsCOMArray.h" + +class TestUniChar // for nsClassHashtable +{ +public: + TestUniChar(PRUint32 aWord) + { + cout << " TestUniChar::TestUniChar() " << aWord << endl; + mWord = aWord; + } + + ~TestUniChar() + { + cout << " TestUniChar::~TestUniChar() " << mWord << endl; + } + + PRUint32 GetChar() const { return mWord; } + +private: + PRUint32 mWord; +}; + +struct EntityNode { + const char* mStr; // never owns buffer + PRUint32 mUnicode; +}; + +EntityNode gEntities[] = { + {"nbsp",160}, + {"iexcl",161}, + {"cent",162}, + {"pound",163}, + {"curren",164}, + {"yen",165}, + {"brvbar",166}, + {"sect",167}, + {"uml",168}, + {"copy",169}, + {"ordf",170}, + {"laquo",171}, + {"not",172}, + {"shy",173}, + {"reg",174}, + {"macr",175} +}; + +#define ENTITY_COUNT (sizeof(gEntities)/sizeof(EntityNode)) + +class EntityToUnicodeEntry : public PLDHashEntryHdr +{ +public: + typedef const char* KeyType; + typedef const char* KeyTypePointer; + + EntityToUnicodeEntry(const char* aKey) { mNode = nsnull; } + EntityToUnicodeEntry(const EntityToUnicodeEntry& aEntry) { mNode = aEntry.mNode; } + ~EntityToUnicodeEntry() { }; + + const char* GetKeyPointer() const { return mNode->mStr; } + PRBool KeyEquals(const char* aEntity) const { return !strcmp(mNode->mStr, aEntity); } + static const char* KeyToPointer(const char* aEntity) { return aEntity; } + static PLDHashNumber HashKey(const char* aEntity) { return nsCRT::HashCode(aEntity); } + enum { ALLOW_MEMMOVE = PR_TRUE }; + + const EntityNode* mNode; +}; + +PLDHashOperator +nsTEnumGo(EntityToUnicodeEntry* aEntry, void* userArg) { + cout << " enumerated \"" << aEntry->mNode->mStr << "\" = " << + aEntry->mNode->mUnicode << endl; + + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsTEnumStop(EntityToUnicodeEntry* aEntry, void* userArg) { + cout << " enumerated \"" << aEntry->mNode->mStr << "\" = " << + aEntry->mNode->mUnicode << endl; + + return PL_DHASH_REMOVE; +} + +void +testTHashtable(nsTHashtable& hash, PRUint32 numEntries) { + cout << "Filling hash with " << numEntries << " entries." << endl; + + PRUint32 i; + for (i = 0; i < numEntries; ++i) { + cout << " Putting entry \"" << gEntities[i].mStr << "\"..."; + EntityToUnicodeEntry* entry = + hash.PutEntry(gEntities[i].mStr); + + if (!entry) { + cout << "FAILED" << endl; + exit (2); + } + cout << "OK..."; + + if (entry->mNode) { + cout << "entry already exists!" << endl; + exit (3); + } + cout << endl; + + entry->mNode = &gEntities[i]; + } + + cout << "Testing Get:" << endl; + + for (i = 0; i < numEntries; ++i) { + cout << " Getting entry \"" << gEntities[i].mStr << "\"..."; + EntityToUnicodeEntry* entry = + hash.GetEntry(gEntities[i].mStr); + + if (!entry) { + cout << "FAILED" << endl; + exit (4); + } + + cout << "Found " << entry->mNode->mUnicode << endl; + } + + cout << "Testing non-existent entries..."; + + EntityToUnicodeEntry* entry = + hash.GetEntry("xxxy"); + + if (entry) { + cout << "FOUND! BAD!" << endl; + exit (5); + } + + cout << "not found; good." << endl; + + cout << "Enumerating:" << endl; + PRUint32 count = hash.EnumerateEntries(nsTEnumGo, nsnull); + if (count != numEntries) { + cout << " Bad count!" << endl; + exit (6); + } +} + +PLDHashOperator +nsDEnumRead(const PRUint32& aKey, const char* aData, void* userArg) { + cout << " enumerated " << aKey << " = \"" << aData << "\"" << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsDEnum(const PRUint32& aKey, const char*& aData, void* userArg) { + cout << " enumerated " << aKey << " = \"" << aData << "\"" << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsCEnumRead(const nsACString& aKey, TestUniChar* aData, void* userArg) { + cout << " enumerated \"" << PromiseFlatCString(aKey).get() << "\" = " << + aData->GetChar() << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsCEnum(const nsACString& aKey, nsAutoPtr& aData, void* userArg) { + cout << " enumerated \"" << PromiseFlatCString(aKey).get() << "\" = " << + aData->GetChar() << endl; + return PL_DHASH_NEXT; +} + +// +// all this nsIFoo stuff was copied wholesale from TestCOMPTr.cpp +// + +#define NS_IFOO_IID \ +{ 0x6f7652e0, 0xee43, 0x11d1, \ + { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } } + +class IFoo : public nsISupports + { + public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IFOO_IID) + + public: + IFoo(); + virtual ~IFoo(); + + NS_IMETHOD_(nsrefcnt) AddRef(); + NS_IMETHOD_(nsrefcnt) Release(); + NS_IMETHOD QueryInterface( const nsIID&, void** ); + + NS_IMETHOD SetString(const nsACString& /*in*/ aString); + NS_IMETHOD GetString(nsACString& /*out*/ aString); + + static void print_totals(); + + private: + unsigned int refcount_; + + static unsigned int total_constructions_; + static unsigned int total_destructions_; + nsCString mString; + }; + +unsigned int IFoo::total_constructions_; +unsigned int IFoo::total_destructions_; + +void +IFoo::print_totals() + { + cout << "total constructions/destructions --> " << total_constructions_ << "/" << total_destructions_ << endl; + } + +IFoo::IFoo() + : refcount_(0) + { + ++total_constructions_; + cout << " new IFoo@" << NS_STATIC_CAST(void*, this) << " [#" << total_constructions_ << "]" << endl; + } + +IFoo::~IFoo() + { + ++total_destructions_; + cout << "IFoo@" << NS_STATIC_CAST(void*, this) << "::~IFoo()" << " [#" << total_destructions_ << "]" << endl; + } + +nsrefcnt +IFoo::AddRef() + { + ++refcount_; + cout << "IFoo@" << NS_STATIC_CAST(void*, this) << "::AddRef(), refcount --> " << refcount_ << endl; + return refcount_; + } + +nsrefcnt +IFoo::Release() + { + int wrap_message = (refcount_ == 1); + if ( wrap_message ) + cout << ">>"; + + --refcount_; + cout << "IFoo@" << NS_STATIC_CAST(void*, this) << "::Release(), refcount --> " << refcount_ << endl; + + if ( !refcount_ ) + { + cout << " delete IFoo@" << NS_STATIC_CAST(void*, this) << endl; + delete this; + } + + if ( wrap_message ) + cout << "<>CreateIFoo() --> "; + IFoo* foop = new IFoo(); + cout << "IFoo@" << NS_STATIC_CAST(void*, foop) << endl; + + foop->AddRef(); + *result = foop; + + cout << "<GetString(str); + + cout << " enumerated " << aKey << " = \"" << str.get() << "\"" << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsIEnum(const PRUint32& aKey, nsCOMPtr& aData, void* userArg) { + nsCAutoString str; + aData->GetString(str); + + cout << " enumerated " << aKey << " = \"" << str.get() << "\"" << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsIEnum2Read(nsISupports* aKey, PRUint32 aData, void* userArg) { + nsCAutoString str; + nsCOMPtr foo = do_QueryInterface(aKey); + foo->GetString(str); + + + cout << " enumerated \"" << str.get() << "\" = " << aData << endl; + return PL_DHASH_NEXT; +} + +PLDHashOperator +nsIEnum2(nsISupports* aKey, PRUint32& aData, void* userArg) { + nsCAutoString str; + nsCOMPtr foo = do_QueryInterface(aKey); + foo->GetString(str); + + cout << " enumerated \"" << str.get() << "\" = " << aData << endl; + return PL_DHASH_NEXT; +} + +int +main(void) { + // check an nsTHashtable + nsTHashtable EntityToUnicode; + + cout << "Initializing nsTHashtable..."; + if (!EntityToUnicode.Init(ENTITY_COUNT)) { + cout << "FAILED" << endl; + exit (1); + } + cout << "OK" << endl; + + cout << "Partially filling nsTHashtable:" << endl; + testTHashtable(EntityToUnicode, 5); + + cout << "Enumerate-removing..." << endl; + PRUint32 count = EntityToUnicode.EnumerateEntries(nsTEnumStop, nsnull); + if (count != 5) { + cout << "wrong count" << endl; + exit (7); + } + cout << "OK" << endl; + + cout << "Check enumeration..."; + count = EntityToUnicode.EnumerateEntries(nsTEnumGo, nsnull); + if (count) { + cout << "entries remain in table!" << endl; + exit (8); + } + cout << "OK" << endl; + + cout << "Filling nsTHashtable:" << endl; + testTHashtable(EntityToUnicode, ENTITY_COUNT); + + cout << "Clearing..."; + EntityToUnicode.Clear(); + cout << "OK" << endl; + + cout << "Check enumeration..."; + count = EntityToUnicode.EnumerateEntries(nsTEnumGo, nsnull); + if (count) { + cout << "entries remain in table!" << endl; + exit (9); + } + cout << "OK" << endl; + + // + // now check a data-hashtable + // + + nsDataHashtable UniToEntity; + + cout << "Initializing nsDataHashtable..."; + if (!UniToEntity.Init(ENTITY_COUNT, PR_FALSE)) { + cout << "FAILED" << endl; + exit (10); + } + cout << "OK" << endl; + + cout << "Filling hash with " << ENTITY_COUNT << " entries." << endl; + + PRUint32 i; + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Putting entry " << gEntities[i].mUnicode << "..."; + if (!UniToEntity.Put(gEntities[i].mUnicode, gEntities[i].mStr)) { + cout << "FAILED" << endl; + exit (11); + } + cout << "OK..." << endl; + } + + cout << "Testing Get:" << endl; + const char* str; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Getting entry " << gEntities[i].mUnicode << "..."; + if (!UniToEntity.Get(gEntities[i].mUnicode, &str)) { + cout << "FAILED" << endl; + exit (12); + } + + cout << "Found " << str << endl; + } + + cout << "Testing non-existent entries..."; + if (UniToEntity.Get(99446, &str)) { + cout << "FOUND! BAD!" << endl; + exit (13); + } + + cout << "not found; good." << endl; + + cout << "Enumerating:" << endl; + + count = UniToEntity.EnumerateRead(nsDEnumRead, nsnull); + if (count != ENTITY_COUNT) { + cout << " Bad count!" << endl; + exit (14); + } + + cout << "Clearing..."; + UniToEntity.Clear(); + cout << "OK" << endl; + + cout << "Checking count..."; + count = UniToEntity.Enumerate(nsDEnum, nsnull); + if (count) { + cout << " Clear did not remove all entries." << endl; + exit (15); + } + + cout << "OK" << endl; + + // + // now check a class-hashtable + // + + nsClassHashtable EntToUniClass; + + cout << "Initializing nsClassHashtable..."; + if (!EntToUniClass.Init(ENTITY_COUNT, PR_FALSE)) { + cout << "FAILED" << endl; + exit (16); + } + cout << "OK" << endl; + + cout << "Filling hash with " << ENTITY_COUNT << " entries." << endl; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Putting entry " << gEntities[i].mUnicode << "..."; + TestUniChar* temp = new TestUniChar(gEntities[i].mUnicode); + + if (!EntToUniClass.Put(NS_LITERAL_CSTRING(gEntities[i].mStr), temp)) { + cout << "FAILED" << endl; + delete temp; + exit (17); + } + cout << "OK..." << endl; + } + + cout << "Testing Get:" << endl; + TestUniChar* myChar; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Getting entry " << gEntities[i].mStr << "..."; + if (!EntToUniClass.Get(NS_LITERAL_CSTRING(gEntities[i].mStr), &myChar)) { + cout << "FAILED" << endl; + exit (18); + } + + cout << "Found " << myChar->GetChar() << endl; + } + + cout << "Testing non-existent entries..."; + if (EntToUniClass.Get(NS_LITERAL_CSTRING("xxxx"), &myChar)) { + cout << "FOUND! BAD!" << endl; + exit (19); + } + + cout << "not found; good." << endl; + + cout << "Enumerating:" << endl; + + count = EntToUniClass.EnumerateRead(nsCEnumRead, nsnull); + if (count != ENTITY_COUNT) { + cout << " Bad count!" << endl; + exit (20); + } + + cout << "Clearing..." << endl; + EntToUniClass.Clear(); + cout << " Clearing OK" << endl; + + cout << "Checking count..."; + count = EntToUniClass.Enumerate(nsCEnum, nsnull); + if (count) { + cout << " Clear did not remove all entries." << endl; + exit (21); + } + + cout << "OK" << endl; + + // + // now check a data-hashtable with an interface key + // + + nsDataHashtable EntToUniClass2; + + cout << "Initializing nsDataHashtable with interface key..."; + if (!EntToUniClass2.Init(ENTITY_COUNT, PR_FALSE)) { + cout << "FAILED" << endl; + exit (22); + } + cout << "OK" << endl; + + cout << "Filling hash with " << ENTITY_COUNT << " entries." << endl; + + nsCOMArray fooArray; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Putting entry " << gEntities[i].mUnicode << "..."; + nsCOMPtr foo; + CreateIFoo(getter_AddRefs(foo)); + foo->SetString(NS_LITERAL_CSTRING(gEntities[i].mStr)); + + + fooArray.InsertObjectAt(foo, i); + + if (!EntToUniClass2.Put(foo, gEntities[i].mUnicode)) { + cout << "FAILED" << endl; + exit (23); + } + cout << "OK..." << endl; + } + + cout << "Testing Get:" << endl; + PRUint32 myChar2; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Getting entry " << gEntities[i].mStr << "..."; + + if (!EntToUniClass2.Get(fooArray[i], &myChar2)) { + cout << "FAILED" << endl; + exit (24); + } + + cout << "Found " << myChar2 << endl; + } + + cout << "Testing non-existent entries..."; + if (EntToUniClass2.Get((nsISupports*) 0x55443316, &myChar2)) { + cout << "FOUND! BAD!" << endl; + exit (25); + } + + cout << "not found; good." << endl; + + cout << "Enumerating:" << endl; + + count = EntToUniClass2.EnumerateRead(nsIEnum2Read, nsnull); + if (count != ENTITY_COUNT) { + cout << " Bad count!" << endl; + exit (26); + } + + cout << "Clearing..." << endl; + EntToUniClass2.Clear(); + cout << " Clearing OK" << endl; + + cout << "Checking count..."; + count = EntToUniClass2.Enumerate(nsIEnum2, nsnull); + if (count) { + cout << " Clear did not remove all entries." << endl; + exit (27); + } + + cout << "OK" << endl; + + // + // now check an interface-hashtable with an PRUint32 key + // + + nsInterfaceHashtable UniToEntClass2; + + cout << "Initializing nsInterfaceHashtable..."; + if (!UniToEntClass2.Init(ENTITY_COUNT, PR_FALSE)) { + cout << "FAILED" << endl; + exit (28); + } + cout << "OK" << endl; + + cout << "Filling hash with " << ENTITY_COUNT << " entries." << endl; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Putting entry " << gEntities[i].mUnicode << "..."; + nsCOMPtr foo; + CreateIFoo(getter_AddRefs(foo)); + foo->SetString(NS_LITERAL_CSTRING(gEntities[i].mStr)); + + if (!UniToEntClass2.Put(gEntities[i].mUnicode, foo)) { + cout << "FAILED" << endl; + exit (29); + } + cout << "OK..." << endl; + } + + cout << "Testing Get:" << endl; + + for (i = 0; i < ENTITY_COUNT; ++i) { + cout << " Getting entry " << gEntities[i].mStr << "..."; + + nsCOMPtr myEnt; + if (!UniToEntClass2.Get(gEntities[i].mUnicode, getter_AddRefs(myEnt))) { + cout << "FAILED" << endl; + exit (30); + } + + nsCAutoString str; + myEnt->GetString(str); + cout << "Found " << str.get() << endl; + } + + cout << "Testing non-existent entries..."; + nsCOMPtr myEnt; + if (UniToEntClass2.Get(9462, getter_AddRefs(myEnt))) { + cout << "FOUND! BAD!" << endl; + exit (31); + } + + cout << "not found; good." << endl; + + cout << "Enumerating:" << endl; + + count = UniToEntClass2.EnumerateRead(nsIEnumRead, nsnull); + if (count != ENTITY_COUNT) { + cout << " Bad count!" << endl; + exit (32); + } + + cout << "Clearing..." << endl; + UniToEntClass2.Clear(); + cout << " Clearing OK" << endl; + + cout << "Checking count..."; + count = UniToEntClass2.Enumerate(nsIEnum, nsnull); + if (count) { + cout << " Clear did not remove all entries." << endl; + exit (33); + } + + cout << "OK" << endl; + + return 0; +}