michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsTHashtable.h" michael@0: #include "nsBaseHashtable.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsInterfaceHashtable.h" michael@0: #include "nsClassHashtable.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsISupports.h" michael@0: #include "nsCOMArray.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include michael@0: michael@0: namespace TestHashtables { michael@0: michael@0: class TestUniChar // for nsClassHashtable michael@0: { michael@0: public: michael@0: TestUniChar(uint32_t aWord) michael@0: { michael@0: printf(" TestUniChar::TestUniChar() %u\n", aWord); michael@0: mWord = aWord; michael@0: } michael@0: michael@0: ~TestUniChar() michael@0: { michael@0: printf(" TestUniChar::~TestUniChar() %u\n", mWord); michael@0: } michael@0: michael@0: uint32_t GetChar() const { return mWord; } michael@0: michael@0: private: michael@0: uint32_t mWord; michael@0: }; michael@0: michael@0: struct EntityNode { michael@0: const char* mStr; // never owns buffer michael@0: uint32_t mUnicode; michael@0: }; michael@0: michael@0: EntityNode gEntities[] = { michael@0: {"nbsp",160}, michael@0: {"iexcl",161}, michael@0: {"cent",162}, michael@0: {"pound",163}, michael@0: {"curren",164}, michael@0: {"yen",165}, michael@0: {"brvbar",166}, michael@0: {"sect",167}, michael@0: {"uml",168}, michael@0: {"copy",169}, michael@0: {"ordf",170}, michael@0: {"laquo",171}, michael@0: {"not",172}, michael@0: {"shy",173}, michael@0: {"reg",174}, michael@0: {"macr",175} michael@0: }; michael@0: michael@0: #define ENTITY_COUNT (unsigned(sizeof(gEntities)/sizeof(EntityNode))) michael@0: michael@0: class EntityToUnicodeEntry : public PLDHashEntryHdr michael@0: { michael@0: public: michael@0: typedef const char* KeyType; michael@0: typedef const char* KeyTypePointer; michael@0: michael@0: EntityToUnicodeEntry(const char* aKey) { mNode = nullptr; } michael@0: EntityToUnicodeEntry(const EntityToUnicodeEntry& aEntry) { mNode = aEntry.mNode; } michael@0: ~EntityToUnicodeEntry() { } michael@0: michael@0: bool KeyEquals(const char* aEntity) const { return !strcmp(mNode->mStr, aEntity); } michael@0: static const char* KeyToPointer(const char* aEntity) { return aEntity; } michael@0: static PLDHashNumber HashKey(const char* aEntity) { return mozilla::HashString(aEntity); } michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: const EntityNode* mNode; michael@0: }; michael@0: michael@0: PLDHashOperator michael@0: nsTEnumGo(EntityToUnicodeEntry* aEntry, void* userArg) { michael@0: printf(" enumerated \"%s\" = %u\n", michael@0: aEntry->mNode->mStr, aEntry->mNode->mUnicode); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsTEnumStop(EntityToUnicodeEntry* aEntry, void* userArg) { michael@0: printf(" enumerated \"%s\" = %u\n", michael@0: aEntry->mNode->mStr, aEntry->mNode->mUnicode); michael@0: michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: testTHashtable(nsTHashtable& hash, uint32_t numEntries) { michael@0: printf("Filling hash with %d entries.\n", numEntries); michael@0: michael@0: uint32_t i; michael@0: for (i = 0; i < numEntries; ++i) { michael@0: printf(" Putting entry \"%s\"...", gEntities[i].mStr); michael@0: EntityToUnicodeEntry* entry = michael@0: hash.PutEntry(gEntities[i].mStr); michael@0: michael@0: if (!entry) { michael@0: printf("FAILED\n"); michael@0: exit (2); michael@0: } michael@0: printf("OK..."); michael@0: michael@0: if (entry->mNode) { michael@0: printf("entry already exists!\n"); michael@0: exit (3); michael@0: } michael@0: printf("\n"); michael@0: michael@0: entry->mNode = &gEntities[i]; michael@0: } michael@0: michael@0: printf("Testing Get:\n"); michael@0: michael@0: for (i = 0; i < numEntries; ++i) { michael@0: printf(" Getting entry \"%s\"...", gEntities[i].mStr); michael@0: EntityToUnicodeEntry* entry = michael@0: hash.GetEntry(gEntities[i].mStr); michael@0: michael@0: if (!entry) { michael@0: printf("FAILED\n"); michael@0: exit (4); michael@0: } michael@0: michael@0: printf("Found %u\n", entry->mNode->mUnicode); michael@0: } michael@0: michael@0: printf("Testing nonexistent entries..."); michael@0: michael@0: EntityToUnicodeEntry* entry = michael@0: hash.GetEntry("xxxy"); michael@0: michael@0: if (entry) { michael@0: printf("FOUND! BAD!\n"); michael@0: exit (5); michael@0: } michael@0: michael@0: printf("not found; good.\n"); michael@0: michael@0: printf("Enumerating:\n"); michael@0: uint32_t count = hash.EnumerateEntries(nsTEnumGo, nullptr); michael@0: if (count != numEntries) { michael@0: printf(" Bad count!\n"); michael@0: exit (6); michael@0: } michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsDEnumRead(const uint32_t& aKey, const char* aData, void* userArg) { michael@0: printf(" enumerated %u = \"%s\"\n", aKey, aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsDEnum(const uint32_t& aKey, const char*& aData, void* userArg) { michael@0: printf(" enumerated %u = \"%s\"\n", aKey, aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsCEnumRead(const nsACString& aKey, TestUniChar* aData, void* userArg) { michael@0: printf(" enumerated \"%s\" = %c\n", michael@0: PromiseFlatCString(aKey).get(), aData->GetChar()); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsCEnum(const nsACString& aKey, nsAutoPtr& aData, void* userArg) { michael@0: printf(" enumerated \"%s\" = %c\n", michael@0: PromiseFlatCString(aKey).get(), aData->GetChar()); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // michael@0: // all this nsIFoo stuff was copied wholesale from TestCOMPtr.cpp michael@0: // michael@0: michael@0: #define NS_IFOO_IID \ michael@0: { 0x6f7652e0, 0xee43, 0x11d1, \ michael@0: { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } } michael@0: michael@0: class IFoo MOZ_FINAL : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID) michael@0: michael@0: IFoo(); michael@0: michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef(); michael@0: NS_IMETHOD_(MozExternalRefCountType) Release(); michael@0: NS_IMETHOD QueryInterface( const nsIID&, void** ); michael@0: michael@0: NS_IMETHOD SetString(const nsACString& /*in*/ aString); michael@0: NS_IMETHOD GetString(nsACString& /*out*/ aString); michael@0: michael@0: static void print_totals(); michael@0: michael@0: private: michael@0: ~IFoo(); michael@0: michael@0: unsigned int refcount_; michael@0: michael@0: static unsigned int total_constructions_; michael@0: static unsigned int total_destructions_; michael@0: nsCString mString; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID) michael@0: michael@0: unsigned int IFoo::total_constructions_; michael@0: unsigned int IFoo::total_destructions_; michael@0: michael@0: void michael@0: IFoo::print_totals() michael@0: { michael@0: printf("total constructions/destructions --> %d/%d\n", michael@0: total_constructions_, total_destructions_); michael@0: } michael@0: michael@0: IFoo::IFoo() michael@0: : refcount_(0) michael@0: { michael@0: ++total_constructions_; michael@0: printf(" new IFoo@%p [#%d]\n", michael@0: static_cast(this), total_constructions_); michael@0: } michael@0: michael@0: IFoo::~IFoo() michael@0: { michael@0: ++total_destructions_; michael@0: printf("IFoo@%p::~IFoo() [#%d]\n", michael@0: static_cast(this), total_destructions_); michael@0: } michael@0: michael@0: MozExternalRefCountType michael@0: IFoo::AddRef() michael@0: { michael@0: ++refcount_; michael@0: printf("IFoo@%p::AddRef(), refcount --> %d\n", michael@0: static_cast(this), refcount_); michael@0: return refcount_; michael@0: } michael@0: michael@0: MozExternalRefCountType michael@0: IFoo::Release() michael@0: { michael@0: int newcount = --refcount_; michael@0: if ( newcount == 0 ) michael@0: printf(">>"); michael@0: michael@0: printf("IFoo@%p::Release(), refcount --> %d\n", michael@0: static_cast(this), refcount_); michael@0: michael@0: if ( newcount == 0 ) michael@0: { michael@0: printf(" delete IFoo@%p\n", static_cast(this)); michael@0: printf("<(this)); michael@0: delete this; michael@0: } michael@0: michael@0: return newcount; michael@0: } michael@0: michael@0: nsresult michael@0: IFoo::QueryInterface( const nsIID& aIID, void** aResult ) michael@0: { michael@0: printf("IFoo@%p::QueryInterface()\n", static_cast(this)); michael@0: nsISupports* rawPtr = 0; michael@0: nsresult status = NS_OK; michael@0: michael@0: if ( aIID.Equals(NS_GET_IID(IFoo)) ) michael@0: rawPtr = this; michael@0: else michael@0: { michael@0: nsID iid_of_ISupports = NS_ISUPPORTS_IID; michael@0: if ( aIID.Equals(iid_of_ISupports) ) michael@0: rawPtr = static_cast(this); michael@0: else michael@0: status = NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: NS_IF_ADDREF(rawPtr); michael@0: *aResult = rawPtr; michael@0: michael@0: return status; michael@0: } michael@0: michael@0: nsresult michael@0: IFoo::SetString(const nsACString& aString) michael@0: { michael@0: mString = aString; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: IFoo::GetString(nsACString& aString) michael@0: { michael@0: aString = mString; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CreateIFoo( IFoo** result ) michael@0: // a typical factory function (that calls AddRef) michael@0: { michael@0: printf(" >>CreateIFoo() --> "); michael@0: IFoo* foop = new IFoo(); michael@0: printf("IFoo@%p\n", static_cast(foop)); michael@0: michael@0: foop->AddRef(); michael@0: *result = foop; michael@0: michael@0: printf("<GetString(str); michael@0: michael@0: printf(" enumerated %u = \"%s\"\n", aKey, str.get()); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsIEnum(const uint32_t& aKey, nsCOMPtr& aData, void* userArg) { michael@0: nsAutoCString str; michael@0: aData->GetString(str); michael@0: michael@0: printf(" enumerated %u = \"%s\"\n", aKey, str.get()); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsIEnum2Read(nsISupports* aKey, uint32_t aData, void* userArg) { michael@0: nsAutoCString str; michael@0: nsCOMPtr foo = do_QueryInterface(aKey); michael@0: foo->GetString(str); michael@0: michael@0: michael@0: printf(" enumerated \"%s\" = %u\n", str.get(), aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsIEnum2(nsISupports* aKey, uint32_t& aData, void* userArg) { michael@0: nsAutoCString str; michael@0: nsCOMPtr foo = do_QueryInterface(aKey); michael@0: foo->GetString(str); michael@0: michael@0: printf(" enumerated \"%s\" = %u\n", str.get(), aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: } michael@0: michael@0: using namespace TestHashtables; michael@0: michael@0: int michael@0: main(void) { michael@0: // check an nsTHashtable michael@0: printf("Initializing nsTHashtable..."); michael@0: nsTHashtable EntityToUnicode(ENTITY_COUNT); michael@0: printf("OK\n"); michael@0: michael@0: printf("Partially filling nsTHashtable:\n"); michael@0: testTHashtable(EntityToUnicode, 5); michael@0: michael@0: printf("Enumerate-removing...\n"); michael@0: uint32_t count = EntityToUnicode.EnumerateEntries(nsTEnumStop, nullptr); michael@0: if (count != 5) { michael@0: printf("wrong count\n"); michael@0: exit (7); michael@0: } michael@0: printf("OK\n"); michael@0: michael@0: printf("Check enumeration..."); michael@0: count = EntityToUnicode.EnumerateEntries(nsTEnumGo, nullptr); michael@0: if (count) { michael@0: printf("entries remain in table!\n"); michael@0: exit (8); michael@0: } michael@0: printf("OK\n"); michael@0: michael@0: printf("Filling nsTHashtable:\n"); michael@0: testTHashtable(EntityToUnicode, ENTITY_COUNT); michael@0: michael@0: printf("Clearing..."); michael@0: EntityToUnicode.Clear(); michael@0: printf("OK\n"); michael@0: michael@0: printf("Check enumeration..."); michael@0: count = EntityToUnicode.EnumerateEntries(nsTEnumGo, nullptr); michael@0: if (count) { michael@0: printf("entries remain in table!\n"); michael@0: exit (9); michael@0: } michael@0: printf("OK\n"); michael@0: michael@0: // michael@0: // now check a data-hashtable michael@0: // michael@0: michael@0: printf("Initializing nsDataHashtable..."); michael@0: nsDataHashtable UniToEntity(ENTITY_COUNT); michael@0: printf("OK\n"); michael@0: michael@0: printf("Filling hash with %u entries.\n", ENTITY_COUNT); michael@0: michael@0: uint32_t i; michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Putting entry %u...", gEntities[i].mUnicode); michael@0: UniToEntity.Put(gEntities[i].mUnicode, gEntities[i].mStr); michael@0: printf("OK...\n"); michael@0: } michael@0: michael@0: printf("Testing Get:\n"); michael@0: const char* str; michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Getting entry %u...", gEntities[i].mUnicode); michael@0: if (!UniToEntity.Get(gEntities[i].mUnicode, &str)) { michael@0: printf("FAILED\n"); michael@0: exit (12); michael@0: } michael@0: michael@0: printf("Found %s\n", str); michael@0: } michael@0: michael@0: printf("Testing nonexistent entries..."); michael@0: if (UniToEntity.Get(99446, &str)) { michael@0: printf("FOUND! BAD!\n"); michael@0: exit (13); michael@0: } michael@0: michael@0: printf("not found; good.\n"); michael@0: michael@0: printf("Enumerating:\n"); michael@0: michael@0: count = UniToEntity.EnumerateRead(nsDEnumRead, nullptr); michael@0: if (count != ENTITY_COUNT) { michael@0: printf(" Bad count!\n"); michael@0: exit (14); michael@0: } michael@0: michael@0: printf("Clearing..."); michael@0: UniToEntity.Clear(); michael@0: printf("OK\n"); michael@0: michael@0: printf("Checking count..."); michael@0: count = UniToEntity.Enumerate(nsDEnum, nullptr); michael@0: if (count) { michael@0: printf(" Clear did not remove all entries.\n"); michael@0: exit (15); michael@0: } michael@0: michael@0: printf("OK\n"); michael@0: michael@0: // michael@0: // now check a class-hashtable michael@0: // michael@0: michael@0: printf("Initializing nsClassHashtable..."); michael@0: nsClassHashtable EntToUniClass(ENTITY_COUNT); michael@0: printf("OK\n"); michael@0: michael@0: printf("Filling hash with %u entries.\n", ENTITY_COUNT); michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Putting entry %u...", gEntities[i].mUnicode); michael@0: TestUniChar* temp = new TestUniChar(gEntities[i].mUnicode); michael@0: michael@0: EntToUniClass.Put(nsDependentCString(gEntities[i].mStr), temp); michael@0: printf("OK...\n"); michael@0: } michael@0: michael@0: printf("Testing Get:\n"); michael@0: TestUniChar* myChar; michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Getting entry %s...", gEntities[i].mStr); michael@0: if (!EntToUniClass.Get(nsDependentCString(gEntities[i].mStr), &myChar)) { michael@0: printf("FAILED\n"); michael@0: exit (18); michael@0: } michael@0: michael@0: printf("Found %c\n", myChar->GetChar()); michael@0: } michael@0: michael@0: printf("Testing nonexistent entries..."); michael@0: if (EntToUniClass.Get(NS_LITERAL_CSTRING("xxxx"), &myChar)) { michael@0: printf("FOUND! BAD!\n"); michael@0: exit (19); michael@0: } michael@0: michael@0: printf("not found; good.\n"); michael@0: michael@0: printf("Enumerating:\n"); michael@0: michael@0: count = EntToUniClass.EnumerateRead(nsCEnumRead, nullptr); michael@0: if (count != ENTITY_COUNT) { michael@0: printf(" Bad count!\n"); michael@0: exit (20); michael@0: } michael@0: michael@0: printf("Clearing...\n"); michael@0: EntToUniClass.Clear(); michael@0: printf(" Clearing OK\n"); michael@0: michael@0: printf("Checking count..."); michael@0: count = EntToUniClass.Enumerate(nsCEnum, nullptr); michael@0: if (count) { michael@0: printf(" Clear did not remove all entries.\n"); michael@0: exit (21); michael@0: } michael@0: michael@0: printf("OK\n"); michael@0: michael@0: // michael@0: // now check a data-hashtable with an interface key michael@0: // michael@0: michael@0: printf("Initializing nsDataHashtable with interface key..."); michael@0: nsDataHashtable EntToUniClass2(ENTITY_COUNT); michael@0: printf("OK\n"); michael@0: michael@0: printf("Filling hash with %u entries.\n", ENTITY_COUNT); michael@0: michael@0: nsCOMArray fooArray; michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Putting entry %u...", gEntities[i].mUnicode); michael@0: nsCOMPtr foo; michael@0: CreateIFoo(getter_AddRefs(foo)); michael@0: foo->SetString(nsDependentCString(gEntities[i].mStr)); michael@0: michael@0: michael@0: fooArray.InsertObjectAt(foo, i); michael@0: michael@0: EntToUniClass2.Put(foo, gEntities[i].mUnicode); michael@0: printf("OK...\n"); michael@0: } michael@0: michael@0: printf("Testing Get:\n"); michael@0: uint32_t myChar2; michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Getting entry %s...", gEntities[i].mStr); michael@0: michael@0: if (!EntToUniClass2.Get(fooArray[i], &myChar2)) { michael@0: printf("FAILED\n"); michael@0: exit (24); michael@0: } michael@0: michael@0: printf("Found %c\n", myChar2); michael@0: } michael@0: michael@0: printf("Testing nonexistent entries..."); michael@0: if (EntToUniClass2.Get((nsISupports*) 0x55443316, &myChar2)) { michael@0: printf("FOUND! BAD!\n"); michael@0: exit (25); michael@0: } michael@0: michael@0: printf("not found; good.\n"); michael@0: michael@0: printf("Enumerating:\n"); michael@0: michael@0: count = EntToUniClass2.EnumerateRead(nsIEnum2Read, nullptr); michael@0: if (count != ENTITY_COUNT) { michael@0: printf(" Bad count!\n"); michael@0: exit (26); michael@0: } michael@0: michael@0: printf("Clearing...\n"); michael@0: EntToUniClass2.Clear(); michael@0: printf(" Clearing OK\n"); michael@0: michael@0: printf("Checking count..."); michael@0: count = EntToUniClass2.Enumerate(nsIEnum2, nullptr); michael@0: if (count) { michael@0: printf(" Clear did not remove all entries.\n"); michael@0: exit (27); michael@0: } michael@0: michael@0: printf("OK\n"); michael@0: michael@0: // michael@0: // now check an interface-hashtable with an uint32_t key michael@0: // michael@0: michael@0: printf("Initializing nsInterfaceHashtable..."); michael@0: nsInterfaceHashtable UniToEntClass2(ENTITY_COUNT); michael@0: printf("OK\n"); michael@0: michael@0: printf("Filling hash with %u entries.\n", ENTITY_COUNT); michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Putting entry %u...", gEntities[i].mUnicode); michael@0: nsCOMPtr foo; michael@0: CreateIFoo(getter_AddRefs(foo)); michael@0: foo->SetString(nsDependentCString(gEntities[i].mStr)); michael@0: michael@0: UniToEntClass2.Put(gEntities[i].mUnicode, foo); michael@0: printf("OK...\n"); michael@0: } michael@0: michael@0: printf("Testing Get:\n"); michael@0: michael@0: for (i = 0; i < ENTITY_COUNT; ++i) { michael@0: printf(" Getting entry %s...", gEntities[i].mStr); michael@0: michael@0: nsCOMPtr myEnt; michael@0: if (!UniToEntClass2.Get(gEntities[i].mUnicode, getter_AddRefs(myEnt))) { michael@0: printf("FAILED\n"); michael@0: exit (30); michael@0: } michael@0: michael@0: nsAutoCString str; michael@0: myEnt->GetString(str); michael@0: printf("Found %s\n", str.get()); michael@0: } michael@0: michael@0: printf("Testing nonexistent entries..."); michael@0: nsCOMPtr myEnt; michael@0: if (UniToEntClass2.Get(9462, getter_AddRefs(myEnt))) { michael@0: printf("FOUND! BAD!\n"); michael@0: exit (31); michael@0: } michael@0: michael@0: printf("not found; good.\n"); michael@0: michael@0: printf("Enumerating:\n"); michael@0: michael@0: count = UniToEntClass2.EnumerateRead(nsIEnumRead, nullptr); michael@0: if (count != ENTITY_COUNT) { michael@0: printf(" Bad count!\n"); michael@0: exit (32); michael@0: } michael@0: michael@0: printf("Clearing...\n"); michael@0: UniToEntClass2.Clear(); michael@0: printf(" Clearing OK\n"); michael@0: michael@0: printf("Checking count..."); michael@0: count = UniToEntClass2.Enumerate(nsIEnum, nullptr); michael@0: if (count) { michael@0: printf(" Clear did not remove all entries.\n"); michael@0: exit (33); michael@0: } michael@0: michael@0: printf("OK\n"); michael@0: michael@0: return 0; michael@0: }