/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * 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 "prmem.h" #include "prlog.h" #include "nsHashtable.h" // // Key operations // static PLHashNumber PR_CALLBACK _hashValue(const void *key) { return ((const nsHashKey *) key)->HashValue(); } static PRIntn PR_CALLBACK _hashKeyCompare(const void *key1, const void *key2) { return ((const nsHashKey *) key1)->Equals((const nsHashKey *) key2); } static PRIntn PR_CALLBACK _hashValueCompare(const void *value1, const void *value2) { // We're not going to make any assumptions about value equality return 0; } // // Memory callbacks // static void * PR_CALLBACK _hashAllocTable(void *pool, PRSize size) { return PR_MALLOC(size); } static void PR_CALLBACK _hashFreeTable(void *pool, void *item) { PR_DELETE(item); } static PLHashEntry * PR_CALLBACK _hashAllocEntry(void *pool, const void *key) { return PR_NEW(PLHashEntry); } static void PR_CALLBACK _hashFreeEntry(void *pool, PLHashEntry *entry, PRUintn flag) { if (flag == HT_FREE_ENTRY) { delete (nsHashKey *) (entry->key); PR_DELETE(entry); } } static PLHashAllocOps _hashAllocOps = { _hashAllocTable, _hashFreeTable, _hashAllocEntry, _hashFreeEntry }; // // Enumerator callback // struct _HashEnumerateArgs { nsHashtableEnumFunc fn; void* arg; }; static PRIntn PR_CALLBACK _hashEnumerate(PLHashEntry *he, PRIntn i, void *arg) { _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg; return thunk->fn((nsHashKey *) he->key, he->value, thunk->arg) ? HT_ENUMERATE_NEXT : HT_ENUMERATE_STOP; } // // HashKey // MOZ_DECL_CTOR_COUNTER(nsHashKey); nsHashKey::nsHashKey(void) { MOZ_COUNT_CTOR(nsHashKey); } nsHashKey::~nsHashKey(void) { MOZ_COUNT_DTOR(nsHashKey); } MOZ_DECL_CTOR_COUNTER(nsHashtable); nsHashtable::nsHashtable(PRUint32 aInitSize, PRBool threadSafe) : mLock(NULL) { MOZ_COUNT_CTOR(nsHashtable); hashtable = PL_NewHashTable(aInitSize, _hashValue, _hashKeyCompare, _hashValueCompare, &_hashAllocOps, NULL); if (threadSafe == PR_TRUE) { 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); PL_HashTableDestroy(hashtable); if (mLock) PR_DestroyLock(mLock); } PRBool nsHashtable::Exists(nsHashKey *aKey) { PLHashNumber hash = aKey->HashValue(); if (mLock) PR_Lock(mLock); PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey); if (mLock) PR_Unlock(mLock); return *hep != NULL; } void *nsHashtable::Put(nsHashKey *aKey, void *aData) { void *res = NULL; PLHashNumber hash = aKey->HashValue(); PLHashEntry *he; if (mLock) PR_Lock(mLock); PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey); if ((he = *hep) != NULL) { res = he->value; he->value = aData; } else { PL_HashTableRawAdd(hashtable, hep, hash, (void *) aKey->Clone(), aData); } if (mLock) PR_Unlock(mLock); return res; } void *nsHashtable::Get(nsHashKey *aKey) { if (mLock) PR_Lock(mLock); void *ret = PL_HashTableLookup(hashtable, (void *) aKey); if (mLock) PR_Unlock(mLock); return ret; } void *nsHashtable::Remove(nsHashKey *aKey) { PLHashNumber hash = aKey->HashValue(); PLHashEntry *he; if (mLock) PR_Lock(mLock); PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey); void *res = NULL; if ((he = *hep) != NULL) { res = he->value; PL_HashTableRawRemove(hashtable, hep, he); } 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. static PRIntn PR_CALLBACK _hashEnumerateShare(PLHashEntry *he, PRIntn i, void *arg) { nsHashtable *newHashtable = (nsHashtable *)arg; newHashtable->Put((nsHashKey *) he->key, he->value); return HT_ENUMERATE_NEXT; } nsHashtable * nsHashtable::Clone() { PRBool threadSafe = PR_FALSE; if (mLock) threadSafe = PR_TRUE; nsHashtable *newHashTable = new nsHashtable(hashtable->nentries, threadSafe); PL_HashTableEnumerateEntries(hashtable, _hashEnumerateShare, newHashTable); return newHashTable; } void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* closure) { _HashEnumerateArgs thunk; thunk.fn = aEnumFunc; thunk.arg = closure; PL_HashTableEnumerateEntries(hashtable, _hashEnumerate, &thunk); } static PRIntn PR_CALLBACK _hashEnumerateRemove(PLHashEntry *he, PRIntn i, void *arg) { _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg; if (thunk) return thunk->fn((nsHashKey *) he->key, he->value, thunk->arg) ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_STOP; else return HT_ENUMERATE_REMOVE; } void nsHashtable::Reset() { Reset(NULL); } void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* closure) { if (destroyFunc != NULL) { _HashEnumerateArgs thunk; thunk.fn = destroyFunc; thunk.arg = closure; PL_HashTableEnumerateEntries(hashtable, _hashEnumerateRemove, &thunk); } else PL_HashTableEnumerateEntries(hashtable, _hashEnumerateRemove, NULL); } //////////////////////////////////////////////////////////////////////////////// nsStringKey::nsStringKey(const char* str) { MOZ_COUNT_CTOR(nsStringKey); mStr.AssignWithConversion(str); } nsStringKey::nsStringKey(const PRUnichar* str) { MOZ_COUNT_CTOR(nsStringKey); mStr.Assign(str); } nsStringKey::nsStringKey(const nsString& str) { MOZ_COUNT_CTOR(nsStringKey); mStr.Assign(str); } nsStringKey::nsStringKey(const nsCString& str) { MOZ_COUNT_CTOR(nsStringKey); mStr.AssignWithConversion(str); } nsStringKey::~nsStringKey(void) { MOZ_COUNT_DTOR(nsStringKey); } PRUint32 nsStringKey::HashValue(void) const { return nsStr::HashCode(mStr); } PRBool nsStringKey::Equals(const nsHashKey* aKey) const { return ((nsStringKey*)aKey)->mStr == mStr; } nsHashKey* nsStringKey::Clone() const { return new nsStringKey(mStr); } const nsString& nsStringKey::GetString() const { return 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(); } PRIntn PR_CALLBACK nsObjectHashtable::CopyElement(PLHashEntry *he, PRIntn i, void *arg) { nsObjectHashtable *newHashtable = (nsObjectHashtable *)arg; void* newElement = newHashtable->mCloneElementFun((nsHashKey*)he->key, he->value, newHashtable->mCloneElementClosure); if (newElement == nsnull) return HT_ENUMERATE_STOP; newHashtable->Put((nsHashKey*)he->key, newElement); return HT_ENUMERATE_NEXT; } nsHashtable* nsObjectHashtable::Clone() { PRBool threadSafe = PR_FALSE; if (mLock) threadSafe = PR_TRUE; nsObjectHashtable* newHashTable = new nsObjectHashtable(mCloneElementFun, mCloneElementClosure, mDestroyElementFun, mDestroyElementClosure, hashtable->nentries, threadSafe); PL_HashTableEnumerateEntries(hashtable, 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); } else return PR_FALSE; } //////////////////////////////////////////////////////////////////////////////// // nsSupportsHashtable: an nsHashtable where the elements are nsISupports* PRBool PR_CALLBACK nsSupportsHashtable::ReleaseElement(nsHashKey *aKey, void *aData, void* closure) { nsISupports* element = NS_STATIC_CAST(nsISupports*, aData); NS_IF_RELEASE(element); return PR_TRUE; } nsSupportsHashtable::~nsSupportsHashtable() { Enumerate(ReleaseElement, nsnull); } // Return 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 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; } PRIntn PR_CALLBACK nsSupportsHashtable::EnumerateCopy(PLHashEntry *he, PRIntn i, void *arg) { nsHashtable *newHashtable = (nsHashtable *)arg; nsISupports* element = NS_STATIC_CAST(nsISupports*, he->value); NS_IF_ADDREF(element); newHashtable->Put((nsHashKey*)he->key, he->value); return HT_ENUMERATE_NEXT; } nsHashtable* nsSupportsHashtable::Clone() { PRBool threadSafe = PR_FALSE; if (mLock) threadSafe = PR_TRUE; nsSupportsHashtable* newHashTable = new nsSupportsHashtable(hashtable->nentries, threadSafe); PL_HashTableEnumerateEntries(hashtable, EnumerateCopy, newHashTable); return newHashTable; } void nsSupportsHashtable::Reset() { Enumerate(ReleaseElement, nsnull); nsHashtable::Reset(); } //////////////////////////////////////////////////////////////////////////////// // nsOpaqueKey: Where keys are opaque byte array blobs // // Note opaque keys are not copied by this constructor. If you want a private // copy in each hash key, you must create one and pass it in to this function. nsOpaqueKey::nsOpaqueKey(const char *aOpaqueKey, PRUint32 aKeyLength) { mOpaqueKey = aOpaqueKey; mKeyLength = aKeyLength; } nsOpaqueKey::~nsOpaqueKey(void) { } PRUint32 nsOpaqueKey::HashValue(void) const { PRUint32 h, i, k; h = 0; // Same hashing technique as for java.lang.String.hashCode() if (mKeyLength <= 15) { // A short key; Use a dense sampling to compute the hash code for (i = 0; i < mKeyLength; i++) h += 37 * mOpaqueKey[i]; } else { // A long key; Use a sparse sampling to compute the hash code k = mKeyLength >> 3; for (i = 0; i < mKeyLength; i += k) h += 39 * mOpaqueKey[i]; } return h; } PRBool nsOpaqueKey::Equals(const nsHashKey* aKey) const { nsOpaqueKey *otherKey = (nsOpaqueKey*)aKey; if (mKeyLength != otherKey->mKeyLength) return PR_FALSE; return !(PRBool)memcmp(otherKey->mOpaqueKey, mOpaqueKey, mKeyLength); } nsHashKey* nsOpaqueKey::Clone() const { return new nsOpaqueKey(mOpaqueKey, mKeyLength); } PRUint32 nsOpaqueKey::GetKeyLength() const { return mKeyLength; } const char* nsOpaqueKey::GetKey() const { return mOpaqueKey; } ////////////////////////////////////////////////////////////////////////////////