michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:cindent:ts=2:et:sw=2: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Compiler.h" michael@0: #include "mozilla/HashFunctions.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: #include "nsAtomTable.h" michael@0: #include "nsStaticAtom.h" michael@0: #include "nsString.h" michael@0: #include "nsCRT.h" michael@0: #include "pldhash.h" michael@0: #include "prenv.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsUnicharUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #if defined(__clang__) michael@0: # pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" michael@0: #elif MOZ_IS_GCC michael@0: # if MOZ_GCC_VERSION_AT_LEAST(4, 7, 0) michael@0: # pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" michael@0: # endif michael@0: #endif michael@0: michael@0: /** michael@0: * The shared hash table for atom lookups. michael@0: * michael@0: * XXX This should be manipulated in a threadsafe way or we should make michael@0: * sure it's only manipulated from the main thread. Probably the latter michael@0: * is better, since the former would hurt performance. michael@0: * michael@0: * If |gAtomTable.ops| is 0, then the table is uninitialized. michael@0: */ michael@0: static PLDHashTable gAtomTable; michael@0: michael@0: /** michael@0: * A hashtable of static atoms that existed at app startup. This hashtable helps michael@0: * nsHtml5AtomTable. michael@0: */ michael@0: static nsDataHashtable* gStaticAtomTable = 0; michael@0: michael@0: /** michael@0: * Whether it is still OK to add atoms to gStaticAtomTable. michael@0: */ michael@0: static bool gStaticAtomTableSealed = false; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Note that AtomImpl objects are sometimes converted into PermanentAtomImpl michael@0: * objects using placement new and just overwriting the vtable pointer. michael@0: */ michael@0: michael@0: class AtomImpl : public nsIAtom { michael@0: public: michael@0: AtomImpl(const nsAString& aString, uint32_t aHash); michael@0: michael@0: // This is currently only used during startup when creating a permanent atom michael@0: // from NS_RegisterStaticAtoms michael@0: AtomImpl(nsStringBuffer* aData, uint32_t aLength, uint32_t aHash); michael@0: michael@0: protected: michael@0: // This is only intended to be used when a normal atom is turned into a michael@0: // permanent one. michael@0: AtomImpl() { michael@0: // We can't really assert that mString is a valid nsStringBuffer string, michael@0: // so do the best we can do and check for some consistencies. michael@0: NS_ASSERTION((mLength + 1) * sizeof(char16_t) <= michael@0: nsStringBuffer::FromData(mString)->StorageSize() && michael@0: mString[mLength] == 0, michael@0: "Not initialized atom"); michael@0: } michael@0: michael@0: // We don't need a virtual destructor here because PermanentAtomImpl michael@0: // deletions aren't handled through Release(). michael@0: ~AtomImpl(); michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIATOM michael@0: michael@0: enum { REFCNT_PERMANENT_SENTINEL = UINT32_MAX }; michael@0: michael@0: virtual bool IsPermanent(); michael@0: michael@0: // We can't use the virtual function in the base class destructor. michael@0: bool IsPermanentInDestructor() { michael@0: return mRefCnt == REFCNT_PERMANENT_SENTINEL; michael@0: } michael@0: michael@0: // for |#ifdef NS_BUILD_REFCNT_LOGGING| access to reference count michael@0: nsrefcnt GetRefCount() { return mRefCnt; } michael@0: michael@0: size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; michael@0: }; michael@0: michael@0: /** michael@0: * A non-refcounted implementation of nsIAtom. michael@0: */ michael@0: michael@0: class PermanentAtomImpl MOZ_FINAL : public AtomImpl { michael@0: public: michael@0: PermanentAtomImpl(const nsAString& aString, PLDHashNumber aKeyHash) michael@0: : AtomImpl(aString, aKeyHash) michael@0: {} michael@0: PermanentAtomImpl(nsStringBuffer* aData, uint32_t aLength, michael@0: PLDHashNumber aKeyHash) michael@0: : AtomImpl(aData, aLength, aKeyHash) michael@0: {} michael@0: PermanentAtomImpl() michael@0: {} michael@0: michael@0: ~PermanentAtomImpl(); michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef(); michael@0: NS_IMETHOD_(MozExternalRefCountType) Release(); michael@0: michael@0: virtual bool IsPermanent(); michael@0: michael@0: // SizeOfIncludingThis() isn't needed -- the one inherited from AtomImpl is michael@0: // good enough, because PermanentAtomImpl doesn't add any new data members. michael@0: michael@0: void* operator new(size_t size, AtomImpl* aAtom) CPP_THROW_NEW; michael@0: void* operator new(size_t size) CPP_THROW_NEW michael@0: { michael@0: return ::operator new(size); michael@0: } michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: struct AtomTableEntry : public PLDHashEntryHdr { michael@0: AtomImpl* mAtom; michael@0: }; michael@0: michael@0: struct AtomTableKey michael@0: { michael@0: AtomTableKey(const char16_t* aUTF16String, uint32_t aLength, michael@0: /*inout*/ uint32_t& aHash) michael@0: : mUTF16String(aUTF16String), michael@0: mUTF8String(nullptr), michael@0: mLength(aLength) michael@0: { michael@0: if (aHash) { michael@0: MOZ_ASSERT(aHash == HashString(mUTF16String, mLength)); michael@0: mHash = aHash; michael@0: } else { michael@0: UpdateHashKey(); michael@0: aHash = mHash; michael@0: } michael@0: } michael@0: michael@0: AtomTableKey(const char* aUTF8String, uint32_t aLength, michael@0: /*inout*/ uint32_t& aHash) michael@0: : mUTF16String(nullptr), michael@0: mUTF8String(aUTF8String), michael@0: mLength(aLength) michael@0: { michael@0: if (aHash) { michael@0: mozilla::DebugOnly err; michael@0: MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err)); michael@0: mHash = aHash; michael@0: } else { michael@0: UpdateHashKey(); michael@0: aHash = mHash; michael@0: } michael@0: } michael@0: michael@0: const char16_t* mUTF16String; michael@0: const char* mUTF8String; michael@0: uint32_t mLength; michael@0: uint32_t mHash; michael@0: michael@0: void UpdateHashKey() michael@0: { michael@0: if (mUTF8String) { michael@0: bool err; michael@0: mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err); michael@0: if (err) { michael@0: mUTF8String = nullptr; michael@0: mLength = 0; michael@0: mHash = 0; michael@0: } michael@0: } else { michael@0: mHash = HashString(mUTF16String, mLength); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: static PLDHashNumber michael@0: AtomTableGetHash(PLDHashTable *table, const void *key) michael@0: { michael@0: const AtomTableKey *k = static_cast(key); michael@0: return k->mHash; michael@0: } michael@0: michael@0: static bool michael@0: AtomTableMatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: const AtomTableEntry *he = static_cast(entry); michael@0: const AtomTableKey *k = static_cast(key); michael@0: michael@0: if (k->mUTF8String) { michael@0: return michael@0: CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String, michael@0: k->mUTF8String + k->mLength), michael@0: nsDependentAtomString(he->mAtom)) == 0; michael@0: } michael@0: michael@0: uint32_t length = he->mAtom->GetLength(); michael@0: if (length != k->mLength) { michael@0: return false; michael@0: } michael@0: michael@0: return memcmp(he->mAtom->GetUTF16String(), michael@0: k->mUTF16String, length * sizeof(char16_t)) == 0; michael@0: } michael@0: michael@0: static void michael@0: AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) michael@0: { michael@0: // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and michael@0: // they then remove themselves from the table. In other words, they michael@0: // are owned by the callers who own references to them. michael@0: // |PermanentAtomImpl| permanent atoms ignore their refcount and are michael@0: // deleted when they are removed from the table at table destruction. michael@0: // In other words, they are owned by the atom table. michael@0: michael@0: AtomImpl *atom = static_cast(entry)->mAtom; michael@0: if (atom->IsPermanent()) { michael@0: // Note that the cast here is important since AtomImpls doesn't have a michael@0: // virtual dtor. michael@0: delete static_cast(atom); michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: AtomTableInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: static_cast(entry)->mAtom = nullptr; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static const PLDHashTableOps AtomTableOps = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: AtomTableGetHash, michael@0: AtomTableMatchKey, michael@0: PL_DHashMoveEntryStub, michael@0: AtomTableClearEntry, michael@0: PL_DHashFinalizeStub, michael@0: AtomTableInitEntry michael@0: }; michael@0: michael@0: michael@0: #ifdef DEBUG michael@0: static PLDHashOperator michael@0: DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he, michael@0: uint32_t index, void *arg) michael@0: { michael@0: AtomTableEntry *entry = static_cast(he); michael@0: michael@0: AtomImpl* atom = entry->mAtom; michael@0: if (!atom->IsPermanent()) { michael@0: ++*static_cast(arg); michael@0: nsAutoCString str; michael@0: atom->ToUTF8String(str); michael@0: fputs(str.get(), stdout); michael@0: fputs("\n", stdout); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: #endif michael@0: michael@0: static inline michael@0: void PromoteToPermanent(AtomImpl* aAtom) michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: { michael@0: nsrefcnt refcount = aAtom->GetRefCount(); michael@0: do { michael@0: NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl"); michael@0: } while (refcount); michael@0: } michael@0: #endif michael@0: aAtom = new (aAtom) PermanentAtomImpl(); michael@0: } michael@0: michael@0: void michael@0: NS_PurgeAtomTable() michael@0: { michael@0: delete gStaticAtomTable; michael@0: michael@0: if (gAtomTable.ops) { michael@0: #ifdef DEBUG michael@0: const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS"); michael@0: if (dumpAtomLeaks && *dumpAtomLeaks) { michael@0: uint32_t leaked = 0; michael@0: printf("*** %d atoms still exist (including permanent):\n", michael@0: gAtomTable.entryCount); michael@0: PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked); michael@0: printf("*** %u non-permanent atoms leaked\n", leaked); michael@0: } michael@0: #endif michael@0: PL_DHashTableFinish(&gAtomTable); michael@0: gAtomTable.entryCount = 0; michael@0: gAtomTable.ops = nullptr; michael@0: } michael@0: } michael@0: michael@0: AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash) michael@0: { michael@0: mLength = aString.Length(); michael@0: nsRefPtr buf = nsStringBuffer::FromString(aString); michael@0: if (buf) { michael@0: mString = static_cast(buf->Data()); michael@0: } else { michael@0: buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t)); michael@0: mString = static_cast(buf->Data()); michael@0: CopyUnicodeTo(aString, 0, mString, mLength); michael@0: mString[mLength] = char16_t(0); michael@0: } michael@0: michael@0: mHash = aHash; michael@0: MOZ_ASSERT(mHash == HashString(mString, mLength)); michael@0: michael@0: NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated"); michael@0: NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t), michael@0: "enough storage"); michael@0: NS_ASSERTION(Equals(aString), "correct data"); michael@0: michael@0: // Take ownership of buffer michael@0: mozilla::unused << buf.forget(); michael@0: } michael@0: michael@0: AtomImpl::AtomImpl(nsStringBuffer* aStringBuffer, uint32_t aLength, michael@0: uint32_t aHash) michael@0: { michael@0: mLength = aLength; michael@0: mString = static_cast(aStringBuffer->Data()); michael@0: // Technically we could currently avoid doing this addref by instead making michael@0: // the static atom buffers have an initial refcount of 2. michael@0: aStringBuffer->AddRef(); michael@0: michael@0: mHash = aHash; michael@0: MOZ_ASSERT(mHash == HashString(mString, mLength)); michael@0: michael@0: NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated"); michael@0: NS_ASSERTION(aStringBuffer && michael@0: aStringBuffer->StorageSize() == (mLength+1) * sizeof(char16_t), michael@0: "correct storage"); michael@0: } michael@0: michael@0: AtomImpl::~AtomImpl() michael@0: { michael@0: NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable"); michael@0: // Permanent atoms are removed from the hashtable at shutdown, and we michael@0: // don't want to remove them twice. See comment above in michael@0: // |AtomTableClearEntry|. michael@0: if (!IsPermanentInDestructor()) { michael@0: AtomTableKey key(mString, mLength, mHash); michael@0: PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE); michael@0: if (gAtomTable.entryCount == 0) { michael@0: PL_DHashTableFinish(&gAtomTable); michael@0: NS_ASSERTION(gAtomTable.entryCount == 0, michael@0: "PL_DHashTableFinish changed the entry count"); michael@0: } michael@0: } michael@0: michael@0: nsStringBuffer::FromData(mString)->Release(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom) michael@0: michael@0: PermanentAtomImpl::~PermanentAtomImpl() michael@0: { michael@0: // So we can tell if we were permanent while running the base class dtor. michael@0: mRefCnt = REFCNT_PERMANENT_SENTINEL; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::AddRef() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); michael@0: return 2; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::Release() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); michael@0: return 1; michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: AtomImpl::IsPermanent() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: PermanentAtomImpl::IsPermanent() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW { michael@0: MOZ_ASSERT(!aAtom->IsPermanent(), michael@0: "converting atom that's already permanent"); michael@0: michael@0: // Just let the constructor overwrite the vtable pointer. michael@0: return aAtom; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AtomImpl::ScriptableToString(nsAString& aBuf) michael@0: { michael@0: nsStringBuffer::FromData(mString)->ToString(mLength, aBuf); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AtomImpl::ToUTF8String(nsACString& aBuf) michael@0: { michael@0: CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: AtomImpl::EqualsUTF8(const nsACString& aString) michael@0: { michael@0: return CompareUTF8toUTF16(aString, michael@0: nsDependentString(mString, mLength)) == 0; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AtomImpl::ScriptableEquals(const nsAString& aString, bool* aResult) michael@0: { michael@0: *aResult = aString.Equals(nsDependentString(mString, mLength)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: AtomImpl::IsStaticAtom() michael@0: { michael@0: return IsPermanent(); michael@0: } michael@0: michael@0: size_t michael@0: AtomImpl::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return aMallocSizeOf(this) + michael@0: nsStringBuffer::FromData(mString)-> michael@0: SizeOfIncludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: static size_t michael@0: SizeOfAtomTableEntryExcludingThis(PLDHashEntryHdr *aHdr, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void *aArg) michael@0: { michael@0: AtomTableEntry* entry = static_cast(aHdr); michael@0: return entry->mAtom->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfStaticAtomTableEntryExcludingThis(const nsAString& aKey, michael@0: nsIAtom* const& aData, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aArg) michael@0: { michael@0: return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf) { michael@0: size_t n = 0; michael@0: if (gAtomTable.ops) { michael@0: n += PL_DHashTableSizeOfExcludingThis(&gAtomTable, michael@0: SizeOfAtomTableEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: } michael@0: if (gStaticAtomTable) { michael@0: n += gStaticAtomTable->SizeOfIncludingThis(SizeOfStaticAtomTableEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: #define ATOM_HASHTABLE_INITIAL_SIZE 4096 michael@0: michael@0: static inline void michael@0: EnsureTableExists() michael@0: { michael@0: if (!gAtomTable.ops) { michael@0: PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0, michael@0: sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE); michael@0: } michael@0: } michael@0: michael@0: static inline AtomTableEntry* michael@0: GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t& aHash) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); michael@0: EnsureTableExists(); michael@0: AtomTableKey key(aString, aLength, aHash); michael@0: AtomTableEntry* e = michael@0: static_cast michael@0: (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD)); michael@0: if (!e) { michael@0: NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize); michael@0: } michael@0: return e; michael@0: } michael@0: michael@0: static inline AtomTableEntry* michael@0: GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t& aHash) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); michael@0: EnsureTableExists(); michael@0: AtomTableKey key(aString, aLength, aHash); michael@0: AtomTableEntry* e = michael@0: static_cast michael@0: (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD)); michael@0: if (!e) { michael@0: NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize); michael@0: } michael@0: return e; michael@0: } michael@0: michael@0: class CheckStaticAtomSizes michael@0: { michael@0: CheckStaticAtomSizes() { michael@0: static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) == michael@0: sizeof(nsStringBuffer().mRefCount)) && michael@0: (sizeof(nsFakeStringBuffer<1>().mSize) == michael@0: sizeof(nsStringBuffer().mStorageSize)) && michael@0: (offsetof(nsFakeStringBuffer<1>, mRefCnt) == michael@0: offsetof(nsStringBuffer, mRefCount)) && michael@0: (offsetof(nsFakeStringBuffer<1>, mSize) == michael@0: offsetof(nsStringBuffer, mStorageSize)) && michael@0: (offsetof(nsFakeStringBuffer<1>, mStringData) == michael@0: sizeof(nsStringBuffer)), michael@0: "mocked-up strings' representations should be compatible"); michael@0: } michael@0: }; michael@0: michael@0: nsresult michael@0: RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount) michael@0: { michael@0: // this does three things: michael@0: // 1) wraps each static atom in a wrapper, if necessary michael@0: // 2) initializes the address pointed to by each mBits slot michael@0: // 3) puts the atom into the static atom table as well michael@0: michael@0: if (!gStaticAtomTable && !gStaticAtomTableSealed) { michael@0: gStaticAtomTable = new nsDataHashtable(); michael@0: } michael@0: michael@0: for (uint32_t i=0; iData()), michael@0: "Static atoms must be ASCII!"); michael@0: michael@0: uint32_t stringLen = michael@0: aAtoms[i].mStringBuffer->StorageSize() / sizeof(char16_t) - 1; michael@0: michael@0: uint32_t hash = 0; michael@0: AtomTableEntry *he = michael@0: GetAtomHashEntry((char16_t*)aAtoms[i].mStringBuffer->Data(), michael@0: stringLen, hash); michael@0: michael@0: if (he->mAtom) { michael@0: // there already is an atom with this name in the table.. but we michael@0: // still have to update mBits michael@0: if (!he->mAtom->IsPermanent()) { michael@0: // since we wanted to create a static atom but there is michael@0: // already one there, we convert it to a non-refcounting michael@0: // permanent atom michael@0: PromoteToPermanent(he->mAtom); michael@0: } michael@0: michael@0: *aAtoms[i].mAtom = he->mAtom; michael@0: } michael@0: else { michael@0: AtomImpl* atom = new PermanentAtomImpl(aAtoms[i].mStringBuffer, michael@0: stringLen, michael@0: hash); michael@0: he->mAtom = atom; michael@0: *aAtoms[i].mAtom = atom; michael@0: michael@0: if (!gStaticAtomTableSealed) { michael@0: gStaticAtomTable->Put(nsAtomString(atom), atom); michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewAtom(const char* aUTF8String) michael@0: { michael@0: return NS_NewAtom(nsDependentCString(aUTF8String)); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewAtom(const nsACString& aUTF8String) michael@0: { michael@0: uint32_t hash = 0; michael@0: AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(), michael@0: aUTF8String.Length(), michael@0: hash); michael@0: michael@0: if (he->mAtom) { michael@0: nsCOMPtr atom = he->mAtom; michael@0: michael@0: return atom.forget(); michael@0: } michael@0: michael@0: // This results in an extra addref/release of the nsStringBuffer. michael@0: // Unfortunately there doesn't seem to be any APIs to avoid that. michael@0: // Actually, now there is, sort of: ForgetSharedBuffer. michael@0: nsString str; michael@0: CopyUTF8toUTF16(aUTF8String, str); michael@0: nsRefPtr atom = new AtomImpl(str, hash); michael@0: michael@0: he->mAtom = atom; michael@0: michael@0: return atom.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewAtom(const char16_t* aUTF16String) michael@0: { michael@0: return NS_NewAtom(nsDependentString(aUTF16String)); michael@0: } michael@0: michael@0: already_AddRefed michael@0: NS_NewAtom(const nsAString& aUTF16String) michael@0: { michael@0: uint32_t hash = 0; michael@0: AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(), michael@0: aUTF16String.Length(), michael@0: hash); michael@0: michael@0: if (he->mAtom) { michael@0: nsCOMPtr atom = he->mAtom; michael@0: michael@0: return atom.forget(); michael@0: } michael@0: michael@0: nsRefPtr atom = new AtomImpl(aUTF16String, hash); michael@0: he->mAtom = atom; michael@0: michael@0: return atom.forget(); michael@0: } michael@0: michael@0: nsIAtom* michael@0: NS_NewPermanentAtom(const nsAString& aUTF16String) michael@0: { michael@0: uint32_t hash = 0; michael@0: AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(), michael@0: aUTF16String.Length(), michael@0: hash); michael@0: michael@0: AtomImpl* atom = he->mAtom; michael@0: if (atom) { michael@0: if (!atom->IsPermanent()) { michael@0: PromoteToPermanent(atom); michael@0: } michael@0: } michael@0: else { michael@0: atom = new PermanentAtomImpl(aUTF16String, hash); michael@0: he->mAtom = atom; michael@0: } michael@0: michael@0: // No need to addref since permanent atoms aren't refcounted anyway michael@0: return atom; michael@0: } michael@0: michael@0: nsrefcnt michael@0: NS_GetNumberOfAtoms(void) michael@0: { michael@0: return gAtomTable.entryCount; michael@0: } michael@0: michael@0: nsIAtom* michael@0: NS_GetStaticAtom(const nsAString& aUTF16String) michael@0: { michael@0: NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet."); michael@0: NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet."); michael@0: nsIAtom* atom; michael@0: if (!gStaticAtomTable->Get(aUTF16String, &atom)) { michael@0: atom = nullptr; michael@0: } michael@0: return atom; michael@0: } michael@0: michael@0: void michael@0: NS_SealStaticAtomTable() michael@0: { michael@0: gStaticAtomTableSealed = true; michael@0: }