xpcom/ds/nsHashtable.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     5  * This Original Code has been modified by IBM Corporation.
     6  * Modifications made by IBM described herein are
     7  * Copyright (c) International Business Machines
     8  * Corporation, 2000
     9  *
    10  * Modifications to Mozilla code or documentation
    11  * identified per MPL Section 3.3
    12  *
    13  * Date         Modified by     Description of modification
    14  * 04/20/2000   IBM Corp.       Added PR_CALLBACK for Optlink use in OS2
    15  */
    17 #include <string.h>
    18 #include "prlog.h"
    19 #include "prlock.h"
    20 #include "nsHashtable.h"
    21 #include "nsIObjectInputStream.h"
    22 #include "nsIObjectOutputStream.h"
    23 #include "nsCRTGlue.h"
    24 #include "mozilla/HashFunctions.h"
    26 using namespace mozilla;
    28 struct HTEntry : PLDHashEntryHdr
    29 {
    30     nsHashKey* key;
    31     void* value;
    32 };
    34 //
    35 // Key operations
    36 //
    38 static bool
    39 matchKeyEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
    40               const void* key)
    41 {
    42     const HTEntry* hashEntry =
    43         static_cast<const HTEntry*>(entry);
    45     if (hashEntry->key == key)
    46         return true;
    48     const nsHashKey* otherKey = reinterpret_cast<const nsHashKey*>(key);
    49     return otherKey->Equals(hashEntry->key);
    50 }
    52 static PLDHashNumber
    53 hashKey(PLDHashTable* table, const void* key)
    54 {
    55     const nsHashKey* hashKey = static_cast<const nsHashKey*>(key);
    57     return hashKey->HashCode();
    58 }
    60 static void
    61 clearHashEntry(PLDHashTable* table, PLDHashEntryHdr* entry)
    62 {
    63     HTEntry* hashEntry = static_cast<HTEntry*>(entry);
    65     // leave it up to the nsHashKey destructor to free the "value"
    66     delete hashEntry->key;
    67     hashEntry->key = nullptr;
    68     hashEntry->value = nullptr;  // probably not necessary, but for
    69                                 // sanity's sake
    70 }
    73 static const PLDHashTableOps hashtableOps = {
    74     PL_DHashAllocTable,
    75     PL_DHashFreeTable,
    76     hashKey,
    77     matchKeyEntry,
    78     PL_DHashMoveEntryStub,
    79     clearHashEntry,
    80     PL_DHashFinalizeStub,
    81     nullptr,
    82 };
    85 //
    86 // Enumerator callback
    87 //
    89 struct _HashEnumerateArgs {
    90     nsHashtableEnumFunc fn;
    91     void* arg;
    92 };
    94 static PLDHashOperator
    95 hashEnumerate(PLDHashTable* table, PLDHashEntryHdr* hdr, uint32_t i, void *arg)
    96 {
    97     _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
    98     HTEntry* entry = static_cast<HTEntry*>(hdr);
   100     if (thunk->fn(entry->key, entry->value, thunk->arg))
   101         return PL_DHASH_NEXT;
   102     return PL_DHASH_STOP;
   103 }
   105 //
   106 // HashKey
   107 //
   109 nsHashKey::~nsHashKey(void)
   110 {
   111     MOZ_COUNT_DTOR(nsHashKey);
   112 }
   114 nsresult
   115 nsHashKey::Write(nsIObjectOutputStream* aStream) const
   116 {
   117     NS_NOTREACHED("oops");
   118     return NS_ERROR_NOT_IMPLEMENTED;
   119 }
   121 nsHashtable::nsHashtable(uint32_t aInitSize, bool aThreadSafe)
   122   : mLock(nullptr), mEnumerating(false)
   123 {
   124     MOZ_COUNT_CTOR(nsHashtable);
   126     bool result = PL_DHashTableInit(&mHashtable, &hashtableOps, nullptr,
   127                                     sizeof(HTEntry), aInitSize, fallible_t());
   128     NS_ASSERTION(result, "Hashtable failed to initialize");
   130     // make sure we detect this later
   131     if (!result)
   132         mHashtable.ops = nullptr;
   134     if (aThreadSafe) {
   135         mLock = PR_NewLock();
   136         if (mLock == nullptr) {
   137             // Cannot create a lock. If running on a multiprocessing system
   138             // we are sure to die.
   139             PR_ASSERT(mLock != nullptr);
   140         }
   141     }
   142 }
   144 nsHashtable::~nsHashtable() {
   145     MOZ_COUNT_DTOR(nsHashtable);
   146     if (mHashtable.ops)
   147         PL_DHashTableFinish(&mHashtable);
   148     if (mLock) PR_DestroyLock(mLock);
   149 }
   151 bool nsHashtable::Exists(nsHashKey *aKey)
   152 {
   153     if (mLock) PR_Lock(mLock);
   155     if (!mHashtable.ops) {
   156         if (mLock) PR_Unlock(mLock);
   157         return false;
   158     }
   160     PLDHashEntryHdr *entry =
   161         PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP);
   163     bool exists = PL_DHASH_ENTRY_IS_BUSY(entry);
   165     if (mLock) PR_Unlock(mLock);
   167     return exists;
   168 }
   170 void *nsHashtable::Put(nsHashKey *aKey, void *aData)
   171 {
   172     void *res =  nullptr;
   174     if (!mHashtable.ops) return nullptr;
   176     if (mLock) PR_Lock(mLock);
   178     // shouldn't be adding an item during enumeration
   179     PR_ASSERT(!mEnumerating);
   181     HTEntry* entry =
   182         static_cast<HTEntry*>
   183                    (PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_ADD));
   185     if (entry) {                // don't return early, or you'll be locked!
   186         if (entry->key) {
   187             // existing entry, need to boot the old value
   188             res = entry->value;
   189             entry->value = aData;
   190         } else {
   191             // new entry (leave res == null)
   192             entry->key = aKey->Clone();
   193             entry->value = aData;
   194         }
   195     }
   197     if (mLock) PR_Unlock(mLock);
   199     return res;
   200 }
   202 void *nsHashtable::Get(nsHashKey *aKey)
   203 {
   204     if (!mHashtable.ops) return nullptr;
   206     if (mLock) PR_Lock(mLock);
   208     HTEntry* entry =
   209         static_cast<HTEntry*>
   210                    (PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP));
   211     void *ret = PL_DHASH_ENTRY_IS_BUSY(entry) ? entry->value : nullptr;
   213     if (mLock) PR_Unlock(mLock);
   215     return ret;
   216 }
   218 void *nsHashtable::Remove(nsHashKey *aKey)
   219 {
   220     if (!mHashtable.ops) return nullptr;
   222     if (mLock) PR_Lock(mLock);
   224     // shouldn't be adding an item during enumeration
   225     PR_ASSERT(!mEnumerating);
   228     // need to see if the entry is actually there, in order to get the
   229     // old value for the result
   230     HTEntry* entry =
   231         static_cast<HTEntry*>
   232                    (PL_DHashTableOperate(&mHashtable, aKey, PL_DHASH_LOOKUP));
   233     void *res;
   235     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
   236         // value wasn't in the table anyway
   237         res = nullptr;
   238     } else {
   239         res = entry->value;
   240         PL_DHashTableRawRemove(&mHashtable, entry);
   241     }
   243     if (mLock) PR_Unlock(mLock);
   245     return res;
   246 }
   248 // XXX This method was called _hashEnumerateCopy, but it didn't copy the element!
   249 // I don't know how this was supposed to work since the elements are neither copied
   250 // nor refcounted.
   251 static PLDHashOperator
   252 hashEnumerateShare(PLDHashTable *table, PLDHashEntryHdr *hdr,
   253                    uint32_t i, void *arg)
   254 {
   255     nsHashtable *newHashtable = (nsHashtable *)arg;
   256     HTEntry * entry = static_cast<HTEntry*>(hdr);
   258     newHashtable->Put(entry->key, entry->value);
   259     return PL_DHASH_NEXT;
   260 }
   262 nsHashtable * nsHashtable::Clone()
   263 {
   264     if (!mHashtable.ops) return nullptr;
   266     bool threadSafe = (mLock != nullptr);
   267     nsHashtable *newHashTable = new nsHashtable(mHashtable.entryCount, threadSafe);
   269     PL_DHashTableEnumerate(&mHashtable, hashEnumerateShare, newHashTable);
   270     return newHashTable;
   271 }
   273 void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* aClosure)
   274 {
   275     if (!mHashtable.ops) return;
   277     bool wasEnumerating = mEnumerating;
   278     mEnumerating = true;
   279     _HashEnumerateArgs thunk;
   280     thunk.fn = aEnumFunc;
   281     thunk.arg = aClosure;
   282     PL_DHashTableEnumerate(&mHashtable, hashEnumerate, &thunk);
   283     mEnumerating = wasEnumerating;
   284 }
   286 static PLDHashOperator
   287 hashEnumerateRemove(PLDHashTable*, PLDHashEntryHdr* hdr, uint32_t i, void *arg)
   288 {
   289     HTEntry* entry = static_cast<HTEntry*>(hdr);
   290     _HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
   291     if (thunk) {
   292         return thunk->fn(entry->key, entry->value, thunk->arg)
   293             ? PL_DHASH_REMOVE
   294             : PL_DHASH_STOP;
   295     }
   296     return PL_DHASH_REMOVE;
   297 }
   299 void nsHashtable::Reset() {
   300     Reset(nullptr);
   301 }
   303 void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* aClosure)
   304 {
   305     if (!mHashtable.ops) return;
   307     _HashEnumerateArgs thunk, *thunkp;
   308     if (!destroyFunc) {
   309         thunkp = nullptr;
   310     } else {
   311         thunkp = &thunk;
   312         thunk.fn = destroyFunc;
   313         thunk.arg = aClosure;
   314     }
   315     PL_DHashTableEnumerate(&mHashtable, hashEnumerateRemove, thunkp);
   316 }
   318 // nsISerializable helpers
   320 nsHashtable::nsHashtable(nsIObjectInputStream* aStream,
   321                          nsHashtableReadEntryFunc aReadEntryFunc,
   322                          nsHashtableFreeEntryFunc aFreeEntryFunc,
   323                          nsresult *aRetVal)
   324   : mLock(nullptr),
   325     mEnumerating(false)
   326 {
   327     MOZ_COUNT_CTOR(nsHashtable);
   329     bool threadSafe;
   330     nsresult rv = aStream->ReadBoolean(&threadSafe);
   331     if (NS_SUCCEEDED(rv)) {
   332         if (threadSafe) {
   333             mLock = PR_NewLock();
   334             if (!mLock)
   335                 rv = NS_ERROR_OUT_OF_MEMORY;
   336         }
   338         if (NS_SUCCEEDED(rv)) {
   339             uint32_t count;
   340             rv = aStream->Read32(&count);
   342             if (NS_SUCCEEDED(rv)) {
   343                 bool status =
   344                     PL_DHashTableInit(&mHashtable, &hashtableOps,
   345                                       nullptr, sizeof(HTEntry), count,
   346                                       fallible_t());
   347                 if (!status) {
   348                     mHashtable.ops = nullptr;
   349                     rv = NS_ERROR_OUT_OF_MEMORY;
   350                 } else {
   351                     for (uint32_t i = 0; i < count; i++) {
   352                         nsHashKey* key;
   353                         void *data;
   355                         rv = aReadEntryFunc(aStream, &key, &data);
   356                         if (NS_SUCCEEDED(rv)) {
   357                             Put(key, data);
   359                             // XXXbe must we clone key? can't we hand off
   360                             aFreeEntryFunc(aStream, key, nullptr);
   361                         }
   362                     }
   363                 }
   364             }
   365         }
   366     }
   367     *aRetVal = rv;
   368 }
   370 struct WriteEntryArgs {
   371     nsIObjectOutputStream*    mStream;
   372     nsHashtableWriteDataFunc  mWriteDataFunc;
   373     nsresult                  mRetVal;
   374 };
   376 static bool
   377 WriteEntry(nsHashKey *aKey, void *aData, void* aClosure)
   378 {
   379     WriteEntryArgs* args = (WriteEntryArgs*) aClosure;
   380     nsIObjectOutputStream* stream = args->mStream;
   382     nsresult rv = aKey->Write(stream);
   383     if (NS_SUCCEEDED(rv))
   384         rv = args->mWriteDataFunc(stream, aData);
   386     args->mRetVal = rv;
   387     return true;
   388 }
   390 nsresult
   391 nsHashtable::Write(nsIObjectOutputStream* aStream,
   392                    nsHashtableWriteDataFunc aWriteDataFunc) const
   393 {
   394     if (!mHashtable.ops)
   395         return NS_ERROR_OUT_OF_MEMORY;
   396     bool threadSafe = (mLock != nullptr);
   397     nsresult rv = aStream->WriteBoolean(threadSafe);
   398     if (NS_FAILED(rv)) return rv;
   400     // Write the entry count first, so we know how many key/value pairs to read.
   401     uint32_t count = mHashtable.entryCount;
   402     rv = aStream->Write32(count);
   403     if (NS_FAILED(rv)) return rv;
   405     // Write all key/value pairs in the table.
   406     WriteEntryArgs args = {aStream, aWriteDataFunc};
   407     const_cast<nsHashtable*>(this)->Enumerate(WriteEntry, (void*) &args);
   408     return args.mRetVal;
   409 }
   411 ////////////////////////////////////////////////////////////////////////////////
   413 // Copy Constructor
   414 // We need to free mStr if the object is passed with mOwnership as OWN. As the
   415 // destructor here is freeing mStr in that case, mStr is NOT getting leaked here.
   417 nsCStringKey::nsCStringKey(const nsCStringKey& aKey)
   418     : mStr(aKey.mStr), mStrLen(aKey.mStrLen), mOwnership(aKey.mOwnership)
   419 {
   420     if (mOwnership != NEVER_OWN) {
   421       uint32_t len = mStrLen * sizeof(char);
   422       char* str = reinterpret_cast<char*>(nsMemory::Alloc(len + sizeof(char)));
   423       if (!str) {
   424         // Pray we don't dangle!
   425         mOwnership = NEVER_OWN;
   426       } else {
   427         // Use memcpy in case there are embedded NULs.
   428         memcpy(str, mStr, len);
   429         str[mStrLen] = '\0';
   430         mStr = str;
   431         mOwnership = OWN;
   432       }
   433     }
   434 #ifdef DEBUG
   435     mKeyType = CStringKey;
   436 #endif
   437     MOZ_COUNT_CTOR(nsCStringKey);
   438 }
   440 nsCStringKey::nsCStringKey(const nsAFlatCString& str)
   441     : mStr(const_cast<char*>(str.get())),
   442       mStrLen(str.Length()),
   443       mOwnership(OWN_CLONE)
   444 {
   445     NS_ASSERTION(mStr, "null string key");
   446 #ifdef DEBUG
   447     mKeyType = CStringKey;
   448 #endif
   449     MOZ_COUNT_CTOR(nsCStringKey);
   450 }
   452 nsCStringKey::nsCStringKey(const nsACString& str)
   453     : mStr(ToNewCString(str)),
   454       mStrLen(str.Length()),
   455       mOwnership(OWN)
   456 {
   457     NS_ASSERTION(mStr, "null string key");
   458 #ifdef DEBUG
   459     mKeyType = CStringKey;
   460 #endif
   461     MOZ_COUNT_CTOR(nsCStringKey);
   462 }
   464 nsCStringKey::nsCStringKey(const char* str, int32_t strLen, Ownership own)
   465     : mStr((char*)str), mStrLen(strLen), mOwnership(own)
   466 {
   467     NS_ASSERTION(mStr, "null string key");
   468     if (mStrLen == uint32_t(-1))
   469         mStrLen = strlen(str);
   470 #ifdef DEBUG
   471     mKeyType = CStringKey;
   472 #endif
   473     MOZ_COUNT_CTOR(nsCStringKey);
   474 }
   476 nsCStringKey::~nsCStringKey(void)
   477 {
   478     if (mOwnership == OWN)
   479         nsMemory::Free(mStr);
   480     MOZ_COUNT_DTOR(nsCStringKey);
   481 }
   483 uint32_t
   484 nsCStringKey::HashCode(void) const
   485 {
   486     return HashString(mStr, mStrLen);
   487 }
   489 bool
   490 nsCStringKey::Equals(const nsHashKey* aKey) const
   491 {
   492     NS_ASSERTION(aKey->GetKeyType() == CStringKey, "mismatched key types");
   493     nsCStringKey* other = (nsCStringKey*)aKey;
   494     NS_ASSERTION(mStrLen != uint32_t(-1), "never called HashCode");
   495     NS_ASSERTION(other->mStrLen != uint32_t(-1), "never called HashCode");
   496     if (mStrLen != other->mStrLen)
   497         return false;
   498     return memcmp(mStr, other->mStr, mStrLen * sizeof(char)) == 0;
   499 }
   501 nsHashKey*
   502 nsCStringKey::Clone() const
   503 {
   504     if (mOwnership == NEVER_OWN)
   505         return new nsCStringKey(mStr, mStrLen, NEVER_OWN);
   507     // Since this might hold binary data OR a string, we ensure that the
   508     // clone string is zero terminated, but don't assume that the source
   509     // string was so terminated.
   511     uint32_t len = mStrLen * sizeof(char);
   512     char* str = (char*)nsMemory::Alloc(len + sizeof(char));
   513     if (str == nullptr)
   514         return nullptr;
   515     memcpy(str, mStr, len);
   516     str[len] = 0;
   517     return new nsCStringKey(str, mStrLen, OWN);
   518 }
   520 nsCStringKey::nsCStringKey(nsIObjectInputStream* aStream, nsresult *aResult)
   521     : mStr(nullptr), mStrLen(0), mOwnership(OWN)
   522 {
   523     nsAutoCString str;
   524     nsresult rv = aStream->ReadCString(str);
   525     mStr = ToNewCString(str);
   526     if (NS_SUCCEEDED(rv))
   527         mStrLen = str.Length();
   528     *aResult = rv;
   529     MOZ_COUNT_CTOR(nsCStringKey);
   530 }
   532 nsresult
   533 nsCStringKey::Write(nsIObjectOutputStream* aStream) const
   534 {
   535     return aStream->WriteStringZ(mStr);
   536 }
   538 ////////////////////////////////////////////////////////////////////////////////
   539 // nsObjectHashtable: an nsHashtable where the elements are C++ objects to be
   540 // deleted
   542 nsObjectHashtable::nsObjectHashtable(nsHashtableCloneElementFunc cloneElementFun,
   543                                      void* cloneElementClosure,
   544                                      nsHashtableEnumFunc destroyElementFun,
   545                                      void* destroyElementClosure,
   546                                      uint32_t aSize, bool threadSafe)
   547     : nsHashtable(aSize, threadSafe),
   548       mCloneElementFun(cloneElementFun),
   549       mCloneElementClosure(cloneElementClosure),
   550       mDestroyElementFun(destroyElementFun),
   551       mDestroyElementClosure(destroyElementClosure)
   552 {
   553 }
   555 nsObjectHashtable::~nsObjectHashtable()
   556 {
   557     Reset();
   558 }
   561 PLDHashOperator
   562 nsObjectHashtable::CopyElement(PLDHashTable* table,
   563                                PLDHashEntryHdr* hdr,
   564                                uint32_t i, void *arg)
   565 {
   566     nsObjectHashtable *newHashtable = (nsObjectHashtable *)arg;
   567     HTEntry *entry = static_cast<HTEntry*>(hdr);
   569     void* newElement =
   570         newHashtable->mCloneElementFun(entry->key, entry->value,
   571                                        newHashtable->mCloneElementClosure);
   572     if (newElement == nullptr)
   573         return PL_DHASH_STOP;
   574     newHashtable->Put(entry->key, newElement);
   575     return PL_DHASH_NEXT;
   576 }
   578 nsHashtable*
   579 nsObjectHashtable::Clone()
   580 {
   581     if (!mHashtable.ops) return nullptr;
   583     bool threadSafe = false;
   584     if (mLock)
   585         threadSafe = true;
   586     nsObjectHashtable* newHashTable =
   587         new nsObjectHashtable(mCloneElementFun, mCloneElementClosure,
   588                               mDestroyElementFun, mDestroyElementClosure,
   589                               mHashtable.entryCount, threadSafe);
   591     PL_DHashTableEnumerate(&mHashtable, CopyElement, newHashTable);
   592     return newHashTable;
   593 }
   595 void
   596 nsObjectHashtable::Reset()
   597 {
   598     nsHashtable::Reset(mDestroyElementFun, mDestroyElementClosure);
   599 }
   601 bool
   602 nsObjectHashtable::RemoveAndDelete(nsHashKey *aKey)
   603 {
   604     void *value = Remove(aKey);
   605     if (value && mDestroyElementFun)
   606         return !!(*mDestroyElementFun)(aKey, value, mDestroyElementClosure);
   607     return false;
   608 }

mercurial