зеркало из https://github.com/mozilla/gecko-dev.git
891 строка
24 KiB
C++
891 строка
24 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
// vim:cindent:ts=2:et:sw=2:
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla 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/MPL/
|
|
*
|
|
* 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 the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsAtomTable.h"
|
|
#include "nsStaticAtom.h"
|
|
#include "nsString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsUTF8Utils.h"
|
|
#include "nsCRT.h"
|
|
#include "pldhash.h"
|
|
#include "prenv.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsHashKeys.h"
|
|
|
|
#define PL_ARENA_CONST_ALIGN_MASK 3
|
|
#include "plarena.h"
|
|
|
|
class nsStaticAtomWrapper;
|
|
|
|
/**
|
|
* The shared hash table for atom lookups.
|
|
*
|
|
* XXX This should be manipulated in a threadsafe way or we should make
|
|
* sure it's only manipulated from the main thread. Probably the latter
|
|
* is better, since the former would hurt performance.
|
|
*
|
|
* If |gAtomTable.ops| is 0, then the table is uninitialized.
|
|
*/
|
|
static PLDHashTable gAtomTable;
|
|
|
|
// this is where we keep the nsStaticAtomWrapper objects
|
|
|
|
static PLArenaPool* gStaticAtomArena = 0;
|
|
|
|
/**
|
|
* A hashtable of static atoms that existed at app startup. This hashtable helps
|
|
* nsHtml5AtomTable.
|
|
*/
|
|
static nsDataHashtable<nsStringHashKey, nsIAtom*>* gStaticAtomTable = 0;
|
|
|
|
/**
|
|
* Whether it is still OK to add atoms to gStaticAtomTable.
|
|
*/
|
|
static PRBool gStaticAtomTableSealed = PR_FALSE;
|
|
|
|
class nsStaticAtomWrapper : public nsIAtom
|
|
{
|
|
public:
|
|
nsStaticAtomWrapper(const nsStaticAtom* aAtom, PRUint32 aLength) :
|
|
mStaticAtom(aAtom), mLength(aLength)
|
|
{
|
|
MOZ_COUNT_CTOR(nsStaticAtomWrapper);
|
|
}
|
|
~nsStaticAtomWrapper() { // no subclasses -> not virtual
|
|
// this is arena allocated and won't be called except in debug
|
|
// builds. If this function ever does anything non-debug, be sure
|
|
// to get rid of the ifdefs in AtomTableClearEntry!
|
|
MOZ_COUNT_DTOR(nsStaticAtomWrapper);
|
|
}
|
|
|
|
NS_IMETHOD QueryInterface(REFNSIID aIID,
|
|
void** aInstancePtr);
|
|
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
|
NS_IMETHOD_(nsrefcnt) Release(void);
|
|
|
|
NS_DECL_NSIATOM
|
|
|
|
const nsStaticAtom* GetStaticAtom() const {
|
|
return mStaticAtom;
|
|
}
|
|
|
|
PRUint32 getLength() const {
|
|
return mLength;
|
|
}
|
|
|
|
private:
|
|
const nsStaticAtom* mStaticAtom;
|
|
|
|
// The length of the string in the static atom. The static atom
|
|
// (nsStaticAtom) doesn't hold a length, so we keep it here in the
|
|
// wrapper instead.
|
|
PRUint32 mLength;
|
|
};
|
|
|
|
// The |key| pointer in the various PLDHashTable callbacks we use is an
|
|
// AtomTableClearEntry*. These pointers can come from two places: either a
|
|
// (probably stack-allocated) string key being passed to PL_DHashTableOperate,
|
|
// or an actual entry in the atom table. PLDHashTable reseves the keyHash
|
|
// values 0 and 1 for internal use, which means that the *PLDHashTable code*
|
|
// will never pass an entry whose keyhash is 0 or 1 to our hooks. That means we
|
|
// can use those values to tell whether an AtomTableEntry is a string key
|
|
// created by a PLDHashTable code caller or an actual live AtomTableEntry used
|
|
// by our PLDHashTable.
|
|
//
|
|
// Evil? Yes, but kinda neat too :-)
|
|
//
|
|
// An AtomTableEntry is a UTF-8 string key if keyHash is 0, in that
|
|
// case mBits points to a UTF-8 encoded char *. If keyHash is 1 the
|
|
// AtomTableEntry is a UTF-16 encoded string key and mBits points to a
|
|
// UTF-16 encoded PRUnichar *.
|
|
//
|
|
// If keyHash is any other value (> 1), the AtomTableEntry is an
|
|
// actual live entry in the table, and then mBits & ~0x1 in the
|
|
// AtomTableEntry points to an AtomImpl or a nsStaticAtomWrapper,
|
|
// indicated by the first bit of PtrBits.
|
|
// XXX This whole mess could be vastly simplified now that pldhash
|
|
// no longer has a getKey callback.
|
|
typedef PRUword PtrBits;
|
|
|
|
struct AtomTableEntry : public PLDHashEntryHdr {
|
|
// If keyHash > 1, mBits & 0x1 means (mBits & ~0x1) points to an
|
|
// nsStaticAtomWrapper else it points to an nsAtomImpl
|
|
PtrBits mBits;
|
|
PRUint32 mLength;
|
|
|
|
inline AtomTableEntry(const char *aString, PRUint32 aLength)
|
|
: mBits(PtrBits(aString)), mLength(aLength)
|
|
{
|
|
keyHash = 0;
|
|
}
|
|
|
|
inline AtomTableEntry(const PRUnichar *aString, PRUint32 aLength)
|
|
: mBits(PtrBits(aString)), mLength(aLength)
|
|
{
|
|
keyHash = 1;
|
|
}
|
|
|
|
inline PRBool IsStaticAtom() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"IsStaticAtom() called on non-atom AtomTableEntry!");
|
|
return (mBits & 0x1) != 0;
|
|
}
|
|
|
|
inline PRBool IsUTF8String() const {
|
|
return keyHash == 0;
|
|
}
|
|
|
|
inline PRBool IsUTF16String() const {
|
|
return keyHash == 1;
|
|
}
|
|
|
|
inline void SetAtomImpl(AtomImpl* aAtom) {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"SetAtomImpl() called on non-atom AtomTableEntry!");
|
|
NS_ASSERTION(aAtom, "Setting null atom");
|
|
mBits = PtrBits(aAtom);
|
|
mLength = aAtom->mLength;
|
|
}
|
|
|
|
inline void SetStaticAtomWrapper(nsStaticAtomWrapper* aAtom) {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"SetStaticAtomWrapper() called on non-atom AtomTableEntry!");
|
|
NS_ASSERTION(aAtom, "Setting null atom");
|
|
NS_ASSERTION((PtrBits(aAtom) & ~0x1) == PtrBits(aAtom),
|
|
"Pointers must align or this is broken");
|
|
|
|
mBits = PtrBits(aAtom) | 0x1;
|
|
mLength = aAtom->getLength();
|
|
}
|
|
|
|
inline void ClearAtom() {
|
|
mBits = nsnull;
|
|
}
|
|
|
|
inline PRBool HasValue() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"HasValue() called on non-atom AtomTableEntry!");
|
|
return (mBits & ~0x1) != 0;
|
|
}
|
|
|
|
// these accessors assume that you already know the type
|
|
inline AtomImpl *GetAtomImpl() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"GetAtomImpl() called on non-atom AtomTableEntry!");
|
|
NS_ASSERTION(!IsStaticAtom(), "This is a static atom, not an AtomImpl");
|
|
return (AtomImpl*) (mBits & ~0x1);
|
|
}
|
|
|
|
inline nsStaticAtomWrapper *GetStaticAtomWrapper() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"GetStaticAtomWrapper() called on non-atom AtomTableEntry!");
|
|
NS_ASSERTION(IsStaticAtom(), "This is an AtomImpl, not a static atom");
|
|
return (nsStaticAtomWrapper*) (mBits & ~0x1);
|
|
}
|
|
|
|
inline const nsStaticAtom* GetStaticAtom() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"GetStaticAtom() called on non-atom AtomTableEntry!");
|
|
return GetStaticAtomWrapper()->GetStaticAtom();
|
|
}
|
|
|
|
// type-agnostic accessors
|
|
|
|
// get the string buffer
|
|
inline const char* getAtomString() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"getAtomString() called on non-atom AtomTableEntry!");
|
|
|
|
return IsStaticAtom() ? GetStaticAtom()->mString : GetAtomImpl()->mString;
|
|
}
|
|
|
|
// get the string buffer
|
|
inline const char* getUTF8String() const {
|
|
NS_ASSERTION(keyHash == 0,
|
|
"getUTF8String() called on non-UTF8 AtomTableEntry!");
|
|
|
|
return (char *)mBits;
|
|
}
|
|
|
|
// get the string buffer
|
|
inline const PRUnichar* getUTF16String() const {
|
|
NS_ASSERTION(keyHash == 1,
|
|
"getUTF16String() called on non-UTF16 AtomTableEntry!");
|
|
|
|
return (PRUnichar *)mBits;
|
|
}
|
|
|
|
// get the string length
|
|
inline PRUint32 getLength() const {
|
|
return mLength;
|
|
}
|
|
|
|
// get an addreffed nsIAtom - not using already_AddRef'ed atom
|
|
// because the callers are not (and should not be) using nsCOMPtr
|
|
inline nsIAtom* GetAtom() const {
|
|
NS_ASSERTION(keyHash > 1,
|
|
"GetAtom() called on non-atom AtomTableEntry!");
|
|
|
|
nsIAtom* result;
|
|
|
|
if (IsStaticAtom())
|
|
result = GetStaticAtomWrapper();
|
|
else {
|
|
result = GetAtomImpl();
|
|
NS_ADDREF(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
static PLDHashNumber
|
|
AtomTableGetHash(PLDHashTable *table, const void *key)
|
|
{
|
|
const AtomTableEntry *e = static_cast<const AtomTableEntry*>(key);
|
|
|
|
if (e->IsUTF16String()) {
|
|
return nsCRT::HashCodeAsUTF8(e->getUTF16String(), e->getLength());;
|
|
}
|
|
|
|
NS_ASSERTION(e->IsUTF8String(),
|
|
"AtomTableGetHash() called on non-string-key AtomTableEntry!");
|
|
|
|
return nsCRT::HashCode(e->getUTF8String(), e->getLength());
|
|
}
|
|
|
|
static PRBool
|
|
AtomTableMatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry,
|
|
const void *key)
|
|
{
|
|
const AtomTableEntry *he = static_cast<const AtomTableEntry*>(entry);
|
|
const AtomTableEntry *strKey = static_cast<const AtomTableEntry*>(key);
|
|
|
|
const char *atomString = he->getAtomString();
|
|
|
|
if (strKey->IsUTF16String()) {
|
|
return
|
|
CompareUTF8toUTF16(nsDependentCSubstring(atomString, atomString + he->getLength()),
|
|
nsDependentSubstring(strKey->getUTF16String(),
|
|
strKey->getUTF16String() + strKey->getLength())) == 0;
|
|
}
|
|
|
|
PRUint32 length = he->getLength();
|
|
if (length != strKey->getLength()) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
const char *str;
|
|
|
|
if (strKey->IsUTF8String()) {
|
|
str = strKey->getUTF8String();
|
|
} else {
|
|
str = strKey->getAtomString();
|
|
}
|
|
|
|
return memcmp(atomString, str, length * sizeof(char)) == 0;
|
|
}
|
|
|
|
static void
|
|
AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
|
|
{
|
|
AtomTableEntry *he = static_cast<AtomTableEntry*>(entry);
|
|
|
|
if (!he->IsStaticAtom()) {
|
|
AtomImpl *atom = he->GetAtomImpl();
|
|
// Normal |AtomImpl| atoms are deleted when their refcount hits 0, and
|
|
// they then remove themselves from the table. In other words, they
|
|
// are owned by the callers who own references to them.
|
|
// |PermanentAtomImpl| permanent atoms ignore their refcount and are
|
|
// deleted when they are removed from the table at table destruction.
|
|
// In other words, they are owned by the atom table.
|
|
if (atom->IsPermanent()) {
|
|
he->keyHash = 0;
|
|
|
|
delete static_cast<PermanentAtomImpl*>(atom);
|
|
}
|
|
}
|
|
else {
|
|
he->GetStaticAtomWrapper()->~nsStaticAtomWrapper();
|
|
}
|
|
|
|
he->ClearAtom();
|
|
}
|
|
|
|
static PRBool
|
|
AtomTableInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
|
|
const void *key)
|
|
{
|
|
AtomTableEntry *he = static_cast<AtomTableEntry*>(entry);
|
|
const AtomTableEntry *strKey = static_cast<const AtomTableEntry*>(key);
|
|
|
|
he->mLength = strKey->getLength();
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
|
|
static const PLDHashTableOps AtomTableOps = {
|
|
PL_DHashAllocTable,
|
|
PL_DHashFreeTable,
|
|
AtomTableGetHash,
|
|
AtomTableMatchKey,
|
|
PL_DHashMoveEntryStub,
|
|
AtomTableClearEntry,
|
|
PL_DHashFinalizeStub,
|
|
AtomTableInitEntry
|
|
};
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static PLDHashOperator
|
|
DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he,
|
|
PRUint32 index, void *arg)
|
|
{
|
|
AtomTableEntry *entry = static_cast<AtomTableEntry*>(he);
|
|
|
|
if (entry->IsStaticAtom())
|
|
return PL_DHASH_NEXT;
|
|
|
|
AtomImpl* atom = entry->GetAtomImpl();
|
|
if (!atom->IsPermanent()) {
|
|
++*static_cast<PRUint32*>(arg);
|
|
const char *str;
|
|
atom->GetUTF8String(&str);
|
|
fputs(str, stdout);
|
|
fputs("\n", stdout);
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
#endif
|
|
|
|
static inline
|
|
void PromoteToPermanent(AtomImpl* aAtom)
|
|
{
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
{
|
|
nsrefcnt refcount = aAtom->GetRefCount();
|
|
do {
|
|
NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl");
|
|
} while (refcount);
|
|
}
|
|
#endif
|
|
aAtom = new (aAtom) PermanentAtomImpl();
|
|
}
|
|
|
|
void
|
|
NS_PurgeAtomTable()
|
|
{
|
|
delete gStaticAtomTable;
|
|
|
|
if (gAtomTable.ops) {
|
|
#ifdef DEBUG
|
|
const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
|
|
if (dumpAtomLeaks && *dumpAtomLeaks) {
|
|
PRUint32 leaked = 0;
|
|
printf("*** %d atoms still exist (including permanent):\n",
|
|
gAtomTable.entryCount);
|
|
PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
|
|
printf("*** %u non-permanent atoms leaked\n", leaked);
|
|
}
|
|
#endif
|
|
PL_DHashTableFinish(&gAtomTable);
|
|
gAtomTable.entryCount = 0;
|
|
gAtomTable.ops = nsnull;
|
|
|
|
if (gStaticAtomArena) {
|
|
PL_FinishArenaPool(gStaticAtomArena);
|
|
delete gStaticAtomArena;
|
|
gStaticAtomArena = nsnull;
|
|
}
|
|
}
|
|
}
|
|
|
|
AtomImpl::AtomImpl()
|
|
{
|
|
}
|
|
|
|
AtomImpl::~AtomImpl()
|
|
{
|
|
NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
|
|
// Permanent atoms are removed from the hashtable at shutdown, and we
|
|
// don't want to remove them twice. See comment above in
|
|
// |AtomTableClearEntry|.
|
|
if (!IsPermanentInDestructor()) {
|
|
AtomTableEntry key(mString, mLength);
|
|
PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE);
|
|
if (gAtomTable.entryCount == 0) {
|
|
PL_DHashTableFinish(&gAtomTable);
|
|
NS_ASSERTION(gAtomTable.entryCount == 0,
|
|
"PL_DHashTableFinish changed the entry count");
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(AtomImpl, nsIAtom)
|
|
|
|
PermanentAtomImpl::PermanentAtomImpl()
|
|
: AtomImpl()
|
|
{
|
|
}
|
|
|
|
PermanentAtomImpl::~PermanentAtomImpl()
|
|
{
|
|
// So we can tell if we were permanent while running the base class dtor.
|
|
mRefCnt = REFCNT_PERMANENT_SENTINEL;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::AddRef()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
return 2;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::Release()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
return 1;
|
|
}
|
|
|
|
/* virtual */ PRBool
|
|
AtomImpl::IsPermanent()
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
/* virtual */ PRBool
|
|
PermanentAtomImpl::IsPermanent()
|
|
{
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void* AtomImpl::operator new ( size_t size, const nsACString& aString ) CPP_THROW_NEW
|
|
{
|
|
/*
|
|
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(char);
|
|
AtomImpl* ii = static_cast<AtomImpl*>(::operator new(size));
|
|
NS_ENSURE_TRUE(ii, nsnull);
|
|
|
|
char* toBegin = &ii->mString[0];
|
|
nsACString::const_iterator fromBegin, fromEnd;
|
|
*copy_string(aString.BeginReading(fromBegin), aString.EndReading(fromEnd), toBegin) = '\0';
|
|
ii->mLength = aString.Length();
|
|
return ii;
|
|
}
|
|
|
|
void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW {
|
|
NS_ASSERTION(!aAtom->IsPermanent(),
|
|
"converting atom that's already permanent");
|
|
|
|
// Just let the constructor overwrite the vtable pointer.
|
|
return aAtom;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AtomImpl::ToString(nsAString& aBuf)
|
|
{
|
|
CopyUTF8toUTF16(nsDependentCString(mString, mLength), aBuf);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AtomImpl::ToUTF8String(nsACString& aBuf)
|
|
{
|
|
aBuf.Assign(mString, mLength);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AtomImpl::GetUTF8String(const char **aResult)
|
|
{
|
|
NS_PRECONDITION(aResult, "null out param");
|
|
*aResult = mString;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AtomImpl::EqualsUTF8(const nsACString& aString, PRBool* aResult)
|
|
{
|
|
*aResult = aString.Equals(nsDependentCString(mString, mLength));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AtomImpl::Equals(const nsAString& aString, PRBool* aResult)
|
|
{
|
|
*aResult = CompareUTF8toUTF16(nsDependentCString(mString, mLength),
|
|
aString) == 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(PRBool)
|
|
AtomImpl::IsStaticAtom()
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// wrapper class for the nsStaticAtom struct
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsStaticAtomWrapper::AddRef()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
return 2;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsStaticAtomWrapper::Release()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
return 1;
|
|
}
|
|
|
|
NS_IMPL_QUERY_INTERFACE1(nsStaticAtomWrapper, nsIAtom)
|
|
|
|
NS_IMETHODIMP
|
|
nsStaticAtomWrapper::GetUTF8String(const char** aResult)
|
|
{
|
|
*aResult = mStaticAtom->mString;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsStaticAtomWrapper::ToString(nsAString& aBuf)
|
|
{
|
|
// static should always be always ASCII, to allow tools like gperf
|
|
// to generate the tables, and to avoid unnecessary conversion
|
|
NS_ASSERTION(nsCRT::IsAscii(mStaticAtom->mString),
|
|
"Data loss - atom should be ASCII");
|
|
CopyASCIItoUTF16(nsDependentCString(mStaticAtom->mString, mLength), aBuf);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsStaticAtomWrapper::ToUTF8String(nsACString& aBuf)
|
|
{
|
|
aBuf.Assign(mStaticAtom->mString);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsStaticAtomWrapper::EqualsUTF8(const nsACString& aString, PRBool* aResult)
|
|
{
|
|
*aResult = aString.Equals(nsDependentCString(mStaticAtom->mString, mLength));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsStaticAtomWrapper::Equals(const nsAString& aString, PRBool* aResult)
|
|
{
|
|
*aResult = CompareUTF8toUTF16(nsDependentCString(mStaticAtom->mString,
|
|
mLength),
|
|
aString) == 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(PRBool)
|
|
nsStaticAtomWrapper::IsStaticAtom()
|
|
{
|
|
return PR_TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
static nsStaticAtomWrapper*
|
|
WrapStaticAtom(const nsStaticAtom* aAtom, PRUint32 aLength)
|
|
{
|
|
if (!gStaticAtomArena) {
|
|
gStaticAtomArena = new PLArenaPool;
|
|
if (!gStaticAtomArena)
|
|
return nsnull;
|
|
|
|
PL_INIT_ARENA_POOL(gStaticAtomArena, "nsStaticAtomArena", 4096);
|
|
}
|
|
|
|
void* mem;
|
|
PL_ARENA_ALLOCATE(mem, gStaticAtomArena, sizeof(nsStaticAtomWrapper));
|
|
|
|
nsStaticAtomWrapper* wrapper =
|
|
new (mem) nsStaticAtomWrapper(aAtom, aLength);
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
#define ATOM_HASHTABLE_INITIAL_SIZE 4096
|
|
|
|
static inline AtomTableEntry*
|
|
GetAtomHashEntry(const char* aString, PRUint32 aLength)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
if (!gAtomTable.ops &&
|
|
!PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
|
|
sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE)) {
|
|
gAtomTable.ops = nsnull;
|
|
return nsnull;
|
|
}
|
|
|
|
AtomTableEntry key(aString, aLength);
|
|
return static_cast<AtomTableEntry*>
|
|
(PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
|
|
}
|
|
|
|
static inline AtomTableEntry*
|
|
GetAtomHashEntry(const PRUnichar* aString, PRUint32 aLength)
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
|
if (!gAtomTable.ops &&
|
|
!PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
|
|
sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE)) {
|
|
gAtomTable.ops = nsnull;
|
|
return nsnull;
|
|
}
|
|
|
|
AtomTableEntry key(aString, aLength);
|
|
return static_cast<AtomTableEntry*>
|
|
(PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
|
|
}
|
|
|
|
NS_COM nsresult
|
|
NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, PRUint32 aAtomCount)
|
|
{
|
|
// this does three things:
|
|
// 1) wraps each static atom in a wrapper, if necessary
|
|
// 2) initializes the address pointed to by each mBits slot
|
|
// 3) puts the atom into the static atom table as well
|
|
|
|
if (!gStaticAtomTable && !gStaticAtomTableSealed) {
|
|
gStaticAtomTable = new nsDataHashtable<nsStringHashKey, nsIAtom*>();
|
|
if (!gStaticAtomTable || !gStaticAtomTable->Init()) {
|
|
delete gStaticAtomTable;
|
|
gStaticAtomTable = nsnull;
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
for (PRUint32 i=0; i<aAtomCount; i++) {
|
|
NS_ASSERTION(nsCRT::IsAscii(aAtoms[i].mString),
|
|
"Static atoms must be ASCII!");
|
|
|
|
PRUint32 stringLen = strlen(aAtoms[i].mString);
|
|
|
|
AtomTableEntry *he =
|
|
GetAtomHashEntry(aAtoms[i].mString, stringLen);
|
|
|
|
if (he->HasValue() && aAtoms[i].mAtom) {
|
|
// there already is an atom with this name in the table.. but we
|
|
// still have to update mBits
|
|
if (!he->IsStaticAtom() && !he->GetAtomImpl()->IsPermanent()) {
|
|
// since we wanted to create a static atom but there is
|
|
// already one there, we convert it to a non-refcounting
|
|
// permanent atom
|
|
PromoteToPermanent(he->GetAtomImpl());
|
|
}
|
|
|
|
// and now, if the consumer wants to remember this value in a
|
|
// slot, we do so
|
|
if (aAtoms[i].mAtom)
|
|
*aAtoms[i].mAtom = he->GetAtom();
|
|
}
|
|
else {
|
|
nsStaticAtomWrapper* atom = WrapStaticAtom(&aAtoms[i], stringLen);
|
|
NS_ASSERTION(atom, "Failed to wrap static atom");
|
|
|
|
// but even if atom is null, no real difference in code..
|
|
he->SetStaticAtomWrapper(atom);
|
|
if (aAtoms[i].mAtom)
|
|
*aAtoms[i].mAtom = atom;
|
|
|
|
if (!gStaticAtomTableSealed) {
|
|
nsAutoString key;
|
|
atom->ToString(key);
|
|
gStaticAtomTable->Put(key, atom);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewAtom(const char* aUTF8String)
|
|
{
|
|
return NS_NewAtom(nsDependentCString(aUTF8String));
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewAtom(const nsACString& aUTF8String)
|
|
{
|
|
AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(),
|
|
aUTF8String.Length());
|
|
|
|
if (!he) {
|
|
return nsnull;
|
|
}
|
|
|
|
NS_ASSERTION(!he->IsUTF8String() && !he->IsUTF16String(),
|
|
"Atom hash entry is string? Should be atom!");
|
|
|
|
if (he->HasValue())
|
|
return he->GetAtom();
|
|
|
|
AtomImpl* atom = new (aUTF8String) AtomImpl();
|
|
he->SetAtomImpl(atom);
|
|
if (!atom) {
|
|
PL_DHashTableRawRemove(&gAtomTable, he);
|
|
return nsnull;
|
|
}
|
|
|
|
NS_ADDREF(atom);
|
|
return atom;
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewAtom(const PRUnichar* aUTF16String)
|
|
{
|
|
return NS_NewAtom(nsDependentString(aUTF16String));
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewAtom(const nsAString& aUTF16String)
|
|
{
|
|
AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(),
|
|
aUTF16String.Length());
|
|
|
|
if (he->HasValue())
|
|
return he->GetAtom();
|
|
|
|
// MSVC.NET doesn't like passing a temporary NS_ConvertUTF16toUTF8() to
|
|
// operator new, so declare one as a local instead.
|
|
NS_ConvertUTF16toUTF8 str(aUTF16String);
|
|
AtomImpl* atom = new (str) AtomImpl();
|
|
he->SetAtomImpl(atom);
|
|
if (!atom) {
|
|
PL_DHashTableRawRemove(&gAtomTable, he);
|
|
return nsnull;
|
|
}
|
|
|
|
NS_ADDREF(atom);
|
|
return atom;
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewPermanentAtom(const char* aUTF8String)
|
|
{
|
|
return NS_NewPermanentAtom(nsDependentCString(aUTF8String));
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewPermanentAtom(const nsACString& aUTF8String)
|
|
{
|
|
AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(),
|
|
aUTF8String.Length());
|
|
|
|
if (he->HasValue() && he->IsStaticAtom())
|
|
return he->GetStaticAtomWrapper();
|
|
|
|
// either there is no atom and we'll create an AtomImpl,
|
|
// or there is an existing AtomImpl
|
|
AtomImpl* atom = he->GetAtomImpl();
|
|
|
|
if (atom) {
|
|
// ensure that it's permanent
|
|
if (!atom->IsPermanent()) {
|
|
PromoteToPermanent(atom);
|
|
}
|
|
} else {
|
|
// otherwise, make a new atom
|
|
atom = new (aUTF8String) PermanentAtomImpl();
|
|
he->SetAtomImpl(atom);
|
|
if ( !atom ) {
|
|
PL_DHashTableRawRemove(&gAtomTable, he);
|
|
return nsnull;
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(atom);
|
|
return atom;
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewPermanentAtom(const nsAString& aUTF16String)
|
|
{
|
|
return NS_NewPermanentAtom(NS_ConvertUTF16toUTF8(aUTF16String));
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_NewPermanentAtom(const PRUnichar* aUTF16String)
|
|
{
|
|
return NS_NewPermanentAtom(NS_ConvertUTF16toUTF8(aUTF16String));
|
|
}
|
|
|
|
NS_COM nsrefcnt
|
|
NS_GetNumberOfAtoms(void)
|
|
{
|
|
return gAtomTable.entryCount;
|
|
}
|
|
|
|
NS_COM nsIAtom*
|
|
NS_GetStaticAtom(const nsAString& aUTF16String)
|
|
{
|
|
NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
|
|
NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
|
|
nsIAtom* atom;
|
|
if (!gStaticAtomTable->Get(aUTF16String, &atom)) {
|
|
atom = nsnull;
|
|
}
|
|
return atom;
|
|
}
|
|
|
|
NS_COM void
|
|
NS_SealStaticAtomTable()
|
|
{
|
|
gStaticAtomTableSealed = PR_TRUE;
|
|
}
|