1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/ds/nsAtomTable.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,717 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim:cindent:ts=2:et:sw=2: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/Assertions.h" 1.11 +#include "mozilla/Attributes.h" 1.12 +#include "mozilla/Compiler.h" 1.13 +#include "mozilla/HashFunctions.h" 1.14 +#include "mozilla/MemoryReporting.h" 1.15 +#include "mozilla/DebugOnly.h" 1.16 +#include "mozilla/unused.h" 1.17 + 1.18 +#include "nsAtomTable.h" 1.19 +#include "nsStaticAtom.h" 1.20 +#include "nsString.h" 1.21 +#include "nsCRT.h" 1.22 +#include "pldhash.h" 1.23 +#include "prenv.h" 1.24 +#include "nsThreadUtils.h" 1.25 +#include "nsDataHashtable.h" 1.26 +#include "nsHashKeys.h" 1.27 +#include "nsAutoPtr.h" 1.28 +#include "nsUnicharUtils.h" 1.29 + 1.30 +using namespace mozilla; 1.31 + 1.32 +#if defined(__clang__) 1.33 +# pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" 1.34 +#elif MOZ_IS_GCC 1.35 +# if MOZ_GCC_VERSION_AT_LEAST(4, 7, 0) 1.36 +# pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" 1.37 +# endif 1.38 +#endif 1.39 + 1.40 +/** 1.41 + * The shared hash table for atom lookups. 1.42 + * 1.43 + * XXX This should be manipulated in a threadsafe way or we should make 1.44 + * sure it's only manipulated from the main thread. Probably the latter 1.45 + * is better, since the former would hurt performance. 1.46 + * 1.47 + * If |gAtomTable.ops| is 0, then the table is uninitialized. 1.48 + */ 1.49 +static PLDHashTable gAtomTable; 1.50 + 1.51 +/** 1.52 + * A hashtable of static atoms that existed at app startup. This hashtable helps 1.53 + * nsHtml5AtomTable. 1.54 + */ 1.55 +static nsDataHashtable<nsStringHashKey, nsIAtom*>* gStaticAtomTable = 0; 1.56 + 1.57 +/** 1.58 + * Whether it is still OK to add atoms to gStaticAtomTable. 1.59 + */ 1.60 +static bool gStaticAtomTableSealed = false; 1.61 + 1.62 +//---------------------------------------------------------------------- 1.63 + 1.64 +/** 1.65 + * Note that AtomImpl objects are sometimes converted into PermanentAtomImpl 1.66 + * objects using placement new and just overwriting the vtable pointer. 1.67 + */ 1.68 + 1.69 +class AtomImpl : public nsIAtom { 1.70 +public: 1.71 + AtomImpl(const nsAString& aString, uint32_t aHash); 1.72 + 1.73 + // This is currently only used during startup when creating a permanent atom 1.74 + // from NS_RegisterStaticAtoms 1.75 + AtomImpl(nsStringBuffer* aData, uint32_t aLength, uint32_t aHash); 1.76 + 1.77 +protected: 1.78 + // This is only intended to be used when a normal atom is turned into a 1.79 + // permanent one. 1.80 + AtomImpl() { 1.81 + // We can't really assert that mString is a valid nsStringBuffer string, 1.82 + // so do the best we can do and check for some consistencies. 1.83 + NS_ASSERTION((mLength + 1) * sizeof(char16_t) <= 1.84 + nsStringBuffer::FromData(mString)->StorageSize() && 1.85 + mString[mLength] == 0, 1.86 + "Not initialized atom"); 1.87 + } 1.88 + 1.89 + // We don't need a virtual destructor here because PermanentAtomImpl 1.90 + // deletions aren't handled through Release(). 1.91 + ~AtomImpl(); 1.92 + 1.93 +public: 1.94 + NS_DECL_ISUPPORTS 1.95 + NS_DECL_NSIATOM 1.96 + 1.97 + enum { REFCNT_PERMANENT_SENTINEL = UINT32_MAX }; 1.98 + 1.99 + virtual bool IsPermanent(); 1.100 + 1.101 + // We can't use the virtual function in the base class destructor. 1.102 + bool IsPermanentInDestructor() { 1.103 + return mRefCnt == REFCNT_PERMANENT_SENTINEL; 1.104 + } 1.105 + 1.106 + // for |#ifdef NS_BUILD_REFCNT_LOGGING| access to reference count 1.107 + nsrefcnt GetRefCount() { return mRefCnt; } 1.108 + 1.109 + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 1.110 +}; 1.111 + 1.112 +/** 1.113 + * A non-refcounted implementation of nsIAtom. 1.114 + */ 1.115 + 1.116 +class PermanentAtomImpl MOZ_FINAL : public AtomImpl { 1.117 +public: 1.118 + PermanentAtomImpl(const nsAString& aString, PLDHashNumber aKeyHash) 1.119 + : AtomImpl(aString, aKeyHash) 1.120 + {} 1.121 + PermanentAtomImpl(nsStringBuffer* aData, uint32_t aLength, 1.122 + PLDHashNumber aKeyHash) 1.123 + : AtomImpl(aData, aLength, aKeyHash) 1.124 + {} 1.125 + PermanentAtomImpl() 1.126 + {} 1.127 + 1.128 + ~PermanentAtomImpl(); 1.129 + NS_IMETHOD_(MozExternalRefCountType) AddRef(); 1.130 + NS_IMETHOD_(MozExternalRefCountType) Release(); 1.131 + 1.132 + virtual bool IsPermanent(); 1.133 + 1.134 + // SizeOfIncludingThis() isn't needed -- the one inherited from AtomImpl is 1.135 + // good enough, because PermanentAtomImpl doesn't add any new data members. 1.136 + 1.137 + void* operator new(size_t size, AtomImpl* aAtom) CPP_THROW_NEW; 1.138 + void* operator new(size_t size) CPP_THROW_NEW 1.139 + { 1.140 + return ::operator new(size); 1.141 + } 1.142 +}; 1.143 + 1.144 +//---------------------------------------------------------------------- 1.145 + 1.146 +struct AtomTableEntry : public PLDHashEntryHdr { 1.147 + AtomImpl* mAtom; 1.148 +}; 1.149 + 1.150 +struct AtomTableKey 1.151 +{ 1.152 + AtomTableKey(const char16_t* aUTF16String, uint32_t aLength, 1.153 + /*inout*/ uint32_t& aHash) 1.154 + : mUTF16String(aUTF16String), 1.155 + mUTF8String(nullptr), 1.156 + mLength(aLength) 1.157 + { 1.158 + if (aHash) { 1.159 + MOZ_ASSERT(aHash == HashString(mUTF16String, mLength)); 1.160 + mHash = aHash; 1.161 + } else { 1.162 + UpdateHashKey(); 1.163 + aHash = mHash; 1.164 + } 1.165 + } 1.166 + 1.167 + AtomTableKey(const char* aUTF8String, uint32_t aLength, 1.168 + /*inout*/ uint32_t& aHash) 1.169 + : mUTF16String(nullptr), 1.170 + mUTF8String(aUTF8String), 1.171 + mLength(aLength) 1.172 + { 1.173 + if (aHash) { 1.174 + mozilla::DebugOnly<bool> err; 1.175 + MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err)); 1.176 + mHash = aHash; 1.177 + } else { 1.178 + UpdateHashKey(); 1.179 + aHash = mHash; 1.180 + } 1.181 + } 1.182 + 1.183 + const char16_t* mUTF16String; 1.184 + const char* mUTF8String; 1.185 + uint32_t mLength; 1.186 + uint32_t mHash; 1.187 + 1.188 + void UpdateHashKey() 1.189 + { 1.190 + if (mUTF8String) { 1.191 + bool err; 1.192 + mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err); 1.193 + if (err) { 1.194 + mUTF8String = nullptr; 1.195 + mLength = 0; 1.196 + mHash = 0; 1.197 + } 1.198 + } else { 1.199 + mHash = HashString(mUTF16String, mLength); 1.200 + } 1.201 + } 1.202 +}; 1.203 + 1.204 +static PLDHashNumber 1.205 +AtomTableGetHash(PLDHashTable *table, const void *key) 1.206 +{ 1.207 + const AtomTableKey *k = static_cast<const AtomTableKey*>(key); 1.208 + return k->mHash; 1.209 +} 1.210 + 1.211 +static bool 1.212 +AtomTableMatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry, 1.213 + const void *key) 1.214 +{ 1.215 + const AtomTableEntry *he = static_cast<const AtomTableEntry*>(entry); 1.216 + const AtomTableKey *k = static_cast<const AtomTableKey*>(key); 1.217 + 1.218 + if (k->mUTF8String) { 1.219 + return 1.220 + CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String, 1.221 + k->mUTF8String + k->mLength), 1.222 + nsDependentAtomString(he->mAtom)) == 0; 1.223 + } 1.224 + 1.225 + uint32_t length = he->mAtom->GetLength(); 1.226 + if (length != k->mLength) { 1.227 + return false; 1.228 + } 1.229 + 1.230 + return memcmp(he->mAtom->GetUTF16String(), 1.231 + k->mUTF16String, length * sizeof(char16_t)) == 0; 1.232 +} 1.233 + 1.234 +static void 1.235 +AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) 1.236 +{ 1.237 + // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and 1.238 + // they then remove themselves from the table. In other words, they 1.239 + // are owned by the callers who own references to them. 1.240 + // |PermanentAtomImpl| permanent atoms ignore their refcount and are 1.241 + // deleted when they are removed from the table at table destruction. 1.242 + // In other words, they are owned by the atom table. 1.243 + 1.244 + AtomImpl *atom = static_cast<AtomTableEntry*>(entry)->mAtom; 1.245 + if (atom->IsPermanent()) { 1.246 + // Note that the cast here is important since AtomImpls doesn't have a 1.247 + // virtual dtor. 1.248 + delete static_cast<PermanentAtomImpl*>(atom); 1.249 + } 1.250 +} 1.251 + 1.252 +static bool 1.253 +AtomTableInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, 1.254 + const void *key) 1.255 +{ 1.256 + static_cast<AtomTableEntry*>(entry)->mAtom = nullptr; 1.257 + 1.258 + return true; 1.259 +} 1.260 + 1.261 + 1.262 +static const PLDHashTableOps AtomTableOps = { 1.263 + PL_DHashAllocTable, 1.264 + PL_DHashFreeTable, 1.265 + AtomTableGetHash, 1.266 + AtomTableMatchKey, 1.267 + PL_DHashMoveEntryStub, 1.268 + AtomTableClearEntry, 1.269 + PL_DHashFinalizeStub, 1.270 + AtomTableInitEntry 1.271 +}; 1.272 + 1.273 + 1.274 +#ifdef DEBUG 1.275 +static PLDHashOperator 1.276 +DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he, 1.277 + uint32_t index, void *arg) 1.278 +{ 1.279 + AtomTableEntry *entry = static_cast<AtomTableEntry*>(he); 1.280 + 1.281 + AtomImpl* atom = entry->mAtom; 1.282 + if (!atom->IsPermanent()) { 1.283 + ++*static_cast<uint32_t*>(arg); 1.284 + nsAutoCString str; 1.285 + atom->ToUTF8String(str); 1.286 + fputs(str.get(), stdout); 1.287 + fputs("\n", stdout); 1.288 + } 1.289 + return PL_DHASH_NEXT; 1.290 +} 1.291 +#endif 1.292 + 1.293 +static inline 1.294 +void PromoteToPermanent(AtomImpl* aAtom) 1.295 +{ 1.296 +#ifdef NS_BUILD_REFCNT_LOGGING 1.297 + { 1.298 + nsrefcnt refcount = aAtom->GetRefCount(); 1.299 + do { 1.300 + NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl"); 1.301 + } while (refcount); 1.302 + } 1.303 +#endif 1.304 + aAtom = new (aAtom) PermanentAtomImpl(); 1.305 +} 1.306 + 1.307 +void 1.308 +NS_PurgeAtomTable() 1.309 +{ 1.310 + delete gStaticAtomTable; 1.311 + 1.312 + if (gAtomTable.ops) { 1.313 +#ifdef DEBUG 1.314 + const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS"); 1.315 + if (dumpAtomLeaks && *dumpAtomLeaks) { 1.316 + uint32_t leaked = 0; 1.317 + printf("*** %d atoms still exist (including permanent):\n", 1.318 + gAtomTable.entryCount); 1.319 + PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked); 1.320 + printf("*** %u non-permanent atoms leaked\n", leaked); 1.321 + } 1.322 +#endif 1.323 + PL_DHashTableFinish(&gAtomTable); 1.324 + gAtomTable.entryCount = 0; 1.325 + gAtomTable.ops = nullptr; 1.326 + } 1.327 +} 1.328 + 1.329 +AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash) 1.330 +{ 1.331 + mLength = aString.Length(); 1.332 + nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString); 1.333 + if (buf) { 1.334 + mString = static_cast<char16_t*>(buf->Data()); 1.335 + } else { 1.336 + buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t)); 1.337 + mString = static_cast<char16_t*>(buf->Data()); 1.338 + CopyUnicodeTo(aString, 0, mString, mLength); 1.339 + mString[mLength] = char16_t(0); 1.340 + } 1.341 + 1.342 + mHash = aHash; 1.343 + MOZ_ASSERT(mHash == HashString(mString, mLength)); 1.344 + 1.345 + NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated"); 1.346 + NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t), 1.347 + "enough storage"); 1.348 + NS_ASSERTION(Equals(aString), "correct data"); 1.349 + 1.350 + // Take ownership of buffer 1.351 + mozilla::unused << buf.forget(); 1.352 +} 1.353 + 1.354 +AtomImpl::AtomImpl(nsStringBuffer* aStringBuffer, uint32_t aLength, 1.355 + uint32_t aHash) 1.356 +{ 1.357 + mLength = aLength; 1.358 + mString = static_cast<char16_t*>(aStringBuffer->Data()); 1.359 + // Technically we could currently avoid doing this addref by instead making 1.360 + // the static atom buffers have an initial refcount of 2. 1.361 + aStringBuffer->AddRef(); 1.362 + 1.363 + mHash = aHash; 1.364 + MOZ_ASSERT(mHash == HashString(mString, mLength)); 1.365 + 1.366 + NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated"); 1.367 + NS_ASSERTION(aStringBuffer && 1.368 + aStringBuffer->StorageSize() == (mLength+1) * sizeof(char16_t), 1.369 + "correct storage"); 1.370 +} 1.371 + 1.372 +AtomImpl::~AtomImpl() 1.373 +{ 1.374 + NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable"); 1.375 + // Permanent atoms are removed from the hashtable at shutdown, and we 1.376 + // don't want to remove them twice. See comment above in 1.377 + // |AtomTableClearEntry|. 1.378 + if (!IsPermanentInDestructor()) { 1.379 + AtomTableKey key(mString, mLength, mHash); 1.380 + PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE); 1.381 + if (gAtomTable.entryCount == 0) { 1.382 + PL_DHashTableFinish(&gAtomTable); 1.383 + NS_ASSERTION(gAtomTable.entryCount == 0, 1.384 + "PL_DHashTableFinish changed the entry count"); 1.385 + } 1.386 + } 1.387 + 1.388 + nsStringBuffer::FromData(mString)->Release(); 1.389 +} 1.390 + 1.391 +NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom) 1.392 + 1.393 +PermanentAtomImpl::~PermanentAtomImpl() 1.394 +{ 1.395 + // So we can tell if we were permanent while running the base class dtor. 1.396 + mRefCnt = REFCNT_PERMANENT_SENTINEL; 1.397 +} 1.398 + 1.399 +NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::AddRef() 1.400 +{ 1.401 + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 1.402 + return 2; 1.403 +} 1.404 + 1.405 +NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::Release() 1.406 +{ 1.407 + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 1.408 + return 1; 1.409 +} 1.410 + 1.411 +/* virtual */ bool 1.412 +AtomImpl::IsPermanent() 1.413 +{ 1.414 + return false; 1.415 +} 1.416 + 1.417 +/* virtual */ bool 1.418 +PermanentAtomImpl::IsPermanent() 1.419 +{ 1.420 + return true; 1.421 +} 1.422 + 1.423 +void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW { 1.424 + MOZ_ASSERT(!aAtom->IsPermanent(), 1.425 + "converting atom that's already permanent"); 1.426 + 1.427 + // Just let the constructor overwrite the vtable pointer. 1.428 + return aAtom; 1.429 +} 1.430 + 1.431 +NS_IMETHODIMP 1.432 +AtomImpl::ScriptableToString(nsAString& aBuf) 1.433 +{ 1.434 + nsStringBuffer::FromData(mString)->ToString(mLength, aBuf); 1.435 + return NS_OK; 1.436 +} 1.437 + 1.438 +NS_IMETHODIMP 1.439 +AtomImpl::ToUTF8String(nsACString& aBuf) 1.440 +{ 1.441 + CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf); 1.442 + return NS_OK; 1.443 +} 1.444 + 1.445 +NS_IMETHODIMP_(bool) 1.446 +AtomImpl::EqualsUTF8(const nsACString& aString) 1.447 +{ 1.448 + return CompareUTF8toUTF16(aString, 1.449 + nsDependentString(mString, mLength)) == 0; 1.450 +} 1.451 + 1.452 +NS_IMETHODIMP 1.453 +AtomImpl::ScriptableEquals(const nsAString& aString, bool* aResult) 1.454 +{ 1.455 + *aResult = aString.Equals(nsDependentString(mString, mLength)); 1.456 + return NS_OK; 1.457 +} 1.458 + 1.459 +NS_IMETHODIMP_(bool) 1.460 +AtomImpl::IsStaticAtom() 1.461 +{ 1.462 + return IsPermanent(); 1.463 +} 1.464 + 1.465 +size_t 1.466 +AtomImpl::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.467 +{ 1.468 + return aMallocSizeOf(this) + 1.469 + nsStringBuffer::FromData(mString)-> 1.470 + SizeOfIncludingThisIfUnshared(aMallocSizeOf); 1.471 +} 1.472 + 1.473 +//---------------------------------------------------------------------- 1.474 + 1.475 +static size_t 1.476 +SizeOfAtomTableEntryExcludingThis(PLDHashEntryHdr *aHdr, 1.477 + MallocSizeOf aMallocSizeOf, 1.478 + void *aArg) 1.479 +{ 1.480 + AtomTableEntry* entry = static_cast<AtomTableEntry*>(aHdr); 1.481 + return entry->mAtom->SizeOfIncludingThis(aMallocSizeOf); 1.482 +} 1.483 + 1.484 +static size_t 1.485 +SizeOfStaticAtomTableEntryExcludingThis(const nsAString& aKey, 1.486 + nsIAtom* const& aData, 1.487 + MallocSizeOf aMallocSizeOf, 1.488 + void* aArg) 1.489 +{ 1.490 + return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 1.491 +} 1.492 + 1.493 +size_t 1.494 +NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf) { 1.495 + size_t n = 0; 1.496 + if (gAtomTable.ops) { 1.497 + n += PL_DHashTableSizeOfExcludingThis(&gAtomTable, 1.498 + SizeOfAtomTableEntryExcludingThis, 1.499 + aMallocSizeOf); 1.500 + } 1.501 + if (gStaticAtomTable) { 1.502 + n += gStaticAtomTable->SizeOfIncludingThis(SizeOfStaticAtomTableEntryExcludingThis, 1.503 + aMallocSizeOf); 1.504 + } 1.505 + return n; 1.506 +} 1.507 + 1.508 +#define ATOM_HASHTABLE_INITIAL_SIZE 4096 1.509 + 1.510 +static inline void 1.511 +EnsureTableExists() 1.512 +{ 1.513 + if (!gAtomTable.ops) { 1.514 + PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0, 1.515 + sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE); 1.516 + } 1.517 +} 1.518 + 1.519 +static inline AtomTableEntry* 1.520 +GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t& aHash) 1.521 +{ 1.522 + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 1.523 + EnsureTableExists(); 1.524 + AtomTableKey key(aString, aLength, aHash); 1.525 + AtomTableEntry* e = 1.526 + static_cast<AtomTableEntry*> 1.527 + (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD)); 1.528 + if (!e) { 1.529 + NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize); 1.530 + } 1.531 + return e; 1.532 +} 1.533 + 1.534 +static inline AtomTableEntry* 1.535 +GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t& aHash) 1.536 +{ 1.537 + MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); 1.538 + EnsureTableExists(); 1.539 + AtomTableKey key(aString, aLength, aHash); 1.540 + AtomTableEntry* e = 1.541 + static_cast<AtomTableEntry*> 1.542 + (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD)); 1.543 + if (!e) { 1.544 + NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize); 1.545 + } 1.546 + return e; 1.547 +} 1.548 + 1.549 +class CheckStaticAtomSizes 1.550 +{ 1.551 + CheckStaticAtomSizes() { 1.552 + static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) == 1.553 + sizeof(nsStringBuffer().mRefCount)) && 1.554 + (sizeof(nsFakeStringBuffer<1>().mSize) == 1.555 + sizeof(nsStringBuffer().mStorageSize)) && 1.556 + (offsetof(nsFakeStringBuffer<1>, mRefCnt) == 1.557 + offsetof(nsStringBuffer, mRefCount)) && 1.558 + (offsetof(nsFakeStringBuffer<1>, mSize) == 1.559 + offsetof(nsStringBuffer, mStorageSize)) && 1.560 + (offsetof(nsFakeStringBuffer<1>, mStringData) == 1.561 + sizeof(nsStringBuffer)), 1.562 + "mocked-up strings' representations should be compatible"); 1.563 + } 1.564 +}; 1.565 + 1.566 +nsresult 1.567 +RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount) 1.568 +{ 1.569 + // this does three things: 1.570 + // 1) wraps each static atom in a wrapper, if necessary 1.571 + // 2) initializes the address pointed to by each mBits slot 1.572 + // 3) puts the atom into the static atom table as well 1.573 + 1.574 + if (!gStaticAtomTable && !gStaticAtomTableSealed) { 1.575 + gStaticAtomTable = new nsDataHashtable<nsStringHashKey, nsIAtom*>(); 1.576 + } 1.577 + 1.578 + for (uint32_t i=0; i<aAtomCount; i++) { 1.579 + NS_ASSERTION(nsCRT::IsAscii((char16_t*)aAtoms[i].mStringBuffer->Data()), 1.580 + "Static atoms must be ASCII!"); 1.581 + 1.582 + uint32_t stringLen = 1.583 + aAtoms[i].mStringBuffer->StorageSize() / sizeof(char16_t) - 1; 1.584 + 1.585 + uint32_t hash = 0; 1.586 + AtomTableEntry *he = 1.587 + GetAtomHashEntry((char16_t*)aAtoms[i].mStringBuffer->Data(), 1.588 + stringLen, hash); 1.589 + 1.590 + if (he->mAtom) { 1.591 + // there already is an atom with this name in the table.. but we 1.592 + // still have to update mBits 1.593 + if (!he->mAtom->IsPermanent()) { 1.594 + // since we wanted to create a static atom but there is 1.595 + // already one there, we convert it to a non-refcounting 1.596 + // permanent atom 1.597 + PromoteToPermanent(he->mAtom); 1.598 + } 1.599 + 1.600 + *aAtoms[i].mAtom = he->mAtom; 1.601 + } 1.602 + else { 1.603 + AtomImpl* atom = new PermanentAtomImpl(aAtoms[i].mStringBuffer, 1.604 + stringLen, 1.605 + hash); 1.606 + he->mAtom = atom; 1.607 + *aAtoms[i].mAtom = atom; 1.608 + 1.609 + if (!gStaticAtomTableSealed) { 1.610 + gStaticAtomTable->Put(nsAtomString(atom), atom); 1.611 + } 1.612 + } 1.613 + } 1.614 + return NS_OK; 1.615 +} 1.616 + 1.617 +already_AddRefed<nsIAtom> 1.618 +NS_NewAtom(const char* aUTF8String) 1.619 +{ 1.620 + return NS_NewAtom(nsDependentCString(aUTF8String)); 1.621 +} 1.622 + 1.623 +already_AddRefed<nsIAtom> 1.624 +NS_NewAtom(const nsACString& aUTF8String) 1.625 +{ 1.626 + uint32_t hash = 0; 1.627 + AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(), 1.628 + aUTF8String.Length(), 1.629 + hash); 1.630 + 1.631 + if (he->mAtom) { 1.632 + nsCOMPtr<nsIAtom> atom = he->mAtom; 1.633 + 1.634 + return atom.forget(); 1.635 + } 1.636 + 1.637 + // This results in an extra addref/release of the nsStringBuffer. 1.638 + // Unfortunately there doesn't seem to be any APIs to avoid that. 1.639 + // Actually, now there is, sort of: ForgetSharedBuffer. 1.640 + nsString str; 1.641 + CopyUTF8toUTF16(aUTF8String, str); 1.642 + nsRefPtr<AtomImpl> atom = new AtomImpl(str, hash); 1.643 + 1.644 + he->mAtom = atom; 1.645 + 1.646 + return atom.forget(); 1.647 +} 1.648 + 1.649 +already_AddRefed<nsIAtom> 1.650 +NS_NewAtom(const char16_t* aUTF16String) 1.651 +{ 1.652 + return NS_NewAtom(nsDependentString(aUTF16String)); 1.653 +} 1.654 + 1.655 +already_AddRefed<nsIAtom> 1.656 +NS_NewAtom(const nsAString& aUTF16String) 1.657 +{ 1.658 + uint32_t hash = 0; 1.659 + AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(), 1.660 + aUTF16String.Length(), 1.661 + hash); 1.662 + 1.663 + if (he->mAtom) { 1.664 + nsCOMPtr<nsIAtom> atom = he->mAtom; 1.665 + 1.666 + return atom.forget(); 1.667 + } 1.668 + 1.669 + nsRefPtr<AtomImpl> atom = new AtomImpl(aUTF16String, hash); 1.670 + he->mAtom = atom; 1.671 + 1.672 + return atom.forget(); 1.673 +} 1.674 + 1.675 +nsIAtom* 1.676 +NS_NewPermanentAtom(const nsAString& aUTF16String) 1.677 +{ 1.678 + uint32_t hash = 0; 1.679 + AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(), 1.680 + aUTF16String.Length(), 1.681 + hash); 1.682 + 1.683 + AtomImpl* atom = he->mAtom; 1.684 + if (atom) { 1.685 + if (!atom->IsPermanent()) { 1.686 + PromoteToPermanent(atom); 1.687 + } 1.688 + } 1.689 + else { 1.690 + atom = new PermanentAtomImpl(aUTF16String, hash); 1.691 + he->mAtom = atom; 1.692 + } 1.693 + 1.694 + // No need to addref since permanent atoms aren't refcounted anyway 1.695 + return atom; 1.696 +} 1.697 + 1.698 +nsrefcnt 1.699 +NS_GetNumberOfAtoms(void) 1.700 +{ 1.701 + return gAtomTable.entryCount; 1.702 +} 1.703 + 1.704 +nsIAtom* 1.705 +NS_GetStaticAtom(const nsAString& aUTF16String) 1.706 +{ 1.707 + NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet."); 1.708 + NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet."); 1.709 + nsIAtom* atom; 1.710 + if (!gStaticAtomTable->Get(aUTF16String, &atom)) { 1.711 + atom = nullptr; 1.712 + } 1.713 + return atom; 1.714 +} 1.715 + 1.716 +void 1.717 +NS_SealStaticAtomTable() 1.718 +{ 1.719 + gStaticAtomTableSealed = true; 1.720 +}