diff options
Diffstat (limited to '')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/ds/nsHashtable.cpp | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/ds/nsHashtable.cpp b/src/libs/xpcom18a4/xpcom/ds/nsHashtable.cpp new file mode 100644 index 00000000..ce74fb28 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/ds/nsHashtable.cpp @@ -0,0 +1,896 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 ***** + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2 + */ + +#include <string.h> +#include "prmem.h" +#include "prlog.h" +#include "nsHashtable.h" +#include "nsReadableUtils.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsCRT.h" + +struct HTEntry : PLDHashEntryHdr +{ + nsHashKey* key; + void* value; +}; + +// +// Key operations +// + +PR_STATIC_CALLBACK(PRBool) +matchKeyEntry(PLDHashTable*, const PLDHashEntryHdr* entry, + const void* key) +{ + const HTEntry* hashEntry = + NS_STATIC_CAST(const HTEntry*, entry); + + if (hashEntry->key == key) + return PR_TRUE; + + const nsHashKey* otherKey = NS_REINTERPRET_CAST(const nsHashKey*, key); + return otherKey->Equals(hashEntry->key); +} + +PR_STATIC_CALLBACK(PLDHashNumber) +hashKey(PLDHashTable* table, const void* key) +{ + const nsHashKey* hashKey = NS_STATIC_CAST(const nsHashKey*, key); + + return hashKey->HashCode(); +} + +PR_STATIC_CALLBACK(void) +clearHashEntry(PLDHashTable* table, PLDHashEntryHdr* entry) +{ + HTEntry* hashEntry = NS_STATIC_CAST(HTEntry*, entry); + + // leave it up to the nsHashKey destructor to free the "value" + delete hashEntry->key; + hashEntry->key = nsnull; + hashEntry->value = nsnull; // probably not necessary, but for + // sanity's sake +} + + +static const PLDHashTableOps hashtableOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + hashKey, + matchKeyEntry, + PL_DHashMoveEntryStub, + clearHashEntry, + PL_DHashFinalizeStub, + nsnull, +}; + + +// +// Enumerator callback +// + +struct _HashEnumerateArgs { + nsHashtableEnumFunc fn; + void* arg; +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +hashEnumerate(PLDHashTable* table, PLDHashEntryHdr* hdr, PRUint32 i, void *arg) +{ + _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg; + HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr); + + switch (thunk->fn(entry->key, entry->value, thunk->arg)) { + case kHashEnumerateNext: + return PL_DHASH_NEXT; + case kHashEnumerateRemove: + return PL_DHASH_REMOVE; + } + return PL_DHASH_STOP; +} + +// +// HashKey +// + +nsHashKey::~nsHashKey(void) +{ + MOZ_COUNT_DTOR(nsHashKey); +} + +nsresult +nsHashKey::Write(nsIObjectOutputStream* aStream) const +{ + NS_NOTREACHED("oops"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +MOZ_DECL_CTOR_COUNTER(nsHashtable) + +nsHashtable::nsHashtable(PRUint32 aInitSize, PRBool threadSafe) + : mLock(NULL), mEnumerating(PR_FALSE) +{ + MOZ_COUNT_CTOR(nsHashtable); + + PRBool result = PL_DHashTableInit(&mHashtable, &hashtableOps, nsnull, + sizeof(HTEntry), aInitSize); + + NS_ASSERTION(result, "Hashtable failed to initialize"); + + // make sure we detect this later + if (!result) + mHashtable.ops = nsnull; + + if (threadSafe) { + mLock = PR_NewLock(); + if (mLock == NULL) { + // Cannot create a lock. If running on a multiprocessing system + // we are sure to die. + PR_ASSERT(mLock != NULL); + } + } +} + + +nsHashtable::~nsHashtable() { + MOZ_COUNT_DTOR(nsHashtable); + if (mHashtable.ops) + PL_DHashTableFinish(&mHashtable); + if (mLock) PR_DestroyLock(mLock); +} + +PRBool nsHashtable::Exists(nsHashKey *aKey) +{ + if (mLock) PR_Lock(mLock); + + if (!mHashtable.ops) + return PR_FALSE; + + PLDHashEntryHdr *entry = + PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP); + + PRBool exists = PL_DHASH_ENTRY_IS_BUSY(entry); + + if (mLock) PR_Unlock(mLock); + + return exists; +} + +void *nsHashtable::Put(nsHashKey *aKey, void *aData) +{ + void *res = NULL; + + if (!mHashtable.ops) return nsnull; + + if (mLock) PR_Lock(mLock); + + // shouldn't be adding an item during enumeration + PR_ASSERT(!mEnumerating); + + HTEntry* entry = + NS_STATIC_CAST(HTEntry*, + PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_ADD)); + + if (entry) { // don't return early, or you'll be locked! + if (entry->key) { + // existing entry, need to boot the old value + res = entry->value; + entry->value = aData; + } else { + // new entry (leave res == null) + entry->key = aKey->Clone(); + entry->value = aData; + } + } + + if (mLock) PR_Unlock(mLock); + + return res; +} + +void *nsHashtable::Get(nsHashKey *aKey) +{ + if (!mHashtable.ops) return nsnull; + + if (mLock) PR_Lock(mLock); + + HTEntry* entry = + NS_STATIC_CAST(HTEntry*, + PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP)); + void *ret = PL_DHASH_ENTRY_IS_BUSY(entry) ? entry->value : nsnull; + + if (mLock) PR_Unlock(mLock); + + return ret; +} + +void *nsHashtable::Remove(nsHashKey *aKey) +{ + if (!mHashtable.ops) return nsnull; + + if (mLock) PR_Lock(mLock); + + // shouldn't be adding an item during enumeration + PR_ASSERT(!mEnumerating); + + + // need to see if the entry is actually there, in order to get the + // old value for the result + HTEntry* entry = + NS_STATIC_CAST(HTEntry*, + PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP)); + void *res; + + if (PL_DHASH_ENTRY_IS_FREE(entry)) { + // value wasn't in the table anyway + res = nsnull; + } else { + res = entry->value; + PL_DHashTableRawRemove(&mHashtable, entry); + } + + if (mLock) PR_Unlock(mLock); + + return res; +} + +// XXX This method was called _hashEnumerateCopy, but it didn't copy the element! +// I don't know how this was supposed to work since the elements are neither copied +// nor refcounted. +PR_STATIC_CALLBACK(PLDHashOperator) +hashEnumerateShare(PLDHashTable *table, PLDHashEntryHdr *hdr, + PRUint32 i, void *arg) +{ + nsHashtable *newHashtable = (nsHashtable *)arg; + HTEntry * entry = NS_STATIC_CAST(HTEntry*, hdr); + + newHashtable->Put(entry->key, entry->value); + return PL_DHASH_NEXT; +} + +nsHashtable * nsHashtable::Clone() +{ + if (!mHashtable.ops) return nsnull; + + PRBool threadSafe = (mLock != nsnull); + nsHashtable *newHashTable = new nsHashtable(mHashtable.entryCount, threadSafe); + + PL_DHashTableEnumerate(&mHashtable, hashEnumerateShare, newHashTable); + return newHashTable; +} + +void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* aClosure) +{ + if (!mHashtable.ops) return; + + PRBool wasEnumerating = mEnumerating; + mEnumerating = PR_TRUE; + _HashEnumerateArgs thunk; + thunk.fn = aEnumFunc; + thunk.arg = aClosure; + PL_DHashTableEnumerate(&mHashtable, hashEnumerate, &thunk); + mEnumerating = wasEnumerating; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +hashEnumerateRemove(PLDHashTable*, PLDHashEntryHdr* hdr, PRUint32 i, void *arg) +{ + HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr); + _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg; + if (thunk) { + return thunk->fn(entry->key, entry->value, thunk->arg) + ? PL_DHASH_REMOVE + : PL_DHASH_STOP; + } + return PL_DHASH_REMOVE; +} + +void nsHashtable::Reset() { + Reset(NULL); +} + +void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* aClosure) +{ + if (!mHashtable.ops) return; + + _HashEnumerateArgs thunk, *thunkp; + if (!destroyFunc) { + thunkp = nsnull; + } else { + thunkp = &thunk; + thunk.fn = destroyFunc; + thunk.arg = aClosure; + } + PL_DHashTableEnumerate(&mHashtable, hashEnumerateRemove, thunkp); +} + +// nsISerializable helpers + +nsHashtable::nsHashtable(nsIObjectInputStream* aStream, + nsHashtableReadEntryFunc aReadEntryFunc, + nsHashtableFreeEntryFunc aFreeEntryFunc, + nsresult *aRetVal) + : mLock(nsnull), + mEnumerating(PR_FALSE) +{ + MOZ_COUNT_CTOR(nsHashtable); + + PRBool threadSafe; + nsresult rv = aStream->ReadBoolean(&threadSafe); + if (NS_SUCCEEDED(rv)) { + if (threadSafe) { + mLock = PR_NewLock(); + if (!mLock) + rv = NS_ERROR_OUT_OF_MEMORY; + } + + if (NS_SUCCEEDED(rv)) { + PRUint32 count; + rv = aStream->Read32(&count); + + if (NS_SUCCEEDED(rv)) { + PRBool status = + PL_DHashTableInit(&mHashtable, &hashtableOps, + nsnull, sizeof(HTEntry), count); + if (!status) { + mHashtable.ops = nsnull; + rv = NS_ERROR_OUT_OF_MEMORY; + } else { + for (PRUint32 i = 0; i < count; i++) { + nsHashKey* key; + void *data; + + rv = aReadEntryFunc(aStream, &key, &data); + if (NS_SUCCEEDED(rv)) { + if (!Put(key, data)) { + rv = NS_ERROR_OUT_OF_MEMORY; + aFreeEntryFunc(aStream, key, data); + } else { + // XXXbe must we clone key? can't we hand off + aFreeEntryFunc(aStream, key, nsnull); + } + if (NS_FAILED(rv)) + break; + } + } + } + } + } + } + *aRetVal = rv; +} + +struct WriteEntryArgs { + nsIObjectOutputStream* mStream; + nsHashtableWriteDataFunc mWriteDataFunc; + nsresult mRetVal; +}; + +PR_STATIC_CALLBACK(PRBool) +WriteEntry(nsHashKey *aKey, void *aData, void* aClosure) +{ + WriteEntryArgs* args = (WriteEntryArgs*) aClosure; + nsIObjectOutputStream* stream = args->mStream; + + nsresult rv = aKey->Write(stream); + if (NS_SUCCEEDED(rv)) + rv = args->mWriteDataFunc(stream, aData); + + args->mRetVal = rv; + return PR_TRUE; +} + +nsresult +nsHashtable::Write(nsIObjectOutputStream* aStream, + nsHashtableWriteDataFunc aWriteDataFunc) const +{ + if (!mHashtable.ops) + return NS_ERROR_OUT_OF_MEMORY; + PRBool threadSafe = (mLock != nsnull); + nsresult rv = aStream->WriteBoolean(threadSafe); + if (NS_FAILED(rv)) return rv; + + // Write the entry count first, so we know how many key/value pairs to read. + PRUint32 count = mHashtable.entryCount; + rv = aStream->Write32(count); + if (NS_FAILED(rv)) return rv; + + // Write all key/value pairs in the table. + WriteEntryArgs args = {aStream, aWriteDataFunc}; + NS_CONST_CAST(nsHashtable*, this)->Enumerate(WriteEntry, (void*) &args); + return args.mRetVal; +} + +//////////////////////////////////////////////////////////////////////////////// + +nsISupportsKey::nsISupportsKey(nsIObjectInputStream* aStream, nsresult *aResult) + : mKey(nsnull) +{ + PRBool nonnull; + nsresult rv = aStream->ReadBoolean(&nonnull); + if (NS_SUCCEEDED(rv) && nonnull) + rv = aStream->ReadObject(PR_TRUE, &mKey); + *aResult = rv; +} + +nsresult +nsISupportsKey::Write(nsIObjectOutputStream* aStream) const +{ + PRBool nonnull = (mKey != nsnull); + nsresult rv = aStream->WriteBoolean(nonnull); + if (NS_SUCCEEDED(rv) && nonnull) + rv = aStream->WriteObject(mKey, PR_TRUE); + return rv; +} + +nsIDKey::nsIDKey(nsIObjectInputStream* aStream, nsresult *aResult) +{ + *aResult = aStream->ReadID(&mID); +} + +nsresult nsIDKey::Write(nsIObjectOutputStream* aStream) const +{ + return aStream->WriteID(mID); +} + +//////////////////////////////////////////////////////////////////////////////// + +// Copy Constructor +// We need to free mStr if the object is passed with mOwnership as OWN. As the +// destructor here is freeing mStr in that case, mStr is NOT getting leaked here. + +nsCStringKey::nsCStringKey(const nsCStringKey& aKey) + : mStr(aKey.mStr), mStrLen(aKey.mStrLen), mOwnership(aKey.mOwnership) +{ + if (mOwnership != NEVER_OWN) { + PRUint32 len = mStrLen * sizeof(char); + char* str = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(len + sizeof(char))); + if (!str) { + // Pray we don't dangle! + mOwnership = NEVER_OWN; + } else { + // Use memcpy in case there are embedded NULs. + memcpy(str, mStr, len); + str[mStrLen] = '\0'; + mStr = str; + mOwnership = OWN; + } + } +#ifdef DEBUG + mKeyType = CStringKey; +#endif + MOZ_COUNT_CTOR(nsCStringKey); +} + +nsCStringKey::nsCStringKey(const nsAFlatCString& str) + : mStr(NS_CONST_CAST(char*, str.get())), + mStrLen(str.Length()), + mOwnership(OWN_CLONE) +{ + NS_ASSERTION(mStr, "null string key"); +#ifdef DEBUG + mKeyType = CStringKey; +#endif + MOZ_COUNT_CTOR(nsCStringKey); +} + +nsCStringKey::nsCStringKey(const nsACString& str) + : mStr(ToNewCString(str)), + mStrLen(str.Length()), + mOwnership(OWN) +{ + NS_ASSERTION(mStr, "null string key"); +#ifdef DEBUG + mKeyType = CStringKey; +#endif + MOZ_COUNT_CTOR(nsCStringKey); +} + +nsCStringKey::nsCStringKey(const char* str, PRInt32 strLen, Ownership own) + : mStr((char*)str), mStrLen(strLen), mOwnership(own) +{ + NS_ASSERTION(mStr, "null string key"); + if (mStrLen == PRUint32(-1)) + mStrLen = strlen(str); +#ifdef DEBUG + mKeyType = CStringKey; +#endif + MOZ_COUNT_CTOR(nsCStringKey); +} + +nsCStringKey::~nsCStringKey(void) +{ + if (mOwnership == OWN) + nsMemory::Free(mStr); + MOZ_COUNT_DTOR(nsCStringKey); +} + +PRUint32 +nsCStringKey::HashCode(void) const +{ + return nsCRT::HashCode(mStr, (PRUint32*)&mStrLen); +} + +PRBool +nsCStringKey::Equals(const nsHashKey* aKey) const +{ + NS_ASSERTION(aKey->GetKeyType() == CStringKey, "mismatched key types"); + nsCStringKey* other = (nsCStringKey*)aKey; + NS_ASSERTION(mStrLen != PRUint32(-1), "never called HashCode"); + NS_ASSERTION(other->mStrLen != PRUint32(-1), "never called HashCode"); + if (mStrLen != other->mStrLen) + return PR_FALSE; + return memcmp(mStr, other->mStr, mStrLen * sizeof(char)) == 0; +} + +nsHashKey* +nsCStringKey::Clone() const +{ + if (mOwnership == NEVER_OWN) + return new nsCStringKey(mStr, mStrLen, NEVER_OWN); + + // Since this might hold binary data OR a string, we ensure that the + // clone string is zero terminated, but don't assume that the source + // string was so terminated. + + PRUint32 len = mStrLen * sizeof(char); + char* str = (char*)nsMemory::Alloc(len + sizeof(char)); + if (str == NULL) + return NULL; + memcpy(str, mStr, len); + str[len] = 0; + return new nsCStringKey(str, mStrLen, OWN); +} + +nsCStringKey::nsCStringKey(nsIObjectInputStream* aStream, nsresult *aResult) + : mStr(nsnull), mStrLen(0), mOwnership(OWN) +{ + nsCAutoString str; + nsresult rv = aStream->ReadCString(str); + mStr = ToNewCString(str); + if (NS_SUCCEEDED(rv)) + mStrLen = str.Length(); + *aResult = rv; + MOZ_COUNT_CTOR(nsCStringKey); +} + +nsresult +nsCStringKey::Write(nsIObjectOutputStream* aStream) const +{ + return aStream->WriteStringZ(mStr); +} + +//////////////////////////////////////////////////////////////////////////////// + +// Copy Constructor +// We need to free mStr if the object is passed with mOwnership as OWN. As the +// destructor here is freeing mStr in that case, mStr is NOT getting leaked here. + +nsStringKey::nsStringKey(const nsStringKey& aKey) + : mStr(aKey.mStr), mStrLen(aKey.mStrLen), mOwnership(aKey.mOwnership) +{ + if (mOwnership != NEVER_OWN) { + PRUint32 len = mStrLen * sizeof(PRUnichar); + PRUnichar* str = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc(len + sizeof(PRUnichar))); + if (!str) { + // Pray we don't dangle! + mOwnership = NEVER_OWN; + } else { + // Use memcpy in case there are embedded NULs. + memcpy(str, mStr, len); + str[mStrLen] = 0; + mStr = str; + mOwnership = OWN; + } + } +#ifdef DEBUG + mKeyType = StringKey; +#endif + MOZ_COUNT_CTOR(nsStringKey); +} + +nsStringKey::nsStringKey(const nsAFlatString& str) + : mStr(NS_CONST_CAST(PRUnichar*, str.get())), + mStrLen(str.Length()), + mOwnership(OWN_CLONE) +{ + NS_ASSERTION(mStr, "null string key"); +#ifdef DEBUG + mKeyType = StringKey; +#endif + MOZ_COUNT_CTOR(nsStringKey); +} + +nsStringKey::nsStringKey(const nsAString& str) + : mStr(ToNewUnicode(str)), + mStrLen(str.Length()), + mOwnership(OWN) +{ + NS_ASSERTION(mStr, "null string key"); +#ifdef DEBUG + mKeyType = StringKey; +#endif + MOZ_COUNT_CTOR(nsStringKey); +} + +nsStringKey::nsStringKey(const PRUnichar* str, PRInt32 strLen, Ownership own) + : mStr((PRUnichar*)str), mStrLen(strLen), mOwnership(own) +{ + NS_ASSERTION(mStr, "null string key"); + if (mStrLen == PRUint32(-1)) + mStrLen = nsCRT::strlen(str); +#ifdef DEBUG + mKeyType = StringKey; +#endif + MOZ_COUNT_CTOR(nsStringKey); +} + +nsStringKey::~nsStringKey(void) +{ + if (mOwnership == OWN) + nsMemory::Free(mStr); + MOZ_COUNT_DTOR(nsStringKey); +} + +PRUint32 +nsStringKey::HashCode(void) const +{ + return nsCRT::HashCode(mStr, (PRUint32*)&mStrLen); +} + +PRBool +nsStringKey::Equals(const nsHashKey* aKey) const +{ + NS_ASSERTION(aKey->GetKeyType() == StringKey, "mismatched key types"); + nsStringKey* other = (nsStringKey*)aKey; + NS_ASSERTION(mStrLen != PRUint32(-1), "never called HashCode"); + NS_ASSERTION(other->mStrLen != PRUint32(-1), "never called HashCode"); + if (mStrLen != other->mStrLen) + return PR_FALSE; + return memcmp(mStr, other->mStr, mStrLen * sizeof(PRUnichar)) == 0; +} + +nsHashKey* +nsStringKey::Clone() const +{ + if (mOwnership == NEVER_OWN) + return new nsStringKey(mStr, mStrLen, NEVER_OWN); + + PRUint32 len = (mStrLen+1) * sizeof(PRUnichar); + PRUnichar* str = (PRUnichar*)nsMemory::Alloc(len); + if (str == NULL) + return NULL; + memcpy(str, mStr, len); + return new nsStringKey(str, mStrLen, OWN); +} + +nsStringKey::nsStringKey(nsIObjectInputStream* aStream, nsresult *aResult) + : mStr(nsnull), mStrLen(0), mOwnership(OWN) +{ + nsAutoString str; + nsresult rv = aStream->ReadString(str); + mStr = ToNewUnicode(str); + if (NS_SUCCEEDED(rv)) + mStrLen = str.Length(); + *aResult = rv; + MOZ_COUNT_CTOR(nsStringKey); +} + +nsresult +nsStringKey::Write(nsIObjectOutputStream* aStream) const +{ + return aStream->WriteWStringZ(mStr); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsObjectHashtable: an nsHashtable where the elements are C++ objects to be +// deleted + +nsObjectHashtable::nsObjectHashtable(nsHashtableCloneElementFunc cloneElementFun, + void* cloneElementClosure, + nsHashtableEnumFunc destroyElementFun, + void* destroyElementClosure, + PRUint32 aSize, PRBool threadSafe) + : nsHashtable(aSize, threadSafe), + mCloneElementFun(cloneElementFun), + mCloneElementClosure(cloneElementClosure), + mDestroyElementFun(destroyElementFun), + mDestroyElementClosure(destroyElementClosure) +{ +} + +nsObjectHashtable::~nsObjectHashtable() +{ + Reset(); +} + + +PLDHashOperator PR_CALLBACK +nsObjectHashtable::CopyElement(PLDHashTable* table, + PLDHashEntryHdr* hdr, + PRUint32 i, void *arg) +{ + nsObjectHashtable *newHashtable = (nsObjectHashtable *)arg; + HTEntry *entry = NS_STATIC_CAST(HTEntry*, hdr); + + void* newElement = + newHashtable->mCloneElementFun(entry->key, entry->value, + newHashtable->mCloneElementClosure); + if (newElement == nsnull) + return PL_DHASH_STOP; + newHashtable->Put(entry->key, newElement); + return PL_DHASH_NEXT; +} + +nsHashtable* +nsObjectHashtable::Clone() +{ + if (!mHashtable.ops) return nsnull; + + PRBool threadSafe = PR_FALSE; + if (mLock) + threadSafe = PR_TRUE; + nsObjectHashtable* newHashTable = + new nsObjectHashtable(mCloneElementFun, mCloneElementClosure, + mDestroyElementFun, mDestroyElementClosure, + mHashtable.entryCount, threadSafe); + + PL_DHashTableEnumerate(&mHashtable, CopyElement, newHashTable); + return newHashTable; +} + +void +nsObjectHashtable::Reset() +{ + nsHashtable::Reset(mDestroyElementFun, mDestroyElementClosure); +} + +PRBool +nsObjectHashtable::RemoveAndDelete(nsHashKey *aKey) +{ + void *value = Remove(aKey); + if (value && mDestroyElementFun) + return (*mDestroyElementFun)(aKey, value, mDestroyElementClosure); + return PR_FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsSupportsHashtable: an nsHashtable where the elements are nsISupports* + +PRBool PR_CALLBACK +nsSupportsHashtable::ReleaseElement(nsHashKey *aKey, void *aData, void* aClosure) +{ + nsISupports* element = NS_STATIC_CAST(nsISupports*, aData); + NS_IF_RELEASE(element); + return PR_TRUE; +} + +nsSupportsHashtable::~nsSupportsHashtable() +{ + Enumerate(ReleaseElement, nsnull); +} + +// Return true if we overwrote something + +PRBool +nsSupportsHashtable::Put(nsHashKey *aKey, nsISupports* aData, nsISupports **value) +{ + NS_IF_ADDREF(aData); + void *prev = nsHashtable::Put(aKey, aData); + nsISupports *old = NS_REINTERPRET_CAST(nsISupports *, prev); + if (value) // pass own the ownership to the caller + *value = old; + else // the caller doesn't care, we do + NS_IF_RELEASE(old); + return prev != nsnull; +} + +nsISupports * +nsSupportsHashtable::Get(nsHashKey *aKey) +{ + void* data = nsHashtable::Get(aKey); + if (!data) + return nsnull; + nsISupports* element = NS_REINTERPRET_CAST(nsISupports*, data); + NS_IF_ADDREF(element); + return element; +} + +// Return true if we found something (useful for checks) + +PRBool +nsSupportsHashtable::Remove(nsHashKey *aKey, nsISupports **value) +{ + void* data = nsHashtable::Remove(aKey); + nsISupports* element = NS_STATIC_CAST(nsISupports*, data); + if (value) // caller wants it + *value = element; + else // caller doesn't care, we do + NS_IF_RELEASE(element); + return data != nsnull; +} + +PLDHashOperator PR_CALLBACK +nsSupportsHashtable::EnumerateCopy(PLDHashTable*, + PLDHashEntryHdr* hdr, + PRUint32 i, void *arg) +{ + nsHashtable *newHashtable = (nsHashtable *)arg; + HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr); + + nsISupports* element = NS_STATIC_CAST(nsISupports*, entry->value); + NS_IF_ADDREF(element); + newHashtable->Put(entry->key, entry->value); + return PL_DHASH_NEXT; +} + +nsHashtable* +nsSupportsHashtable::Clone() +{ + if (!mHashtable.ops) return nsnull; + + PRBool threadSafe = (mLock != nsnull); + nsSupportsHashtable* newHashTable = + new nsSupportsHashtable(mHashtable.entryCount, threadSafe); + + PL_DHashTableEnumerate(&mHashtable, EnumerateCopy, newHashTable); + return newHashTable; +} + +void +nsSupportsHashtable::Reset() +{ + Enumerate(ReleaseElement, nsnull); + nsHashtable::Reset(); +} + +//////////////////////////////////////////////////////////////////////////////// + |