mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
1023 lines
28 KiB
C++
1023 lines
28 KiB
C++
/* -*- 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 <string.h>
|
|
#include "prmem.h"
|
|
#include "prlog.h"
|
|
#include "nsHashtable.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsIObjectInputStream.h"
|
|
#include "nsIObjectOutputStream.h"
|
|
#include "nsCRT.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// These functions really should be part of nspr, and have internal knowledge
|
|
// of its workings. They allow the PLHashTable to be embedded in the structure
|
|
// of the nsHashtable, thereby avoiding a secondary allocation. I've added them
|
|
// here because we don't have the "right" to add anything to nspr at this point.
|
|
|
|
#include "prbit.h"
|
|
|
|
/* Compute the number of buckets in ht */
|
|
#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
|
|
|
|
/* The smallest table has 16 buckets */
|
|
#define MINBUCKETSLOG2 4
|
|
#define MINBUCKETS (1 << MINBUCKETSLOG2)
|
|
|
|
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
|
|
#define OVERLOADED(n) ((n) - ((n) >> 3))
|
|
|
|
/* Compute the number of entries below which we shrink the table by half */
|
|
#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
|
|
|
|
PR_IMPLEMENT(PRStatus)
|
|
PL_HashTableInit(PLHashTable *ht, PRUint32 n, PLHashFunction keyHash,
|
|
PLHashComparator keyCompare, PLHashComparator valueCompare,
|
|
const PLHashAllocOps *allocOps, void *allocPriv)
|
|
{
|
|
PRSize nb;
|
|
|
|
if (n <= MINBUCKETS) {
|
|
n = MINBUCKETSLOG2;
|
|
} else {
|
|
n = PR_CeilingLog2(n);
|
|
if ((PRInt32)n < 0)
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
#if 0 // if we were in nspr...
|
|
if (!allocOps) allocOps = &defaultHashAllocOps;
|
|
#else
|
|
PR_ASSERT(allocOps);
|
|
#endif
|
|
|
|
memset(ht, 0, sizeof *ht);
|
|
ht->shift = PL_HASH_BITS - n;
|
|
n = 1 << n;
|
|
#if defined(WIN16)
|
|
if (n > 16000) {
|
|
(*allocOps->freeTable)(allocPriv, ht);
|
|
return PR_FAILURE;
|
|
}
|
|
#endif /* WIN16 */
|
|
nb = n * sizeof(PLHashEntry *);
|
|
ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
|
|
if (!ht->buckets) {
|
|
(*allocOps->freeTable)(allocPriv, ht);
|
|
return PR_FAILURE;
|
|
}
|
|
memset(ht->buckets, 0, nb);
|
|
|
|
ht->keyHash = keyHash;
|
|
ht->keyCompare = keyCompare;
|
|
ht->valueCompare = valueCompare;
|
|
ht->allocOps = allocOps;
|
|
ht->allocPriv = allocPriv;
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
PR_IMPLEMENT(void)
|
|
PL_HashTableFinalize(PLHashTable *ht)
|
|
{
|
|
PRUint32 i, n;
|
|
PLHashEntry *he, *next;
|
|
const PLHashAllocOps *allocOps = ht->allocOps;
|
|
void *allocPriv = ht->allocPriv;
|
|
|
|
n = NBUCKETS(ht);
|
|
for (i = 0; i < n; i++) {
|
|
for (he = ht->buckets[i]; he; he = next) {
|
|
next = he->next;
|
|
(*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
|
|
#endif
|
|
(*allocOps->freeTable)(allocPriv, ht->buckets);
|
|
#ifdef DEBUG
|
|
memset(ht, 0xDB, sizeof *ht);
|
|
#endif
|
|
}
|
|
|
|
// end of nspr stuff
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Key operations
|
|
//
|
|
|
|
static PLHashNumber PR_CALLBACK _hashValue(const void *key)
|
|
{
|
|
return ((const nsHashKey *) key)->HashCode();
|
|
}
|
|
|
|
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;
|
|
switch (thunk->fn((nsHashKey *) he->key, he->value, thunk->arg)) {
|
|
case kHashEnumerateNext:
|
|
return HT_ENUMERATE_NEXT;
|
|
case kHashEnumerateRemove:
|
|
return HT_ENUMERATE_REMOVE;
|
|
case kHashEnumerateUnhash:
|
|
return HT_ENUMERATE_UNHASH;
|
|
}
|
|
return HT_ENUMERATE_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);
|
|
PRStatus status = PL_HashTableInit(&mHashtable,
|
|
aInitSize,
|
|
_hashValue,
|
|
_hashKeyCompare,
|
|
_hashValueCompare,
|
|
&_hashAllocOps,
|
|
NULL);
|
|
PR_ASSERT(status == PR_SUCCESS);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HASHMETER
|
|
PRIntn DontEnum(PLHashEntry *he, PRIntn i, void *arg) {
|
|
return HT_ENUMERATE_STOP;
|
|
}
|
|
#endif
|
|
|
|
nsHashtable::~nsHashtable() {
|
|
#ifdef HASHMETER
|
|
PL_HashTableDump(&mHashtable, DontEnum, stdout);
|
|
#endif
|
|
MOZ_COUNT_DTOR(nsHashtable);
|
|
PL_HashTableFinalize(&mHashtable);
|
|
if (mLock) PR_DestroyLock(mLock);
|
|
}
|
|
|
|
PRBool nsHashtable::Exists(nsHashKey *aKey)
|
|
{
|
|
PLHashNumber hash = aKey->HashCode();
|
|
|
|
if (mLock) PR_Lock(mLock);
|
|
|
|
PLHashEntry *const*hep = mEnumerating ?
|
|
PL_HashTableRawLookupConst(&mHashtable, hash, (void *) aKey) :
|
|
PL_HashTableRawLookup(&mHashtable, hash, (void *) aKey);
|
|
|
|
if (mLock) PR_Unlock(mLock);
|
|
|
|
return *hep != NULL;
|
|
}
|
|
|
|
void *nsHashtable::Put(nsHashKey *aKey, void *aData)
|
|
{
|
|
void *res = NULL;
|
|
PLHashNumber hash = aKey->HashCode();
|
|
PLHashEntry *he;
|
|
|
|
if (mLock) PR_Lock(mLock);
|
|
|
|
// shouldn't be adding an item during enumeration
|
|
PR_ASSERT(!mEnumerating);
|
|
PLHashEntry **hep = PL_HashTableRawLookup(&mHashtable, hash, (void *) aKey);
|
|
|
|
if ((he = *hep) != NULL) {
|
|
res = he->value;
|
|
he->value = aData;
|
|
} else {
|
|
nsHashKey* key = aKey->Clone();
|
|
if (key) {
|
|
PL_HashTableRawAdd(&mHashtable, hep, hash,
|
|
(void *)key, aData);
|
|
}
|
|
else
|
|
res = NULL;
|
|
}
|
|
|
|
if (mLock) PR_Unlock(mLock);
|
|
|
|
return res;
|
|
}
|
|
|
|
void *nsHashtable::Get(nsHashKey *aKey)
|
|
{
|
|
if (mLock) PR_Lock(mLock);
|
|
|
|
void *ret = mEnumerating ?
|
|
PL_HashTableLookupConst(&mHashtable, (void *) aKey) :
|
|
PL_HashTableLookup(&mHashtable, (void *) aKey);
|
|
if (mLock) PR_Unlock(mLock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *nsHashtable::Remove(nsHashKey *aKey)
|
|
{
|
|
PLHashNumber hash = aKey->HashCode();
|
|
PLHashEntry *he;
|
|
|
|
if (mLock) PR_Lock(mLock);
|
|
|
|
// shouldn't be adding an item during enumeration
|
|
PR_ASSERT(!mEnumerating);
|
|
PLHashEntry **hep = PL_HashTableRawLookup(&mHashtable, hash, (void *) aKey);
|
|
void *res = NULL;
|
|
|
|
if ((he = *hep) != NULL) {
|
|
res = he->value;
|
|
PL_HashTableRawRemove(&mHashtable, 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 = (mLock != nsnull);
|
|
nsHashtable *newHashTable = new nsHashtable(mHashtable.nentries, threadSafe);
|
|
|
|
PL_HashTableEnumerateEntries(&mHashtable, _hashEnumerateShare, newHashTable);
|
|
return newHashTable;
|
|
}
|
|
|
|
void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* aClosure)
|
|
{
|
|
PRBool wasEnumerating = mEnumerating;
|
|
mEnumerating = PR_TRUE;
|
|
_HashEnumerateArgs thunk;
|
|
thunk.fn = aEnumFunc;
|
|
thunk.arg = aClosure;
|
|
PL_HashTableEnumerateEntries(&mHashtable, _hashEnumerate, &thunk);
|
|
mEnumerating = wasEnumerating;
|
|
}
|
|
|
|
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;
|
|
}
|
|
return HT_ENUMERATE_REMOVE;
|
|
}
|
|
|
|
void nsHashtable::Reset() {
|
|
Reset(NULL);
|
|
}
|
|
|
|
void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* aClosure)
|
|
{
|
|
_HashEnumerateArgs thunk, *thunkp;
|
|
if (!destroyFunc) {
|
|
thunkp = nsnull;
|
|
} else {
|
|
thunkp = &thunk;
|
|
thunk.fn = destroyFunc;
|
|
thunk.arg = aClosure;
|
|
}
|
|
PL_HashTableEnumerateEntries(&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)) {
|
|
PRStatus status = PL_HashTableInit(&mHashtable,
|
|
count,
|
|
_hashValue,
|
|
_hashKeyCompare,
|
|
_hashValueCompare,
|
|
&_hashAllocOps,
|
|
NULL);
|
|
if (status != PR_SUCCESS) {
|
|
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
|
|
{
|
|
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.nentries;
|
|
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)
|
|
{
|
|
nsresult rv = aStream->ReadStringZ(&mStr);
|
|
if (NS_SUCCEEDED(rv))
|
|
mStrLen = strlen(mStr);
|
|
*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)
|
|
{
|
|
nsresult rv = aStream->ReadWStringZ(&mStr);
|
|
if (NS_SUCCEEDED(rv))
|
|
mStrLen = nsCRT::strlen(mStr);
|
|
*aResult = rv;
|
|
MOZ_COUNT_CTOR(nsStringKey);
|
|
}
|
|
|
|
nsresult
|
|
nsStringKey::Write(nsIObjectOutputStream* aStream) const
|
|
{
|
|
return aStream->WriteWStringZ(mStr);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Copy Constructor
|
|
// We need to free mBuf if the object is passed with mOwnership as OWN. As the
|
|
// destructor here is freeing mBuf in that case, mBuf is NOT getting leaked.
|
|
|
|
nsOpaqueKey::nsOpaqueKey(const nsOpaqueKey& aKey)
|
|
: mBuf(aKey.mBuf), mBufLen(aKey.mBufLen), mOwnership(aKey.mOwnership)
|
|
{
|
|
if (mOwnership != NEVER_OWN) {
|
|
char* newBuf = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(mBufLen));
|
|
if (!newBuf) {
|
|
// Pray we don't dangle!
|
|
mOwnership = NEVER_OWN;
|
|
} else {
|
|
memcpy(newBuf, mBuf, mBufLen);
|
|
mBuf = newBuf;
|
|
mOwnership = OWN;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
mKeyType = OpaqueKey;
|
|
#endif
|
|
MOZ_COUNT_CTOR(nsOpaqueKey);
|
|
}
|
|
|
|
nsOpaqueKey::nsOpaqueKey(const char* str, PRUint32 strLen, Ownership own)
|
|
: mBuf((char*)str), mBufLen(strLen), mOwnership(own)
|
|
{
|
|
NS_ASSERTION(mBuf, "null buffer");
|
|
#ifdef DEBUG
|
|
mKeyType = OpaqueKey;
|
|
#endif
|
|
MOZ_COUNT_CTOR(nsOpaqueKey);
|
|
}
|
|
|
|
nsOpaqueKey::~nsOpaqueKey(void)
|
|
{
|
|
if (mOwnership == OWN)
|
|
nsMemory::Free(mBuf);
|
|
MOZ_COUNT_DTOR(nsOpaqueKey);
|
|
}
|
|
|
|
PRUint32
|
|
nsOpaqueKey::HashCode(void) const
|
|
{
|
|
return nsCRT::BufferHashCode(mBuf, mBufLen);
|
|
}
|
|
|
|
PRBool
|
|
nsOpaqueKey::Equals(const nsHashKey* aKey) const
|
|
{
|
|
NS_ASSERTION(aKey->GetKeyType() == OpaqueKey, "mismatched key types");
|
|
nsOpaqueKey* other = (nsOpaqueKey*)aKey;
|
|
if (mBufLen != other->mBufLen)
|
|
return PR_FALSE;
|
|
return memcmp(mBuf, other->mBuf, mBufLen) == 0;
|
|
}
|
|
|
|
nsHashKey*
|
|
nsOpaqueKey::Clone() const
|
|
{
|
|
if (mOwnership == NEVER_OWN)
|
|
return new nsOpaqueKey(mBuf, mBufLen, 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 = mBufLen * sizeof(char);
|
|
char* str = (char*)nsMemory::Alloc(len + sizeof(char));
|
|
if (str == NULL)
|
|
return NULL;
|
|
memcpy(str, mBuf, len);
|
|
str[len] = 0;
|
|
return new nsOpaqueKey(str, mBufLen, OWN);
|
|
}
|
|
|
|
nsOpaqueKey::nsOpaqueKey(nsIObjectInputStream* aStream, nsresult *aResult)
|
|
: mBuf(nsnull), mBufLen(0), mOwnership(OWN)
|
|
{
|
|
nsresult rv = aStream->Read32(&mBufLen);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = aStream->ReadBytes(&mBuf, mBufLen);
|
|
*aResult = rv;
|
|
MOZ_COUNT_CTOR(nsOpaqueKey);
|
|
}
|
|
|
|
nsresult
|
|
nsOpaqueKey::Write(nsIObjectOutputStream* aStream) const
|
|
{
|
|
nsresult rv = aStream->Write32(mBufLen);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = aStream->WriteBytes(mBuf, mBufLen);
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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,
|
|
mHashtable.nentries, threadSafe);
|
|
|
|
PL_HashTableEnumerateEntries(&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;
|
|
}
|
|
|
|
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 = (mLock != nsnull);
|
|
nsSupportsHashtable* newHashTable =
|
|
new nsSupportsHashtable(mHashtable.nentries, threadSafe);
|
|
|
|
PL_HashTableEnumerateEntries(&mHashtable, EnumerateCopy, newHashTable);
|
|
return newHashTable;
|
|
}
|
|
|
|
void
|
|
nsSupportsHashtable::Reset()
|
|
{
|
|
Enumerate(ReleaseElement, nsnull);
|
|
nsHashtable::Reset();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|