mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
500 lines
12 KiB
C++
500 lines
12 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):
|
|
*/
|
|
|
|
#include "prmem.h"
|
|
#include "prlog.h"
|
|
#include "nsHashtable.h"
|
|
|
|
//
|
|
// Key operations
|
|
//
|
|
|
|
static PR_CALLBACK PLHashNumber _hashValue(const void *key)
|
|
{
|
|
return ((const nsHashKey *) key)->HashValue();
|
|
}
|
|
|
|
static PR_CALLBACK PRIntn _hashKeyCompare(const void *key1, const void *key2) {
|
|
return ((const nsHashKey *) key1)->Equals((const nsHashKey *) key2);
|
|
}
|
|
|
|
static PR_CALLBACK PRIntn _hashValueCompare(const void *value1,
|
|
const void *value2) {
|
|
// We're not going to make any assumptions about value equality
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Memory callbacks
|
|
//
|
|
|
|
static PR_CALLBACK void *_hashAllocTable(void *pool, PRSize size) {
|
|
return PR_MALLOC(size);
|
|
}
|
|
|
|
static PR_CALLBACK void _hashFreeTable(void *pool, void *item) {
|
|
PR_DELETE(item);
|
|
}
|
|
|
|
static PR_CALLBACK PLHashEntry *_hashAllocEntry(void *pool, const void *key) {
|
|
return PR_NEW(PLHashEntry);
|
|
}
|
|
|
|
static PR_CALLBACK void _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 PR_CALLBACK PRIntn _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
|
|
//
|
|
nsHashKey::nsHashKey(void)
|
|
{
|
|
}
|
|
|
|
nsHashKey::~nsHashKey(void)
|
|
{
|
|
}
|
|
|
|
nsHashtable::nsHashtable(PRUint32 aInitSize, PRBool threadSafe)
|
|
: mLock(NULL)
|
|
{
|
|
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() {
|
|
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 PR_CALLBACK PRIntn _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 PR_CALLBACK PRIntn _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)
|
|
{
|
|
mStr.Assign(str);
|
|
}
|
|
|
|
nsStringKey::nsStringKey(const PRUnichar* str)
|
|
{
|
|
mStr.Assign(str);
|
|
}
|
|
|
|
nsStringKey::nsStringKey(const nsStr& str)
|
|
{
|
|
mStr.Assign(str);
|
|
}
|
|
|
|
nsStringKey::~nsStringKey(void)
|
|
{
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
PR_CALLBACK PRIntn
|
|
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*
|
|
|
|
static PR_CALLBACK PRBool
|
|
_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);
|
|
}
|
|
|
|
void*
|
|
nsSupportsHashtable::Put(nsHashKey *aKey, void *aData)
|
|
{
|
|
nsISupports* element = NS_REINTERPRET_CAST(nsISupports*, aData);
|
|
NS_IF_ADDREF(element);
|
|
return nsHashtable::Put(aKey, aData);
|
|
}
|
|
|
|
void*
|
|
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 data;
|
|
}
|
|
|
|
void*
|
|
nsSupportsHashtable::Remove(nsHashKey *aKey)
|
|
{
|
|
void* data = nsHashtable::Remove(aKey);
|
|
if (!data)
|
|
return nsnull;
|
|
nsISupports* element = NS_STATIC_CAST(nsISupports*, data);
|
|
NS_IF_RELEASE(element);
|
|
return data;
|
|
}
|
|
|
|
static PR_CALLBACK PRIntn
|
|
_hashEnumerateCopy(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, _hashEnumerateCopy, 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|