Back to index

lightning-sunbird  0.9+nobinonly
nsHashtable.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK *****
00037  * This Original Code has been modified by IBM Corporation.
00038  * Modifications made by IBM described herein are
00039  * Copyright (c) International Business Machines
00040  * Corporation, 2000
00041  *
00042  * Modifications to Mozilla code or documentation
00043  * identified per MPL Section 3.3
00044  *
00045  * Date         Modified by     Description of modification
00046  * 04/20/2000   IBM Corp.       Added PR_CALLBACK for Optlink use in OS2
00047  */
00048 
00049 #include <string.h>
00050 #include "prmem.h"
00051 #include "prlog.h"
00052 #include "nsHashtable.h"
00053 #include "nsReadableUtils.h"
00054 #include "nsIObjectInputStream.h"
00055 #include "nsIObjectOutputStream.h"
00056 #include "nsCRT.h"
00057 
00058 struct HTEntry : PLDHashEntryHdr
00059 {
00060     nsHashKey* key;
00061     void* value;
00062 };
00063 
00064 //
00065 // Key operations
00066 //
00067 
00068 PR_STATIC_CALLBACK(PRBool)
00069 matchKeyEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
00070               const void* key)
00071 {
00072     const HTEntry* hashEntry =
00073         NS_STATIC_CAST(const HTEntry*, entry);
00074 
00075     if (hashEntry->key == key)
00076         return PR_TRUE;
00077 
00078     const nsHashKey* otherKey = NS_REINTERPRET_CAST(const nsHashKey*, key);
00079     return otherKey->Equals(hashEntry->key);
00080 }
00081 
00082 PR_STATIC_CALLBACK(PLDHashNumber)
00083 hashKey(PLDHashTable* table, const void* key)
00084 {
00085     const nsHashKey* hashKey = NS_STATIC_CAST(const nsHashKey*, key);
00086 
00087     return hashKey->HashCode();
00088 }
00089 
00090 PR_STATIC_CALLBACK(void)
00091 clearHashEntry(PLDHashTable* table, PLDHashEntryHdr* entry)
00092 {
00093     HTEntry* hashEntry = NS_STATIC_CAST(HTEntry*, entry);
00094 
00095     // leave it up to the nsHashKey destructor to free the "value"
00096     delete hashEntry->key;
00097     hashEntry->key = nsnull;
00098     hashEntry->value = nsnull;  // probably not necessary, but for
00099                                 // sanity's sake
00100 }
00101 
00102 
00103 static const PLDHashTableOps hashtableOps = {
00104     PL_DHashAllocTable,
00105     PL_DHashFreeTable,
00106     PL_DHashGetKeyStub,
00107     hashKey,
00108     matchKeyEntry,
00109     PL_DHashMoveEntryStub,
00110     clearHashEntry,
00111     PL_DHashFinalizeStub,
00112     nsnull,
00113 };
00114 
00115 
00116 //
00117 // Enumerator callback
00118 //
00119 
00120 struct _HashEnumerateArgs {
00121     nsHashtableEnumFunc fn;
00122     void* arg;
00123 };
00124 
00125 PR_STATIC_CALLBACK(PLDHashOperator)
00126 hashEnumerate(PLDHashTable* table, PLDHashEntryHdr* hdr, PRUint32 i, void *arg)
00127 {
00128     _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
00129     HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr);
00130     
00131     switch (thunk->fn(entry->key, entry->value, thunk->arg)) {
00132       case kHashEnumerateNext:
00133         return PL_DHASH_NEXT;
00134       case kHashEnumerateRemove:
00135         return PL_DHASH_REMOVE;
00136     }
00137     return PL_DHASH_STOP;           
00138 }
00139 
00140 //
00141 // HashKey
00142 //
00143 
00144 nsHashKey::~nsHashKey(void)
00145 {
00146     MOZ_COUNT_DTOR(nsHashKey);
00147 }
00148 
00149 nsresult
00150 nsHashKey::Write(nsIObjectOutputStream* aStream) const
00151 {
00152     NS_NOTREACHED("oops");
00153     return NS_ERROR_NOT_IMPLEMENTED;
00154 }
00155 
00156 MOZ_DECL_CTOR_COUNTER(nsHashtable)
00157 
00158 nsHashtable::nsHashtable(PRUint32 aInitSize, PRBool threadSafe)
00159   : mLock(NULL), mEnumerating(PR_FALSE)
00160 {
00161     MOZ_COUNT_CTOR(nsHashtable);
00162 
00163     PRBool result = PL_DHashTableInit(&mHashtable, &hashtableOps, nsnull,
00164                                       sizeof(HTEntry), aInitSize);
00165     
00166     NS_ASSERTION(result, "Hashtable failed to initialize");
00167 
00168     // make sure we detect this later
00169     if (!result)
00170         mHashtable.ops = nsnull;
00171     
00172     if (threadSafe) {
00173         mLock = PR_NewLock();
00174         if (mLock == NULL) {
00175             // Cannot create a lock. If running on a multiprocessing system
00176             // we are sure to die.
00177             PR_ASSERT(mLock != NULL);
00178         }
00179     }
00180 }
00181 
00182 
00183 nsHashtable::~nsHashtable() {
00184     MOZ_COUNT_DTOR(nsHashtable);
00185     if (mHashtable.ops)
00186         PL_DHashTableFinish(&mHashtable);
00187     if (mLock) PR_DestroyLock(mLock);
00188 }
00189 
00190 PRBool nsHashtable::Exists(nsHashKey *aKey)
00191 {
00192     if (mLock) PR_Lock(mLock);
00193 
00194     if (!mHashtable.ops)
00195         return PR_FALSE;
00196     
00197     PLDHashEntryHdr *entry =
00198         PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP);
00199     
00200     PRBool exists = PL_DHASH_ENTRY_IS_BUSY(entry);
00201     
00202     if (mLock) PR_Unlock(mLock);
00203 
00204     return exists;
00205 }
00206 
00207 void *nsHashtable::Put(nsHashKey *aKey, void *aData)
00208 {
00209     void *res =  NULL;
00210 
00211     if (!mHashtable.ops) return nsnull;
00212     
00213     if (mLock) PR_Lock(mLock);
00214 
00215     // shouldn't be adding an item during enumeration
00216     PR_ASSERT(!mEnumerating);
00217     
00218     HTEntry* entry =
00219         NS_STATIC_CAST(HTEntry*,
00220                        PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_ADD));
00221     
00222     if (entry) {                // don't return early, or you'll be locked!
00223         if (entry->key) {
00224             // existing entry, need to boot the old value
00225             res = entry->value;
00226             entry->value = aData;
00227         } else {
00228             // new entry (leave res == null)
00229             entry->key = aKey->Clone();
00230             entry->value = aData;
00231         }
00232     }
00233 
00234     if (mLock) PR_Unlock(mLock);
00235 
00236     return res;
00237 }
00238 
00239 void *nsHashtable::Get(nsHashKey *aKey)
00240 {
00241     if (!mHashtable.ops) return nsnull;
00242     
00243     if (mLock) PR_Lock(mLock);
00244 
00245     HTEntry* entry =
00246         NS_STATIC_CAST(HTEntry*,
00247                        PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP));
00248     void *ret = PL_DHASH_ENTRY_IS_BUSY(entry) ? entry->value : nsnull;
00249     
00250     if (mLock) PR_Unlock(mLock);
00251 
00252     return ret;
00253 }
00254 
00255 void *nsHashtable::Remove(nsHashKey *aKey)
00256 {
00257     if (!mHashtable.ops) return nsnull;
00258     
00259     if (mLock) PR_Lock(mLock);
00260 
00261     // shouldn't be adding an item during enumeration
00262     PR_ASSERT(!mEnumerating);
00263 
00264 
00265     // need to see if the entry is actually there, in order to get the
00266     // old value for the result
00267     HTEntry* entry =
00268         NS_STATIC_CAST(HTEntry*,
00269                        PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP));
00270     void *res;
00271     
00272     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
00273         // value wasn't in the table anyway
00274         res = nsnull;
00275     } else {
00276         res = entry->value;
00277         PL_DHashTableRawRemove(&mHashtable, entry);
00278     }
00279 
00280     if (mLock) PR_Unlock(mLock);
00281 
00282     return res;
00283 }
00284 
00285 // XXX This method was called _hashEnumerateCopy, but it didn't copy the element!
00286 // I don't know how this was supposed to work since the elements are neither copied
00287 // nor refcounted.
00288 PR_STATIC_CALLBACK(PLDHashOperator)
00289 hashEnumerateShare(PLDHashTable *table, PLDHashEntryHdr *hdr,
00290                    PRUint32 i, void *arg)
00291 {
00292     nsHashtable *newHashtable = (nsHashtable *)arg;
00293     HTEntry * entry = NS_STATIC_CAST(HTEntry*, hdr);
00294     
00295     newHashtable->Put(entry->key, entry->value);
00296     return PL_DHASH_NEXT;
00297 }
00298 
00299 nsHashtable * nsHashtable::Clone()
00300 {
00301     if (!mHashtable.ops) return nsnull;
00302     
00303     PRBool threadSafe = (mLock != nsnull);
00304     nsHashtable *newHashTable = new nsHashtable(mHashtable.entryCount, threadSafe);
00305 
00306     PL_DHashTableEnumerate(&mHashtable, hashEnumerateShare, newHashTable);
00307     return newHashTable;
00308 }
00309 
00310 void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* aClosure)
00311 {
00312     if (!mHashtable.ops) return;
00313     
00314     PRBool wasEnumerating = mEnumerating;
00315     mEnumerating = PR_TRUE;
00316     _HashEnumerateArgs thunk;
00317     thunk.fn = aEnumFunc;
00318     thunk.arg = aClosure;
00319     PL_DHashTableEnumerate(&mHashtable, hashEnumerate, &thunk);
00320     mEnumerating = wasEnumerating;
00321 }
00322 
00323 PR_STATIC_CALLBACK(PLDHashOperator)
00324 hashEnumerateRemove(PLDHashTable*, PLDHashEntryHdr* hdr, PRUint32 i, void *arg)
00325 {
00326     HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr);
00327     _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
00328     if (thunk) {
00329         return thunk->fn(entry->key, entry->value, thunk->arg)
00330             ? PL_DHASH_REMOVE
00331             : PL_DHASH_STOP;
00332     }
00333     return PL_DHASH_REMOVE;
00334 }
00335 
00336 void nsHashtable::Reset() {
00337     Reset(NULL);
00338 }
00339 
00340 void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* aClosure)
00341 {
00342     if (!mHashtable.ops) return;
00343     
00344     _HashEnumerateArgs thunk, *thunkp;
00345     if (!destroyFunc) {
00346         thunkp = nsnull;
00347     } else {
00348         thunkp = &thunk;
00349         thunk.fn = destroyFunc;
00350         thunk.arg = aClosure;
00351     }
00352     PL_DHashTableEnumerate(&mHashtable, hashEnumerateRemove, thunkp);
00353 }
00354 
00355 // nsISerializable helpers
00356 
00357 nsHashtable::nsHashtable(nsIObjectInputStream* aStream,
00358                          nsHashtableReadEntryFunc aReadEntryFunc,
00359                          nsHashtableFreeEntryFunc aFreeEntryFunc,
00360                          nsresult *aRetVal)
00361   : mLock(nsnull),
00362     mEnumerating(PR_FALSE)
00363 {
00364     MOZ_COUNT_CTOR(nsHashtable);
00365 
00366     PRBool threadSafe;
00367     nsresult rv = aStream->ReadBoolean(&threadSafe);
00368     if (NS_SUCCEEDED(rv)) {
00369         if (threadSafe) {
00370             mLock = PR_NewLock();
00371             if (!mLock)
00372                 rv = NS_ERROR_OUT_OF_MEMORY;
00373         }
00374 
00375         if (NS_SUCCEEDED(rv)) {
00376             PRUint32 count;
00377             rv = aStream->Read32(&count);
00378 
00379             if (NS_SUCCEEDED(rv)) {
00380                 PRBool status =
00381                     PL_DHashTableInit(&mHashtable, &hashtableOps,
00382                                       nsnull, sizeof(HTEntry), count);
00383                 if (!status) {
00384                     mHashtable.ops = nsnull;
00385                     rv = NS_ERROR_OUT_OF_MEMORY;
00386                 } else {
00387                     for (PRUint32 i = 0; i < count; i++) {
00388                         nsHashKey* key;
00389                         void *data;
00390 
00391                         rv = aReadEntryFunc(aStream, &key, &data);
00392                         if (NS_SUCCEEDED(rv)) {
00393                             if (!Put(key, data)) {
00394                                 rv = NS_ERROR_OUT_OF_MEMORY;
00395                                 aFreeEntryFunc(aStream, key, data);
00396                             } else {
00397                                 // XXXbe must we clone key? can't we hand off
00398                                 aFreeEntryFunc(aStream, key, nsnull);
00399                             }
00400                             if (NS_FAILED(rv))
00401                                 break;
00402                         }
00403                     }
00404                 }
00405             }
00406         }
00407     }
00408     *aRetVal = rv;
00409 }
00410 
00411 struct WriteEntryArgs {
00412     nsIObjectOutputStream*    mStream;
00413     nsHashtableWriteDataFunc  mWriteDataFunc;
00414     nsresult                  mRetVal;
00415 };
00416 
00417 PR_STATIC_CALLBACK(PRBool)
00418 WriteEntry(nsHashKey *aKey, void *aData, void* aClosure)
00419 {
00420     WriteEntryArgs* args = (WriteEntryArgs*) aClosure;
00421     nsIObjectOutputStream* stream = args->mStream;
00422 
00423     nsresult rv = aKey->Write(stream);
00424     if (NS_SUCCEEDED(rv))
00425         rv = args->mWriteDataFunc(stream, aData);
00426 
00427     args->mRetVal = rv;
00428     return PR_TRUE;
00429 }
00430 
00431 nsresult
00432 nsHashtable::Write(nsIObjectOutputStream* aStream,
00433                    nsHashtableWriteDataFunc aWriteDataFunc) const
00434 {
00435     if (!mHashtable.ops)
00436         return NS_ERROR_OUT_OF_MEMORY;
00437     PRBool threadSafe = (mLock != nsnull);
00438     nsresult rv = aStream->WriteBoolean(threadSafe);
00439     if (NS_FAILED(rv)) return rv;
00440 
00441     // Write the entry count first, so we know how many key/value pairs to read.
00442     PRUint32 count = mHashtable.entryCount;
00443     rv = aStream->Write32(count);
00444     if (NS_FAILED(rv)) return rv;
00445 
00446     // Write all key/value pairs in the table.
00447     WriteEntryArgs args = {aStream, aWriteDataFunc};
00448     NS_CONST_CAST(nsHashtable*, this)->Enumerate(WriteEntry, (void*) &args);
00449     return args.mRetVal;
00450 }
00451 
00453 
00454 nsISupportsKey::nsISupportsKey(nsIObjectInputStream* aStream, nsresult *aResult)
00455     : mKey(nsnull)
00456 {
00457     PRBool nonnull;
00458     nsresult rv = aStream->ReadBoolean(&nonnull);
00459     if (NS_SUCCEEDED(rv) && nonnull)
00460         rv = aStream->ReadObject(PR_TRUE, &mKey);
00461     *aResult = rv;
00462 }
00463 
00464 nsresult
00465 nsISupportsKey::Write(nsIObjectOutputStream* aStream) const
00466 {
00467     PRBool nonnull = (mKey != nsnull);
00468     nsresult rv = aStream->WriteBoolean(nonnull);
00469     if (NS_SUCCEEDED(rv) && nonnull)
00470         rv = aStream->WriteObject(mKey, PR_TRUE);
00471     return rv;
00472 }
00473 
00474 nsIDKey::nsIDKey(nsIObjectInputStream* aStream, nsresult *aResult)
00475 {
00476     *aResult = aStream->ReadID(&mID);
00477 }
00478 
00479 nsresult nsIDKey::Write(nsIObjectOutputStream* aStream) const
00480 {
00481     return aStream->WriteID(mID);
00482 }
00483 
00485 
00486 // Copy Constructor
00487 // We need to free mStr if the object is passed with mOwnership as OWN. As the 
00488 // destructor here is freeing mStr in that case, mStr is NOT getting leaked here.
00489 
00490 nsCStringKey::nsCStringKey(const nsCStringKey& aKey)
00491     : mStr(aKey.mStr), mStrLen(aKey.mStrLen), mOwnership(aKey.mOwnership)
00492 {
00493     if (mOwnership != NEVER_OWN) {
00494       PRUint32 len = mStrLen * sizeof(char);
00495       char* str = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(len + sizeof(char)));
00496       if (!str) {
00497         // Pray we don't dangle!
00498         mOwnership = NEVER_OWN;
00499       } else {
00500         // Use memcpy in case there are embedded NULs.
00501         memcpy(str, mStr, len);
00502         str[mStrLen] = '\0';
00503         mStr = str;
00504         mOwnership = OWN;
00505       }
00506     }
00507 #ifdef DEBUG
00508     mKeyType = CStringKey;
00509 #endif
00510     MOZ_COUNT_CTOR(nsCStringKey);
00511 }
00512 
00513 nsCStringKey::nsCStringKey(const nsAFlatCString& str)
00514     : mStr(NS_CONST_CAST(char*, str.get())),
00515       mStrLen(str.Length()),
00516       mOwnership(OWN_CLONE)
00517 {
00518     NS_ASSERTION(mStr, "null string key");
00519 #ifdef DEBUG
00520     mKeyType = CStringKey;
00521 #endif
00522     MOZ_COUNT_CTOR(nsCStringKey);
00523 }
00524 
00525 nsCStringKey::nsCStringKey(const nsACString& str)
00526     : mStr(ToNewCString(str)),
00527       mStrLen(str.Length()),
00528       mOwnership(OWN)
00529 {
00530     NS_ASSERTION(mStr, "null string key");
00531 #ifdef DEBUG
00532     mKeyType = CStringKey;
00533 #endif
00534     MOZ_COUNT_CTOR(nsCStringKey);
00535 }
00536 
00537 nsCStringKey::nsCStringKey(const char* str, PRInt32 strLen, Ownership own)
00538     : mStr((char*)str), mStrLen(strLen), mOwnership(own)
00539 {
00540     NS_ASSERTION(mStr, "null string key");
00541     if (mStrLen == PRUint32(-1))
00542         mStrLen = strlen(str);
00543 #ifdef DEBUG
00544     mKeyType = CStringKey;
00545 #endif
00546     MOZ_COUNT_CTOR(nsCStringKey);
00547 }
00548 
00549 nsCStringKey::~nsCStringKey(void)
00550 {
00551     if (mOwnership == OWN)
00552         nsMemory::Free(mStr);
00553     MOZ_COUNT_DTOR(nsCStringKey);
00554 }
00555 
00556 PRUint32
00557 nsCStringKey::HashCode(void) const
00558 {
00559     return nsCRT::HashCode(mStr, (PRUint32*)&mStrLen);
00560 }
00561 
00562 PRBool
00563 nsCStringKey::Equals(const nsHashKey* aKey) const
00564 {
00565     NS_ASSERTION(aKey->GetKeyType() == CStringKey, "mismatched key types");
00566     nsCStringKey* other = (nsCStringKey*)aKey;
00567     NS_ASSERTION(mStrLen != PRUint32(-1), "never called HashCode");
00568     NS_ASSERTION(other->mStrLen != PRUint32(-1), "never called HashCode");
00569     if (mStrLen != other->mStrLen)
00570         return PR_FALSE;
00571     return memcmp(mStr, other->mStr, mStrLen * sizeof(char)) == 0;
00572 }
00573 
00574 nsHashKey*
00575 nsCStringKey::Clone() const
00576 {
00577     if (mOwnership == NEVER_OWN)
00578         return new nsCStringKey(mStr, mStrLen, NEVER_OWN);
00579 
00580     // Since this might hold binary data OR a string, we ensure that the
00581     // clone string is zero terminated, but don't assume that the source
00582     // string was so terminated.
00583 
00584     PRUint32 len = mStrLen * sizeof(char);
00585     char* str = (char*)nsMemory::Alloc(len + sizeof(char));
00586     if (str == NULL)
00587         return NULL;
00588     memcpy(str, mStr, len);
00589     str[len] = 0;
00590     return new nsCStringKey(str, mStrLen, OWN);
00591 }
00592 
00593 nsCStringKey::nsCStringKey(nsIObjectInputStream* aStream, nsresult *aResult)
00594     : mStr(nsnull), mStrLen(0), mOwnership(OWN)
00595 {
00596     nsCAutoString str;
00597     nsresult rv = aStream->ReadCString(str);
00598     mStr = ToNewCString(str);
00599     if (NS_SUCCEEDED(rv))
00600         mStrLen = str.Length();
00601     *aResult = rv;
00602     MOZ_COUNT_CTOR(nsCStringKey);
00603 }
00604 
00605 nsresult
00606 nsCStringKey::Write(nsIObjectOutputStream* aStream) const
00607 {
00608     return aStream->WriteStringZ(mStr);
00609 }
00610 
00612 
00613 // Copy Constructor
00614 // We need to free mStr if the object is passed with mOwnership as OWN. As the 
00615 // destructor here is freeing mStr in that case, mStr is NOT getting leaked here.
00616 
00617 nsStringKey::nsStringKey(const nsStringKey& aKey)
00618     : mStr(aKey.mStr), mStrLen(aKey.mStrLen), mOwnership(aKey.mOwnership)
00619 {
00620     if (mOwnership != NEVER_OWN) {
00621         PRUint32 len = mStrLen * sizeof(PRUnichar);
00622         PRUnichar* str = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc(len + sizeof(PRUnichar)));
00623         if (!str) {
00624             // Pray we don't dangle!
00625             mOwnership = NEVER_OWN;
00626         } else {
00627             // Use memcpy in case there are embedded NULs.
00628             memcpy(str, mStr, len);
00629             str[mStrLen] = 0;
00630             mStr = str;
00631             mOwnership = OWN;
00632         }
00633     }
00634 #ifdef DEBUG
00635     mKeyType = StringKey;
00636 #endif
00637     MOZ_COUNT_CTOR(nsStringKey);
00638 }
00639 
00640 nsStringKey::nsStringKey(const nsAFlatString& str)
00641     : mStr(NS_CONST_CAST(PRUnichar*, str.get())),
00642       mStrLen(str.Length()),
00643       mOwnership(OWN_CLONE)
00644 {
00645     NS_ASSERTION(mStr, "null string key");
00646 #ifdef DEBUG
00647     mKeyType = StringKey;
00648 #endif
00649     MOZ_COUNT_CTOR(nsStringKey);
00650 }
00651 
00652 nsStringKey::nsStringKey(const nsAString& str)
00653     : mStr(ToNewUnicode(str)),
00654       mStrLen(str.Length()),
00655       mOwnership(OWN)
00656 {
00657     NS_ASSERTION(mStr, "null string key");
00658 #ifdef DEBUG
00659     mKeyType = StringKey;
00660 #endif
00661     MOZ_COUNT_CTOR(nsStringKey);
00662 }
00663 
00664 nsStringKey::nsStringKey(const PRUnichar* str, PRInt32 strLen, Ownership own)
00665     : mStr((PRUnichar*)str), mStrLen(strLen), mOwnership(own)
00666 {
00667     NS_ASSERTION(mStr, "null string key");
00668     if (mStrLen == PRUint32(-1))
00669         mStrLen = nsCRT::strlen(str);
00670 #ifdef DEBUG
00671     mKeyType = StringKey;
00672 #endif
00673     MOZ_COUNT_CTOR(nsStringKey);
00674 }
00675 
00676 nsStringKey::~nsStringKey(void)
00677 {
00678     if (mOwnership == OWN)
00679         nsMemory::Free(mStr);
00680     MOZ_COUNT_DTOR(nsStringKey);
00681 }
00682 
00683 PRUint32
00684 nsStringKey::HashCode(void) const
00685 {
00686     return nsCRT::HashCode(mStr, (PRUint32*)&mStrLen);
00687 }
00688 
00689 PRBool
00690 nsStringKey::Equals(const nsHashKey* aKey) const
00691 {
00692     NS_ASSERTION(aKey->GetKeyType() == StringKey, "mismatched key types");
00693     nsStringKey* other = (nsStringKey*)aKey;
00694     NS_ASSERTION(mStrLen != PRUint32(-1), "never called HashCode");
00695     NS_ASSERTION(other->mStrLen != PRUint32(-1), "never called HashCode");
00696     if (mStrLen != other->mStrLen)
00697         return PR_FALSE;
00698     return memcmp(mStr, other->mStr, mStrLen * sizeof(PRUnichar)) == 0;
00699 }
00700 
00701 nsHashKey*
00702 nsStringKey::Clone() const
00703 {
00704     if (mOwnership == NEVER_OWN)
00705         return new nsStringKey(mStr, mStrLen, NEVER_OWN);
00706 
00707     PRUint32 len = (mStrLen+1) * sizeof(PRUnichar);
00708     PRUnichar* str = (PRUnichar*)nsMemory::Alloc(len);
00709     if (str == NULL)
00710         return NULL;
00711     memcpy(str, mStr, len);
00712     return new nsStringKey(str, mStrLen, OWN);
00713 }
00714 
00715 nsStringKey::nsStringKey(nsIObjectInputStream* aStream, nsresult *aResult)
00716     : mStr(nsnull), mStrLen(0), mOwnership(OWN)
00717 {
00718     nsAutoString str;
00719     nsresult rv = aStream->ReadString(str);
00720     mStr = ToNewUnicode(str);
00721     if (NS_SUCCEEDED(rv))
00722         mStrLen = str.Length();
00723     *aResult = rv;
00724     MOZ_COUNT_CTOR(nsStringKey);
00725 }
00726 
00727 nsresult
00728 nsStringKey::Write(nsIObjectOutputStream* aStream) const
00729 {
00730   return aStream->WriteWStringZ(mStr);
00731 }
00732 
00734 // nsObjectHashtable: an nsHashtable where the elements are C++ objects to be
00735 // deleted
00736 
00737 nsObjectHashtable::nsObjectHashtable(nsHashtableCloneElementFunc cloneElementFun,
00738                                      void* cloneElementClosure,
00739                                      nsHashtableEnumFunc destroyElementFun,
00740                                      void* destroyElementClosure,
00741                                      PRUint32 aSize, PRBool threadSafe)
00742     : nsHashtable(aSize, threadSafe),
00743       mCloneElementFun(cloneElementFun),
00744       mCloneElementClosure(cloneElementClosure),
00745       mDestroyElementFun(destroyElementFun),
00746       mDestroyElementClosure(destroyElementClosure)
00747 {
00748 }
00749 
00750 nsObjectHashtable::~nsObjectHashtable()
00751 {
00752     Reset();
00753 }
00754 
00755 
00756 PLDHashOperator PR_CALLBACK
00757 nsObjectHashtable::CopyElement(PLDHashTable* table,
00758                                PLDHashEntryHdr* hdr,
00759                                PRUint32 i, void *arg)
00760 {
00761     nsObjectHashtable *newHashtable = (nsObjectHashtable *)arg;
00762     HTEntry *entry = NS_STATIC_CAST(HTEntry*, hdr);
00763     
00764     void* newElement =
00765         newHashtable->mCloneElementFun(entry->key, entry->value,
00766                                        newHashtable->mCloneElementClosure);
00767     if (newElement == nsnull)
00768         return PL_DHASH_STOP;
00769     newHashtable->Put(entry->key, newElement);
00770     return PL_DHASH_NEXT;
00771 }
00772 
00773 nsHashtable*
00774 nsObjectHashtable::Clone()
00775 {
00776     if (!mHashtable.ops) return nsnull;
00777     
00778     PRBool threadSafe = PR_FALSE;
00779     if (mLock)
00780         threadSafe = PR_TRUE;
00781     nsObjectHashtable* newHashTable =
00782         new nsObjectHashtable(mCloneElementFun, mCloneElementClosure,
00783                               mDestroyElementFun, mDestroyElementClosure,
00784                               mHashtable.entryCount, threadSafe);
00785 
00786     PL_DHashTableEnumerate(&mHashtable, CopyElement, newHashTable);
00787     return newHashTable;
00788 }
00789 
00790 void
00791 nsObjectHashtable::Reset()
00792 {
00793     nsHashtable::Reset(mDestroyElementFun, mDestroyElementClosure);
00794 }
00795 
00796 PRBool
00797 nsObjectHashtable::RemoveAndDelete(nsHashKey *aKey)
00798 {
00799     void *value = Remove(aKey);
00800     if (value && mDestroyElementFun)
00801         return (*mDestroyElementFun)(aKey, value, mDestroyElementClosure);
00802     return PR_FALSE;
00803 }
00804 
00806 // nsSupportsHashtable: an nsHashtable where the elements are nsISupports*
00807 
00808 PRBool PR_CALLBACK
00809 nsSupportsHashtable::ReleaseElement(nsHashKey *aKey, void *aData, void* aClosure)
00810 {
00811     nsISupports* element = NS_STATIC_CAST(nsISupports*, aData);
00812     NS_IF_RELEASE(element);
00813     return PR_TRUE;
00814 }
00815 
00816 nsSupportsHashtable::~nsSupportsHashtable()
00817 {
00818     Enumerate(ReleaseElement, nsnull);
00819 }
00820 
00821 // Return true if we overwrote something
00822 
00823 PRBool
00824 nsSupportsHashtable::Put(nsHashKey *aKey, nsISupports* aData, nsISupports **value)
00825 {
00826     NS_IF_ADDREF(aData);
00827     void *prev = nsHashtable::Put(aKey, aData);
00828     nsISupports *old = NS_REINTERPRET_CAST(nsISupports *, prev);
00829     if (value)  // pass own the ownership to the caller
00830         *value = old;
00831     else        // the caller doesn't care, we do
00832         NS_IF_RELEASE(old);
00833     return prev != nsnull;
00834 }
00835 
00836 nsISupports *
00837 nsSupportsHashtable::Get(nsHashKey *aKey)
00838 {
00839     void* data = nsHashtable::Get(aKey);
00840     if (!data)
00841         return nsnull;
00842     nsISupports* element = NS_REINTERPRET_CAST(nsISupports*, data);
00843     NS_IF_ADDREF(element);
00844     return element;
00845 }
00846 
00847 // Return true if we found something (useful for checks)
00848 
00849 PRBool
00850 nsSupportsHashtable::Remove(nsHashKey *aKey, nsISupports **value)
00851 {
00852     void* data = nsHashtable::Remove(aKey);
00853     nsISupports* element = NS_STATIC_CAST(nsISupports*, data);
00854     if (value)            // caller wants it
00855         *value = element;
00856     else                  // caller doesn't care, we do
00857         NS_IF_RELEASE(element);
00858     return data != nsnull;
00859 }
00860 
00861 PLDHashOperator PR_CALLBACK
00862 nsSupportsHashtable::EnumerateCopy(PLDHashTable*,
00863                                    PLDHashEntryHdr* hdr,
00864                                    PRUint32 i, void *arg)
00865 {
00866     nsHashtable *newHashtable = (nsHashtable *)arg;
00867     HTEntry* entry = NS_STATIC_CAST(HTEntry*, hdr);
00868     
00869     nsISupports* element = NS_STATIC_CAST(nsISupports*, entry->value);
00870     NS_IF_ADDREF(element);
00871     newHashtable->Put(entry->key, entry->value);
00872     return PL_DHASH_NEXT;
00873 }
00874 
00875 nsHashtable*
00876 nsSupportsHashtable::Clone()
00877 {
00878     if (!mHashtable.ops) return nsnull;
00879     
00880     PRBool threadSafe = (mLock != nsnull);
00881     nsSupportsHashtable* newHashTable =
00882         new nsSupportsHashtable(mHashtable.entryCount, threadSafe);
00883 
00884     PL_DHashTableEnumerate(&mHashtable, EnumerateCopy, newHashTable);
00885     return newHashTable;
00886 }
00887 
00888 void
00889 nsSupportsHashtable::Reset()
00890 {
00891     Enumerate(ReleaseElement, nsnull);
00892     nsHashtable::Reset();
00893 }
00894 
00896