xpcom/ds/nsAtomTable.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // vim:cindent:ts=2:et:sw=2:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/Assertions.h"
michael@0 8 #include "mozilla/Attributes.h"
michael@0 9 #include "mozilla/Compiler.h"
michael@0 10 #include "mozilla/HashFunctions.h"
michael@0 11 #include "mozilla/MemoryReporting.h"
michael@0 12 #include "mozilla/DebugOnly.h"
michael@0 13 #include "mozilla/unused.h"
michael@0 14
michael@0 15 #include "nsAtomTable.h"
michael@0 16 #include "nsStaticAtom.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include "nsCRT.h"
michael@0 19 #include "pldhash.h"
michael@0 20 #include "prenv.h"
michael@0 21 #include "nsThreadUtils.h"
michael@0 22 #include "nsDataHashtable.h"
michael@0 23 #include "nsHashKeys.h"
michael@0 24 #include "nsAutoPtr.h"
michael@0 25 #include "nsUnicharUtils.h"
michael@0 26
michael@0 27 using namespace mozilla;
michael@0 28
michael@0 29 #if defined(__clang__)
michael@0 30 # pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
michael@0 31 #elif MOZ_IS_GCC
michael@0 32 # if MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
michael@0 33 # pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
michael@0 34 # endif
michael@0 35 #endif
michael@0 36
michael@0 37 /**
michael@0 38 * The shared hash table for atom lookups.
michael@0 39 *
michael@0 40 * XXX This should be manipulated in a threadsafe way or we should make
michael@0 41 * sure it's only manipulated from the main thread. Probably the latter
michael@0 42 * is better, since the former would hurt performance.
michael@0 43 *
michael@0 44 * If |gAtomTable.ops| is 0, then the table is uninitialized.
michael@0 45 */
michael@0 46 static PLDHashTable gAtomTable;
michael@0 47
michael@0 48 /**
michael@0 49 * A hashtable of static atoms that existed at app startup. This hashtable helps
michael@0 50 * nsHtml5AtomTable.
michael@0 51 */
michael@0 52 static nsDataHashtable<nsStringHashKey, nsIAtom*>* gStaticAtomTable = 0;
michael@0 53
michael@0 54 /**
michael@0 55 * Whether it is still OK to add atoms to gStaticAtomTable.
michael@0 56 */
michael@0 57 static bool gStaticAtomTableSealed = false;
michael@0 58
michael@0 59 //----------------------------------------------------------------------
michael@0 60
michael@0 61 /**
michael@0 62 * Note that AtomImpl objects are sometimes converted into PermanentAtomImpl
michael@0 63 * objects using placement new and just overwriting the vtable pointer.
michael@0 64 */
michael@0 65
michael@0 66 class AtomImpl : public nsIAtom {
michael@0 67 public:
michael@0 68 AtomImpl(const nsAString& aString, uint32_t aHash);
michael@0 69
michael@0 70 // This is currently only used during startup when creating a permanent atom
michael@0 71 // from NS_RegisterStaticAtoms
michael@0 72 AtomImpl(nsStringBuffer* aData, uint32_t aLength, uint32_t aHash);
michael@0 73
michael@0 74 protected:
michael@0 75 // This is only intended to be used when a normal atom is turned into a
michael@0 76 // permanent one.
michael@0 77 AtomImpl() {
michael@0 78 // We can't really assert that mString is a valid nsStringBuffer string,
michael@0 79 // so do the best we can do and check for some consistencies.
michael@0 80 NS_ASSERTION((mLength + 1) * sizeof(char16_t) <=
michael@0 81 nsStringBuffer::FromData(mString)->StorageSize() &&
michael@0 82 mString[mLength] == 0,
michael@0 83 "Not initialized atom");
michael@0 84 }
michael@0 85
michael@0 86 // We don't need a virtual destructor here because PermanentAtomImpl
michael@0 87 // deletions aren't handled through Release().
michael@0 88 ~AtomImpl();
michael@0 89
michael@0 90 public:
michael@0 91 NS_DECL_ISUPPORTS
michael@0 92 NS_DECL_NSIATOM
michael@0 93
michael@0 94 enum { REFCNT_PERMANENT_SENTINEL = UINT32_MAX };
michael@0 95
michael@0 96 virtual bool IsPermanent();
michael@0 97
michael@0 98 // We can't use the virtual function in the base class destructor.
michael@0 99 bool IsPermanentInDestructor() {
michael@0 100 return mRefCnt == REFCNT_PERMANENT_SENTINEL;
michael@0 101 }
michael@0 102
michael@0 103 // for |#ifdef NS_BUILD_REFCNT_LOGGING| access to reference count
michael@0 104 nsrefcnt GetRefCount() { return mRefCnt; }
michael@0 105
michael@0 106 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 107 };
michael@0 108
michael@0 109 /**
michael@0 110 * A non-refcounted implementation of nsIAtom.
michael@0 111 */
michael@0 112
michael@0 113 class PermanentAtomImpl MOZ_FINAL : public AtomImpl {
michael@0 114 public:
michael@0 115 PermanentAtomImpl(const nsAString& aString, PLDHashNumber aKeyHash)
michael@0 116 : AtomImpl(aString, aKeyHash)
michael@0 117 {}
michael@0 118 PermanentAtomImpl(nsStringBuffer* aData, uint32_t aLength,
michael@0 119 PLDHashNumber aKeyHash)
michael@0 120 : AtomImpl(aData, aLength, aKeyHash)
michael@0 121 {}
michael@0 122 PermanentAtomImpl()
michael@0 123 {}
michael@0 124
michael@0 125 ~PermanentAtomImpl();
michael@0 126 NS_IMETHOD_(MozExternalRefCountType) AddRef();
michael@0 127 NS_IMETHOD_(MozExternalRefCountType) Release();
michael@0 128
michael@0 129 virtual bool IsPermanent();
michael@0 130
michael@0 131 // SizeOfIncludingThis() isn't needed -- the one inherited from AtomImpl is
michael@0 132 // good enough, because PermanentAtomImpl doesn't add any new data members.
michael@0 133
michael@0 134 void* operator new(size_t size, AtomImpl* aAtom) CPP_THROW_NEW;
michael@0 135 void* operator new(size_t size) CPP_THROW_NEW
michael@0 136 {
michael@0 137 return ::operator new(size);
michael@0 138 }
michael@0 139 };
michael@0 140
michael@0 141 //----------------------------------------------------------------------
michael@0 142
michael@0 143 struct AtomTableEntry : public PLDHashEntryHdr {
michael@0 144 AtomImpl* mAtom;
michael@0 145 };
michael@0 146
michael@0 147 struct AtomTableKey
michael@0 148 {
michael@0 149 AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
michael@0 150 /*inout*/ uint32_t& aHash)
michael@0 151 : mUTF16String(aUTF16String),
michael@0 152 mUTF8String(nullptr),
michael@0 153 mLength(aLength)
michael@0 154 {
michael@0 155 if (aHash) {
michael@0 156 MOZ_ASSERT(aHash == HashString(mUTF16String, mLength));
michael@0 157 mHash = aHash;
michael@0 158 } else {
michael@0 159 UpdateHashKey();
michael@0 160 aHash = mHash;
michael@0 161 }
michael@0 162 }
michael@0 163
michael@0 164 AtomTableKey(const char* aUTF8String, uint32_t aLength,
michael@0 165 /*inout*/ uint32_t& aHash)
michael@0 166 : mUTF16String(nullptr),
michael@0 167 mUTF8String(aUTF8String),
michael@0 168 mLength(aLength)
michael@0 169 {
michael@0 170 if (aHash) {
michael@0 171 mozilla::DebugOnly<bool> err;
michael@0 172 MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err));
michael@0 173 mHash = aHash;
michael@0 174 } else {
michael@0 175 UpdateHashKey();
michael@0 176 aHash = mHash;
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 const char16_t* mUTF16String;
michael@0 181 const char* mUTF8String;
michael@0 182 uint32_t mLength;
michael@0 183 uint32_t mHash;
michael@0 184
michael@0 185 void UpdateHashKey()
michael@0 186 {
michael@0 187 if (mUTF8String) {
michael@0 188 bool err;
michael@0 189 mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
michael@0 190 if (err) {
michael@0 191 mUTF8String = nullptr;
michael@0 192 mLength = 0;
michael@0 193 mHash = 0;
michael@0 194 }
michael@0 195 } else {
michael@0 196 mHash = HashString(mUTF16String, mLength);
michael@0 197 }
michael@0 198 }
michael@0 199 };
michael@0 200
michael@0 201 static PLDHashNumber
michael@0 202 AtomTableGetHash(PLDHashTable *table, const void *key)
michael@0 203 {
michael@0 204 const AtomTableKey *k = static_cast<const AtomTableKey*>(key);
michael@0 205 return k->mHash;
michael@0 206 }
michael@0 207
michael@0 208 static bool
michael@0 209 AtomTableMatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry,
michael@0 210 const void *key)
michael@0 211 {
michael@0 212 const AtomTableEntry *he = static_cast<const AtomTableEntry*>(entry);
michael@0 213 const AtomTableKey *k = static_cast<const AtomTableKey*>(key);
michael@0 214
michael@0 215 if (k->mUTF8String) {
michael@0 216 return
michael@0 217 CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
michael@0 218 k->mUTF8String + k->mLength),
michael@0 219 nsDependentAtomString(he->mAtom)) == 0;
michael@0 220 }
michael@0 221
michael@0 222 uint32_t length = he->mAtom->GetLength();
michael@0 223 if (length != k->mLength) {
michael@0 224 return false;
michael@0 225 }
michael@0 226
michael@0 227 return memcmp(he->mAtom->GetUTF16String(),
michael@0 228 k->mUTF16String, length * sizeof(char16_t)) == 0;
michael@0 229 }
michael@0 230
michael@0 231 static void
michael@0 232 AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
michael@0 233 {
michael@0 234 // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and
michael@0 235 // they then remove themselves from the table. In other words, they
michael@0 236 // are owned by the callers who own references to them.
michael@0 237 // |PermanentAtomImpl| permanent atoms ignore their refcount and are
michael@0 238 // deleted when they are removed from the table at table destruction.
michael@0 239 // In other words, they are owned by the atom table.
michael@0 240
michael@0 241 AtomImpl *atom = static_cast<AtomTableEntry*>(entry)->mAtom;
michael@0 242 if (atom->IsPermanent()) {
michael@0 243 // Note that the cast here is important since AtomImpls doesn't have a
michael@0 244 // virtual dtor.
michael@0 245 delete static_cast<PermanentAtomImpl*>(atom);
michael@0 246 }
michael@0 247 }
michael@0 248
michael@0 249 static bool
michael@0 250 AtomTableInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
michael@0 251 const void *key)
michael@0 252 {
michael@0 253 static_cast<AtomTableEntry*>(entry)->mAtom = nullptr;
michael@0 254
michael@0 255 return true;
michael@0 256 }
michael@0 257
michael@0 258
michael@0 259 static const PLDHashTableOps AtomTableOps = {
michael@0 260 PL_DHashAllocTable,
michael@0 261 PL_DHashFreeTable,
michael@0 262 AtomTableGetHash,
michael@0 263 AtomTableMatchKey,
michael@0 264 PL_DHashMoveEntryStub,
michael@0 265 AtomTableClearEntry,
michael@0 266 PL_DHashFinalizeStub,
michael@0 267 AtomTableInitEntry
michael@0 268 };
michael@0 269
michael@0 270
michael@0 271 #ifdef DEBUG
michael@0 272 static PLDHashOperator
michael@0 273 DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he,
michael@0 274 uint32_t index, void *arg)
michael@0 275 {
michael@0 276 AtomTableEntry *entry = static_cast<AtomTableEntry*>(he);
michael@0 277
michael@0 278 AtomImpl* atom = entry->mAtom;
michael@0 279 if (!atom->IsPermanent()) {
michael@0 280 ++*static_cast<uint32_t*>(arg);
michael@0 281 nsAutoCString str;
michael@0 282 atom->ToUTF8String(str);
michael@0 283 fputs(str.get(), stdout);
michael@0 284 fputs("\n", stdout);
michael@0 285 }
michael@0 286 return PL_DHASH_NEXT;
michael@0 287 }
michael@0 288 #endif
michael@0 289
michael@0 290 static inline
michael@0 291 void PromoteToPermanent(AtomImpl* aAtom)
michael@0 292 {
michael@0 293 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 294 {
michael@0 295 nsrefcnt refcount = aAtom->GetRefCount();
michael@0 296 do {
michael@0 297 NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl");
michael@0 298 } while (refcount);
michael@0 299 }
michael@0 300 #endif
michael@0 301 aAtom = new (aAtom) PermanentAtomImpl();
michael@0 302 }
michael@0 303
michael@0 304 void
michael@0 305 NS_PurgeAtomTable()
michael@0 306 {
michael@0 307 delete gStaticAtomTable;
michael@0 308
michael@0 309 if (gAtomTable.ops) {
michael@0 310 #ifdef DEBUG
michael@0 311 const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
michael@0 312 if (dumpAtomLeaks && *dumpAtomLeaks) {
michael@0 313 uint32_t leaked = 0;
michael@0 314 printf("*** %d atoms still exist (including permanent):\n",
michael@0 315 gAtomTable.entryCount);
michael@0 316 PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
michael@0 317 printf("*** %u non-permanent atoms leaked\n", leaked);
michael@0 318 }
michael@0 319 #endif
michael@0 320 PL_DHashTableFinish(&gAtomTable);
michael@0 321 gAtomTable.entryCount = 0;
michael@0 322 gAtomTable.ops = nullptr;
michael@0 323 }
michael@0 324 }
michael@0 325
michael@0 326 AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash)
michael@0 327 {
michael@0 328 mLength = aString.Length();
michael@0 329 nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
michael@0 330 if (buf) {
michael@0 331 mString = static_cast<char16_t*>(buf->Data());
michael@0 332 } else {
michael@0 333 buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t));
michael@0 334 mString = static_cast<char16_t*>(buf->Data());
michael@0 335 CopyUnicodeTo(aString, 0, mString, mLength);
michael@0 336 mString[mLength] = char16_t(0);
michael@0 337 }
michael@0 338
michael@0 339 mHash = aHash;
michael@0 340 MOZ_ASSERT(mHash == HashString(mString, mLength));
michael@0 341
michael@0 342 NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
michael@0 343 NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t),
michael@0 344 "enough storage");
michael@0 345 NS_ASSERTION(Equals(aString), "correct data");
michael@0 346
michael@0 347 // Take ownership of buffer
michael@0 348 mozilla::unused << buf.forget();
michael@0 349 }
michael@0 350
michael@0 351 AtomImpl::AtomImpl(nsStringBuffer* aStringBuffer, uint32_t aLength,
michael@0 352 uint32_t aHash)
michael@0 353 {
michael@0 354 mLength = aLength;
michael@0 355 mString = static_cast<char16_t*>(aStringBuffer->Data());
michael@0 356 // Technically we could currently avoid doing this addref by instead making
michael@0 357 // the static atom buffers have an initial refcount of 2.
michael@0 358 aStringBuffer->AddRef();
michael@0 359
michael@0 360 mHash = aHash;
michael@0 361 MOZ_ASSERT(mHash == HashString(mString, mLength));
michael@0 362
michael@0 363 NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
michael@0 364 NS_ASSERTION(aStringBuffer &&
michael@0 365 aStringBuffer->StorageSize() == (mLength+1) * sizeof(char16_t),
michael@0 366 "correct storage");
michael@0 367 }
michael@0 368
michael@0 369 AtomImpl::~AtomImpl()
michael@0 370 {
michael@0 371 NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
michael@0 372 // Permanent atoms are removed from the hashtable at shutdown, and we
michael@0 373 // don't want to remove them twice. See comment above in
michael@0 374 // |AtomTableClearEntry|.
michael@0 375 if (!IsPermanentInDestructor()) {
michael@0 376 AtomTableKey key(mString, mLength, mHash);
michael@0 377 PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE);
michael@0 378 if (gAtomTable.entryCount == 0) {
michael@0 379 PL_DHashTableFinish(&gAtomTable);
michael@0 380 NS_ASSERTION(gAtomTable.entryCount == 0,
michael@0 381 "PL_DHashTableFinish changed the entry count");
michael@0 382 }
michael@0 383 }
michael@0 384
michael@0 385 nsStringBuffer::FromData(mString)->Release();
michael@0 386 }
michael@0 387
michael@0 388 NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom)
michael@0 389
michael@0 390 PermanentAtomImpl::~PermanentAtomImpl()
michael@0 391 {
michael@0 392 // So we can tell if we were permanent while running the base class dtor.
michael@0 393 mRefCnt = REFCNT_PERMANENT_SENTINEL;
michael@0 394 }
michael@0 395
michael@0 396 NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::AddRef()
michael@0 397 {
michael@0 398 MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
michael@0 399 return 2;
michael@0 400 }
michael@0 401
michael@0 402 NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::Release()
michael@0 403 {
michael@0 404 MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
michael@0 405 return 1;
michael@0 406 }
michael@0 407
michael@0 408 /* virtual */ bool
michael@0 409 AtomImpl::IsPermanent()
michael@0 410 {
michael@0 411 return false;
michael@0 412 }
michael@0 413
michael@0 414 /* virtual */ bool
michael@0 415 PermanentAtomImpl::IsPermanent()
michael@0 416 {
michael@0 417 return true;
michael@0 418 }
michael@0 419
michael@0 420 void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW {
michael@0 421 MOZ_ASSERT(!aAtom->IsPermanent(),
michael@0 422 "converting atom that's already permanent");
michael@0 423
michael@0 424 // Just let the constructor overwrite the vtable pointer.
michael@0 425 return aAtom;
michael@0 426 }
michael@0 427
michael@0 428 NS_IMETHODIMP
michael@0 429 AtomImpl::ScriptableToString(nsAString& aBuf)
michael@0 430 {
michael@0 431 nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
michael@0 432 return NS_OK;
michael@0 433 }
michael@0 434
michael@0 435 NS_IMETHODIMP
michael@0 436 AtomImpl::ToUTF8String(nsACString& aBuf)
michael@0 437 {
michael@0 438 CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
michael@0 439 return NS_OK;
michael@0 440 }
michael@0 441
michael@0 442 NS_IMETHODIMP_(bool)
michael@0 443 AtomImpl::EqualsUTF8(const nsACString& aString)
michael@0 444 {
michael@0 445 return CompareUTF8toUTF16(aString,
michael@0 446 nsDependentString(mString, mLength)) == 0;
michael@0 447 }
michael@0 448
michael@0 449 NS_IMETHODIMP
michael@0 450 AtomImpl::ScriptableEquals(const nsAString& aString, bool* aResult)
michael@0 451 {
michael@0 452 *aResult = aString.Equals(nsDependentString(mString, mLength));
michael@0 453 return NS_OK;
michael@0 454 }
michael@0 455
michael@0 456 NS_IMETHODIMP_(bool)
michael@0 457 AtomImpl::IsStaticAtom()
michael@0 458 {
michael@0 459 return IsPermanent();
michael@0 460 }
michael@0 461
michael@0 462 size_t
michael@0 463 AtomImpl::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 464 {
michael@0 465 return aMallocSizeOf(this) +
michael@0 466 nsStringBuffer::FromData(mString)->
michael@0 467 SizeOfIncludingThisIfUnshared(aMallocSizeOf);
michael@0 468 }
michael@0 469
michael@0 470 //----------------------------------------------------------------------
michael@0 471
michael@0 472 static size_t
michael@0 473 SizeOfAtomTableEntryExcludingThis(PLDHashEntryHdr *aHdr,
michael@0 474 MallocSizeOf aMallocSizeOf,
michael@0 475 void *aArg)
michael@0 476 {
michael@0 477 AtomTableEntry* entry = static_cast<AtomTableEntry*>(aHdr);
michael@0 478 return entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
michael@0 479 }
michael@0 480
michael@0 481 static size_t
michael@0 482 SizeOfStaticAtomTableEntryExcludingThis(const nsAString& aKey,
michael@0 483 nsIAtom* const& aData,
michael@0 484 MallocSizeOf aMallocSizeOf,
michael@0 485 void* aArg)
michael@0 486 {
michael@0 487 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
michael@0 488 }
michael@0 489
michael@0 490 size_t
michael@0 491 NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf) {
michael@0 492 size_t n = 0;
michael@0 493 if (gAtomTable.ops) {
michael@0 494 n += PL_DHashTableSizeOfExcludingThis(&gAtomTable,
michael@0 495 SizeOfAtomTableEntryExcludingThis,
michael@0 496 aMallocSizeOf);
michael@0 497 }
michael@0 498 if (gStaticAtomTable) {
michael@0 499 n += gStaticAtomTable->SizeOfIncludingThis(SizeOfStaticAtomTableEntryExcludingThis,
michael@0 500 aMallocSizeOf);
michael@0 501 }
michael@0 502 return n;
michael@0 503 }
michael@0 504
michael@0 505 #define ATOM_HASHTABLE_INITIAL_SIZE 4096
michael@0 506
michael@0 507 static inline void
michael@0 508 EnsureTableExists()
michael@0 509 {
michael@0 510 if (!gAtomTable.ops) {
michael@0 511 PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
michael@0 512 sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE);
michael@0 513 }
michael@0 514 }
michael@0 515
michael@0 516 static inline AtomTableEntry*
michael@0 517 GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t& aHash)
michael@0 518 {
michael@0 519 MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
michael@0 520 EnsureTableExists();
michael@0 521 AtomTableKey key(aString, aLength, aHash);
michael@0 522 AtomTableEntry* e =
michael@0 523 static_cast<AtomTableEntry*>
michael@0 524 (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
michael@0 525 if (!e) {
michael@0 526 NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
michael@0 527 }
michael@0 528 return e;
michael@0 529 }
michael@0 530
michael@0 531 static inline AtomTableEntry*
michael@0 532 GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t& aHash)
michael@0 533 {
michael@0 534 MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
michael@0 535 EnsureTableExists();
michael@0 536 AtomTableKey key(aString, aLength, aHash);
michael@0 537 AtomTableEntry* e =
michael@0 538 static_cast<AtomTableEntry*>
michael@0 539 (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
michael@0 540 if (!e) {
michael@0 541 NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
michael@0 542 }
michael@0 543 return e;
michael@0 544 }
michael@0 545
michael@0 546 class CheckStaticAtomSizes
michael@0 547 {
michael@0 548 CheckStaticAtomSizes() {
michael@0 549 static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
michael@0 550 sizeof(nsStringBuffer().mRefCount)) &&
michael@0 551 (sizeof(nsFakeStringBuffer<1>().mSize) ==
michael@0 552 sizeof(nsStringBuffer().mStorageSize)) &&
michael@0 553 (offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
michael@0 554 offsetof(nsStringBuffer, mRefCount)) &&
michael@0 555 (offsetof(nsFakeStringBuffer<1>, mSize) ==
michael@0 556 offsetof(nsStringBuffer, mStorageSize)) &&
michael@0 557 (offsetof(nsFakeStringBuffer<1>, mStringData) ==
michael@0 558 sizeof(nsStringBuffer)),
michael@0 559 "mocked-up strings' representations should be compatible");
michael@0 560 }
michael@0 561 };
michael@0 562
michael@0 563 nsresult
michael@0 564 RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
michael@0 565 {
michael@0 566 // this does three things:
michael@0 567 // 1) wraps each static atom in a wrapper, if necessary
michael@0 568 // 2) initializes the address pointed to by each mBits slot
michael@0 569 // 3) puts the atom into the static atom table as well
michael@0 570
michael@0 571 if (!gStaticAtomTable && !gStaticAtomTableSealed) {
michael@0 572 gStaticAtomTable = new nsDataHashtable<nsStringHashKey, nsIAtom*>();
michael@0 573 }
michael@0 574
michael@0 575 for (uint32_t i=0; i<aAtomCount; i++) {
michael@0 576 NS_ASSERTION(nsCRT::IsAscii((char16_t*)aAtoms[i].mStringBuffer->Data()),
michael@0 577 "Static atoms must be ASCII!");
michael@0 578
michael@0 579 uint32_t stringLen =
michael@0 580 aAtoms[i].mStringBuffer->StorageSize() / sizeof(char16_t) - 1;
michael@0 581
michael@0 582 uint32_t hash = 0;
michael@0 583 AtomTableEntry *he =
michael@0 584 GetAtomHashEntry((char16_t*)aAtoms[i].mStringBuffer->Data(),
michael@0 585 stringLen, hash);
michael@0 586
michael@0 587 if (he->mAtom) {
michael@0 588 // there already is an atom with this name in the table.. but we
michael@0 589 // still have to update mBits
michael@0 590 if (!he->mAtom->IsPermanent()) {
michael@0 591 // since we wanted to create a static atom but there is
michael@0 592 // already one there, we convert it to a non-refcounting
michael@0 593 // permanent atom
michael@0 594 PromoteToPermanent(he->mAtom);
michael@0 595 }
michael@0 596
michael@0 597 *aAtoms[i].mAtom = he->mAtom;
michael@0 598 }
michael@0 599 else {
michael@0 600 AtomImpl* atom = new PermanentAtomImpl(aAtoms[i].mStringBuffer,
michael@0 601 stringLen,
michael@0 602 hash);
michael@0 603 he->mAtom = atom;
michael@0 604 *aAtoms[i].mAtom = atom;
michael@0 605
michael@0 606 if (!gStaticAtomTableSealed) {
michael@0 607 gStaticAtomTable->Put(nsAtomString(atom), atom);
michael@0 608 }
michael@0 609 }
michael@0 610 }
michael@0 611 return NS_OK;
michael@0 612 }
michael@0 613
michael@0 614 already_AddRefed<nsIAtom>
michael@0 615 NS_NewAtom(const char* aUTF8String)
michael@0 616 {
michael@0 617 return NS_NewAtom(nsDependentCString(aUTF8String));
michael@0 618 }
michael@0 619
michael@0 620 already_AddRefed<nsIAtom>
michael@0 621 NS_NewAtom(const nsACString& aUTF8String)
michael@0 622 {
michael@0 623 uint32_t hash = 0;
michael@0 624 AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(),
michael@0 625 aUTF8String.Length(),
michael@0 626 hash);
michael@0 627
michael@0 628 if (he->mAtom) {
michael@0 629 nsCOMPtr<nsIAtom> atom = he->mAtom;
michael@0 630
michael@0 631 return atom.forget();
michael@0 632 }
michael@0 633
michael@0 634 // This results in an extra addref/release of the nsStringBuffer.
michael@0 635 // Unfortunately there doesn't seem to be any APIs to avoid that.
michael@0 636 // Actually, now there is, sort of: ForgetSharedBuffer.
michael@0 637 nsString str;
michael@0 638 CopyUTF8toUTF16(aUTF8String, str);
michael@0 639 nsRefPtr<AtomImpl> atom = new AtomImpl(str, hash);
michael@0 640
michael@0 641 he->mAtom = atom;
michael@0 642
michael@0 643 return atom.forget();
michael@0 644 }
michael@0 645
michael@0 646 already_AddRefed<nsIAtom>
michael@0 647 NS_NewAtom(const char16_t* aUTF16String)
michael@0 648 {
michael@0 649 return NS_NewAtom(nsDependentString(aUTF16String));
michael@0 650 }
michael@0 651
michael@0 652 already_AddRefed<nsIAtom>
michael@0 653 NS_NewAtom(const nsAString& aUTF16String)
michael@0 654 {
michael@0 655 uint32_t hash = 0;
michael@0 656 AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(),
michael@0 657 aUTF16String.Length(),
michael@0 658 hash);
michael@0 659
michael@0 660 if (he->mAtom) {
michael@0 661 nsCOMPtr<nsIAtom> atom = he->mAtom;
michael@0 662
michael@0 663 return atom.forget();
michael@0 664 }
michael@0 665
michael@0 666 nsRefPtr<AtomImpl> atom = new AtomImpl(aUTF16String, hash);
michael@0 667 he->mAtom = atom;
michael@0 668
michael@0 669 return atom.forget();
michael@0 670 }
michael@0 671
michael@0 672 nsIAtom*
michael@0 673 NS_NewPermanentAtom(const nsAString& aUTF16String)
michael@0 674 {
michael@0 675 uint32_t hash = 0;
michael@0 676 AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(),
michael@0 677 aUTF16String.Length(),
michael@0 678 hash);
michael@0 679
michael@0 680 AtomImpl* atom = he->mAtom;
michael@0 681 if (atom) {
michael@0 682 if (!atom->IsPermanent()) {
michael@0 683 PromoteToPermanent(atom);
michael@0 684 }
michael@0 685 }
michael@0 686 else {
michael@0 687 atom = new PermanentAtomImpl(aUTF16String, hash);
michael@0 688 he->mAtom = atom;
michael@0 689 }
michael@0 690
michael@0 691 // No need to addref since permanent atoms aren't refcounted anyway
michael@0 692 return atom;
michael@0 693 }
michael@0 694
michael@0 695 nsrefcnt
michael@0 696 NS_GetNumberOfAtoms(void)
michael@0 697 {
michael@0 698 return gAtomTable.entryCount;
michael@0 699 }
michael@0 700
michael@0 701 nsIAtom*
michael@0 702 NS_GetStaticAtom(const nsAString& aUTF16String)
michael@0 703 {
michael@0 704 NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
michael@0 705 NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
michael@0 706 nsIAtom* atom;
michael@0 707 if (!gStaticAtomTable->Get(aUTF16String, &atom)) {
michael@0 708 atom = nullptr;
michael@0 709 }
michael@0 710 return atom;
michael@0 711 }
michael@0 712
michael@0 713 void
michael@0 714 NS_SealStaticAtomTable()
michael@0 715 {
michael@0 716 gStaticAtomTableSealed = true;
michael@0 717 }

mercurial