gecko-dev/xpcom/ds/nsAtomTable.cpp

210 lines
5.6 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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):
* IBM Corp.
*/
#include "nsAtomTable.h"
#include "nsString.h"
#include "nsCRT.h"
#include "plhash.h"
#include "nsISizeOfHandler.h"
#include "nslog.h"
NS_IMPL_LOG(nsAtomTableLog)
#define PRINTF NS_LOG_PRINTF(nsAtomTableLog)
#define FLUSH NS_LOG_FLUSH(nsAtomTableLog)
/**
* The shared hash table for atom lookups.
*/
static nsrefcnt gAtoms;
static struct PLHashTable* gAtomHashTable;
#if defined(DEBUG) && (defined(XP_UNIX) || defined(XP_PC))
static PRIntn PR_CALLBACK
DumpAtomLeaks(PLHashEntry *he, PRIntn index, void *arg)
{
AtomImpl* atom = (AtomImpl*) he->value;
if (atom) {
nsAutoString tmp;
atom->ToString(tmp);
fputs(tmp, stdout);
fputs("\n", stdout);
}
return HT_ENUMERATE_NEXT;
}
#endif
NS_COM void NS_PurgeAtomTable(void)
{
if (gAtomHashTable) {
#if defined(DEBUG) && (defined(XP_UNIX) || defined(XP_PC))
if (gAtoms) {
if (NS_LOG_ENABLED(nsAtomTableLog)) {
PRINTF("*** leaking %d atoms\n", gAtoms);
PL_HashTableEnumerateEntries(gAtomHashTable, DumpAtomLeaks, 0);
}
}
#endif
PL_HashTableDestroy(gAtomHashTable);
gAtomHashTable = nsnull;
}
}
AtomImpl::AtomImpl()
{
NS_INIT_REFCNT();
// Every live atom holds a reference on the atom hashtable
gAtoms++;
}
AtomImpl::~AtomImpl()
{
NS_PRECONDITION(nsnull != gAtomHashTable, "null atom hashtable");
if (nsnull != gAtomHashTable) {
PL_HashTableRemove(gAtomHashTable, mString);
nsrefcnt cnt = --gAtoms;
if (0 == cnt) {
// When the last atom is destroyed, the atom arena is destroyed
NS_ASSERTION(0 == gAtomHashTable->nentries, "bad atom table");
PL_HashTableDestroy(gAtomHashTable);
gAtomHashTable = nsnull;
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS1(AtomImpl, nsIAtom)
void* AtomImpl::operator new ( size_t size, const nsAReadableString& aString )
{
/*
Note: since the |size| will initially also include the |PRUnichar| member
|mString|, our size calculation will give us one character too many.
We use that extra character for a zero-terminator.
Note: this construction is not guaranteed to be possible by the C++
compiler. A more reliable scheme is used by |nsShared[C]String|s, see
http://lxr.mozilla.org/seamonkey/source/xpcom/ds/nsSharedString.h#174
*/
size += aString.Length() * sizeof(PRUnichar);
AtomImpl* ii = (AtomImpl*) ::operator new(size);
PRUnichar* toBegin = &ii->mString[0];
nsReadingIterator<PRUnichar> fromBegin, fromEnd;
*copy_string(aString.BeginReading(fromBegin), aString.EndReading(fromEnd), toBegin) = PRUnichar(0);
return ii;
}
NS_IMETHODIMP
AtomImpl::ToString(nsAWritableString& aBuf) /*FIX: const */
{
aBuf.SetLength(0);
aBuf.Append(mString, nsCRT::strlen(mString));
return NS_OK;
}
NS_IMETHODIMP
AtomImpl::GetUnicode(const PRUnichar **aResult) /*FIX: const */
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = mString;
return NS_OK;
}
NS_IMETHODIMP
AtomImpl::SizeOf(nsISizeOfHandler* aHandler, PRUint32* _retval) /*FIX: const */
{
NS_ENSURE_ARG_POINTER(_retval);
PRUint32 sum = sizeof(*this) + nsCRT::strlen(mString) * sizeof(PRUnichar);
*_retval = sum;
return NS_OK;
}
//----------------------------------------------------------------------
static PLHashNumber HashKey(const PRUnichar* k)
{
PRUint32 hashResult = 0;
PRUint32 N = nsCRT::strlen(k);
for ( PRUint32 n=0; n<N; ++n )
hashResult = (hashResult<<5) + (hashResult<<2) + hashResult + *k++; // mHash = mHash*37 + *s
return hashResult;
}
static PRIntn CompareKeys( const PRUnichar* k1, const PRUnichar* k2 )
{
return nsCRT::strcmp(k1, k2) == 0;
}
NS_COM nsIAtom* NS_NewAtom(const char* isolatin1)
{
return NS_NewAtom(NS_ConvertASCIItoUCS2(isolatin1));
}
NS_COM nsIAtom* NS_NewAtom( const nsAReadableString& aString )
{
if ( !gAtomHashTable )
gAtomHashTable = PL_NewHashTable(2048, (PLHashFunction)HashKey,
(PLHashComparator)CompareKeys,
(PLHashComparator)0, 0, 0);
const nsPromiseFlatString& flat = PromiseFlatString(aString);
const PRUnichar *str = flat.get();
PRUint32 hashCode = HashKey(str);
PLHashEntry** hep = PL_HashTableRawLookup(gAtomHashTable, hashCode, str);
PLHashEntry* he = *hep;
AtomImpl* id;
if ( he ) {
// if we found one, great
id = NS_STATIC_CAST(AtomImpl*, he->value);
} else {
// otherwise, we'll make a new atom
id = new (aString) AtomImpl();
if ( id ) {
PL_HashTableRawAdd(gAtomHashTable, hep, hashCode, id->mString, id);
}
}
NS_IF_ADDREF(id);
return id;
}
NS_COM nsIAtom* NS_NewAtom( const PRUnichar* str )
{
return NS_NewAtom(nsDependentString(str));
}
NS_COM nsrefcnt NS_GetNumberOfAtoms(void)
{
if (nsnull != gAtomHashTable) {
NS_PRECONDITION(nsrefcnt(gAtomHashTable->nentries) == gAtoms, "bad atom table");
}
return gAtoms;
}