Bug 1825336 - Make toolkit/components/url-classifier/ buildable outside of a unified...
[gecko.git] / xpcom / ds / nsAtomTable.cpp
blob56618cd46c5a44e5930118a2e54c924793ae3e43
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Assertions.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/HashFunctions.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/MruCache.h"
12 #include "mozilla/Mutex.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/Sprintf.h"
15 #include "mozilla/TextUtils.h"
16 #include "mozilla/Unused.h"
18 #include "nsAtom.h"
19 #include "nsAtomTable.h"
20 #include "nsCRT.h"
21 #include "nsGkAtoms.h"
22 #include "nsHashKeys.h"
23 #include "nsPrintfCString.h"
24 #include "nsString.h"
25 #include "nsThreadUtils.h"
26 #include "nsUnicharUtils.h"
27 #include "PLDHashTable.h"
28 #include "prenv.h"
30 // There are two kinds of atoms handled by this module.
32 // - Dynamic: the atom itself is heap allocated, as is the char buffer it
33 // points to. |gAtomTable| holds weak references to dynamic atoms. When the
34 // refcount of a dynamic atom drops to zero, we increment a static counter.
35 // When that counter reaches a certain threshold, we iterate over the atom
36 // table, removing and deleting dynamic atoms with refcount zero. This allows
37 // us to avoid acquiring the atom table lock during normal refcounting.
39 // - Static: both the atom and its chars are statically allocated and
40 // immutable, so it ignores all AddRef/Release calls.
42 // Note that gAtomTable is used on multiple threads, and has internal
43 // synchronization.
45 using namespace mozilla;
47 //----------------------------------------------------------------------
49 enum class GCKind {
50 RegularOperation,
51 Shutdown,
54 //----------------------------------------------------------------------
56 // gUnusedAtomCount is incremented when an atom loses its last reference
57 // (and thus turned into unused state), and decremented when an unused
58 // atom gets a reference again. The atom table relies on this value to
59 // schedule GC. This value can temporarily go below zero when multiple
60 // threads are operating the same atom, so it has to be signed so that
61 // we wouldn't use overflow value for comparison.
62 // See nsAtom::AddRef() and nsAtom::Release().
63 // This atomic can be accessed during the GC and other places where recorded
64 // events are not allowed, so its value is not preserved when recording or
65 // replaying.
66 Atomic<int32_t, ReleaseAcquire> nsDynamicAtom::gUnusedAtomCount;
68 nsDynamicAtom::nsDynamicAtom(const nsAString& aString, uint32_t aHash,
69 bool aIsAsciiLowercase)
70 : nsAtom(aString, aHash, aIsAsciiLowercase), mRefCnt(1) {}
72 // Returns true if ToLowercaseASCII would return the string unchanged.
73 static bool IsAsciiLowercase(const char16_t* aString, const uint32_t aLength) {
74 for (uint32_t i = 0; i < aLength; ++i) {
75 if (IS_ASCII_UPPER(aString[i])) {
76 return false;
80 return true;
83 nsDynamicAtom* nsDynamicAtom::Create(const nsAString& aString, uint32_t aHash) {
84 // We tack the chars onto the end of the nsDynamicAtom object.
85 size_t numCharBytes = (aString.Length() + 1) * sizeof(char16_t);
86 size_t numTotalBytes = sizeof(nsDynamicAtom) + numCharBytes;
88 bool isAsciiLower = ::IsAsciiLowercase(aString.Data(), aString.Length());
90 nsDynamicAtom* atom = (nsDynamicAtom*)moz_xmalloc(numTotalBytes);
91 new (atom) nsDynamicAtom(aString, aHash, isAsciiLower);
92 memcpy(const_cast<char16_t*>(atom->String()),
93 PromiseFlatString(aString).get(), numCharBytes);
95 MOZ_ASSERT(atom->String()[atom->GetLength()] == char16_t(0));
96 MOZ_ASSERT(atom->Equals(aString));
97 MOZ_ASSERT(atom->mHash == HashString(atom->String(), atom->GetLength()));
98 MOZ_ASSERT(atom->mIsAsciiLowercase == isAsciiLower);
100 return atom;
103 void nsDynamicAtom::Destroy(nsDynamicAtom* aAtom) {
104 aAtom->~nsDynamicAtom();
105 free(aAtom);
108 void nsAtom::ToString(nsAString& aString) const {
109 // See the comment on |mString|'s declaration.
110 if (IsStatic()) {
111 // AssignLiteral() lets us assign without copying. This isn't a string
112 // literal, but it's a static atom and thus has an unbounded lifetime,
113 // which is what's important.
114 aString.AssignLiteral(AsStatic()->String(), mLength);
115 } else {
116 aString.Assign(AsDynamic()->String(), mLength);
120 void nsAtom::ToUTF8String(nsACString& aBuf) const {
121 CopyUTF16toUTF8(nsDependentString(GetUTF16String(), mLength), aBuf);
124 void nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
125 AtomsSizes& aSizes) const {
126 // Static atoms are in static memory, and so are not measured here.
127 if (IsDynamic()) {
128 aSizes.mDynamicAtoms += aMallocSizeOf(this);
132 char16ptr_t nsAtom::GetUTF16String() const {
133 return IsStatic() ? AsStatic()->String() : AsDynamic()->String();
136 //----------------------------------------------------------------------
138 struct AtomTableKey {
139 explicit AtomTableKey(const nsStaticAtom* aAtom)
140 : mUTF16String(aAtom->String()),
141 mUTF8String(nullptr),
142 mLength(aAtom->GetLength()),
143 mHash(aAtom->hash()) {
144 MOZ_ASSERT(HashString(mUTF16String, mLength) == mHash);
147 AtomTableKey(const char16_t* aUTF16String, uint32_t aLength)
148 : mUTF16String(aUTF16String), mUTF8String(nullptr), mLength(aLength) {
149 mHash = HashString(mUTF16String, mLength);
152 AtomTableKey(const char* aUTF8String, uint32_t aLength, bool* aErr)
153 : mUTF16String(nullptr), mUTF8String(aUTF8String), mLength(aLength) {
154 mHash = HashUTF8AsUTF16(mUTF8String, mLength, aErr);
157 const char16_t* mUTF16String;
158 const char* mUTF8String;
159 uint32_t mLength;
160 uint32_t mHash;
163 struct AtomTableEntry : public PLDHashEntryHdr {
164 // These references are either to dynamic atoms, in which case they are
165 // non-owning, or they are to static atoms, which aren't really refcounted.
166 // See the comment at the top of this file for more details.
167 nsAtom* MOZ_NON_OWNING_REF mAtom;
170 struct AtomCache : public MruCache<AtomTableKey, nsAtom*, AtomCache> {
171 static HashNumber Hash(const AtomTableKey& aKey) { return aKey.mHash; }
172 static bool Match(const AtomTableKey& aKey, const nsAtom* aVal) {
173 MOZ_ASSERT(aKey.mUTF16String);
174 return aVal->Equals(aKey.mUTF16String, aKey.mLength);
178 static AtomCache sRecentlyUsedMainThreadAtoms;
180 // In order to reduce locking contention for concurrent atomization, we segment
181 // the atom table into N subtables, each with a separate lock. If the hash
182 // values we use to select the subtable are evenly distributed, this reduces the
183 // probability of contention by a factor of N. See bug 1440824.
185 // NB: This is somewhat similar to the technique used by Java's
186 // ConcurrentHashTable.
187 class nsAtomSubTable {
188 friend class nsAtomTable;
189 Mutex mLock;
190 PLDHashTable mTable;
191 nsAtomSubTable();
192 void GCLocked(GCKind aKind) MOZ_REQUIRES(mLock);
193 void AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
194 AtomsSizes& aSizes) MOZ_REQUIRES(mLock);
196 AtomTableEntry* Search(AtomTableKey& aKey) const MOZ_REQUIRES(mLock) {
197 mLock.AssertCurrentThreadOwns();
198 return static_cast<AtomTableEntry*>(mTable.Search(&aKey));
201 AtomTableEntry* Add(AtomTableKey& aKey) MOZ_REQUIRES(mLock) {
202 mLock.AssertCurrentThreadOwns();
203 return static_cast<AtomTableEntry*>(mTable.Add(&aKey)); // Infallible
207 // The outer atom table, which coordinates access to the inner array of
208 // subtables.
209 class nsAtomTable {
210 public:
211 nsAtomSubTable& SelectSubTable(AtomTableKey& aKey);
212 void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes);
213 void GC(GCKind aKind);
214 already_AddRefed<nsAtom> Atomize(const nsAString& aUTF16String);
215 already_AddRefed<nsAtom> Atomize(const nsACString& aUTF8String);
216 already_AddRefed<nsAtom> AtomizeMainThread(const nsAString& aUTF16String);
217 nsStaticAtom* GetStaticAtom(const nsAString& aUTF16String);
218 void RegisterStaticAtoms(const nsStaticAtom* aAtoms, size_t aAtomsLen);
220 // The result of this function may be imprecise if other threads are operating
221 // on atoms concurrently. It's also slow, since it triggers a GC before
222 // counting.
223 size_t RacySlowCount();
225 // This hash table op is a static member of this class so that it can take
226 // advantage of |friend| declarations.
227 static void AtomTableClearEntry(PLDHashTable* aTable,
228 PLDHashEntryHdr* aEntry);
230 // We achieve measurable reduction in locking contention in parallel CSS
231 // parsing by increasing the number of subtables up to 128. This has been
232 // measured to have neglible impact on the performance of initialization, GC,
233 // and shutdown.
235 // Another important consideration is memory, since we're adding fixed
236 // overhead per content process, which we try to avoid. Measuring a
237 // mostly-empty page [1] with various numbers of subtables, we get the
238 // following deep sizes for the atom table:
239 // 1 subtable: 278K
240 // 8 subtables: 279K
241 // 16 subtables: 282K
242 // 64 subtables: 286K
243 // 128 subtables: 290K
245 // So 128 subtables costs us 12K relative to a single table, and 4K relative
246 // to 64 subtables. Conversely, measuring parallel (6 thread) CSS parsing on
247 // tp6-facebook, a single table provides ~150ms of locking overhead per
248 // thread, 64 subtables provides ~2-3ms of overhead, and 128 subtables
249 // provides <1ms. And so while either 64 or 128 subtables would probably be
250 // acceptable, achieving a measurable reduction in contention for 4k of fixed
251 // memory overhead is probably worth it.
253 // [1] The numbers will look different for content processes with complex
254 // pages loaded, but in those cases the actual atoms will dominate memory
255 // usage and the overhead of extra tables will be negligible. We're mostly
256 // interested in the fixed cost for nearly-empty content processes.
257 const static size_t kNumSubTables = 128; // Must be power of two.
259 private:
260 nsAtomSubTable mSubTables[kNumSubTables];
263 // Static singleton instance for the atom table.
264 static nsAtomTable* gAtomTable;
266 static PLDHashNumber AtomTableGetHash(const void* aKey) {
267 const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
268 return k->mHash;
271 static bool AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey) {
272 const AtomTableEntry* he = static_cast<const AtomTableEntry*>(aEntry);
273 const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
275 if (k->mUTF8String) {
276 bool err = false;
277 return (CompareUTF8toUTF16(nsDependentCSubstring(
278 k->mUTF8String, k->mUTF8String + k->mLength),
279 nsDependentAtomString(he->mAtom), &err) == 0) &&
280 !err;
283 return he->mAtom->Equals(k->mUTF16String, k->mLength);
286 void nsAtomTable::AtomTableClearEntry(PLDHashTable* aTable,
287 PLDHashEntryHdr* aEntry) {
288 auto entry = static_cast<AtomTableEntry*>(aEntry);
289 entry->mAtom = nullptr;
292 static void AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey) {
293 static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
296 static const PLDHashTableOps AtomTableOps = {
297 AtomTableGetHash, AtomTableMatchKey, PLDHashTable::MoveEntryStub,
298 nsAtomTable::AtomTableClearEntry, AtomTableInitEntry};
300 // The atom table very quickly gets 10,000+ entries in it (or even 100,000+).
301 // But choosing the best initial subtable length has some subtleties: we add
302 // ~2700 static atoms at start-up, and then we start adding and removing
303 // dynamic atoms. If we make the tables too big to start with, when the first
304 // dynamic atom gets removed from a given table the load factor will be < 25%
305 // and we will shrink it.
307 // So we first make the simplifying assumption that the atoms are more or less
308 // evenly-distributed across the subtables (which is the case empirically).
309 // Then, we take the total atom count when the first dynamic atom is removed
310 // (~2700), divide that across the N subtables, and the largest capacity that
311 // will allow each subtable to be > 25% full with that count.
313 // So want an initial subtable capacity less than (2700 / N) * 4 = 10800 / N.
314 // Rounding down to the nearest power of two gives us 8192 / N. Since the
315 // capacity is double the initial length, we end up with (4096 / N) per
316 // subtable.
317 #define INITIAL_SUBTABLE_LENGTH (4096 / nsAtomTable::kNumSubTables)
319 nsAtomSubTable& nsAtomTable::SelectSubTable(AtomTableKey& aKey) {
320 // There are a few considerations around how we select subtables.
322 // First, we want entries to be evenly distributed across the subtables. This
323 // can be achieved by using any bits in the hash key, assuming the key itself
324 // is evenly-distributed. Empirical measurements indicate that this method
325 // produces a roughly-even distribution across subtables.
327 // Second, we want to use the hash bits that are least likely to influence an
328 // entry's position within the subtable. If we used the exact same bits used
329 // by the subtables, then each subtable would compute the same position for
330 // every entry it observes, leading to pessimal performance. In this case,
331 // we're using PLDHashTable, whose primary hash function uses the N leftmost
332 // bits of the hash value (where N is the log2 capacity of the table). This
333 // means we should prefer the rightmost bits here.
335 // Note that the below is equivalent to mHash % kNumSubTables, a replacement
336 // which an optimizing compiler should make, but let's avoid any doubt.
337 static_assert((kNumSubTables & (kNumSubTables - 1)) == 0,
338 "must be power of two");
339 return mSubTables[aKey.mHash & (kNumSubTables - 1)];
342 void nsAtomTable::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
343 AtomsSizes& aSizes) {
344 MOZ_ASSERT(NS_IsMainThread());
345 aSizes.mTable += aMallocSizeOf(this);
346 for (auto& table : mSubTables) {
347 MutexAutoLock lock(table.mLock);
348 table.AddSizeOfExcludingThisLocked(aMallocSizeOf, aSizes);
352 void nsAtomTable::GC(GCKind aKind) {
353 MOZ_ASSERT(NS_IsMainThread());
354 sRecentlyUsedMainThreadAtoms.Clear();
356 // Note that this is effectively an incremental GC, since only one subtable
357 // is locked at a time.
358 for (auto& table : mSubTables) {
359 MutexAutoLock lock(table.mLock);
360 table.GCLocked(aKind);
363 // We would like to assert that gUnusedAtomCount matches the number of atoms
364 // we found in the table which we removed. However, there are two problems
365 // with this:
366 // * We have multiple subtables, each with their own lock. For optimal
367 // performance we only want to hold one lock at a time, but this means
368 // that atoms can be added and removed between GC slices.
369 // * Even if we held all the locks and performed all GC slices atomically,
370 // the locks are not acquired for AddRef() and Release() calls. This means
371 // we might see a gUnusedAtomCount value in between, say, AddRef()
372 // incrementing mRefCnt and it decrementing gUnusedAtomCount.
374 // So, we don't bother asserting that there are no unused atoms at the end of
375 // a regular GC. But we can (and do) assert this just after the last GC at
376 // shutdown.
378 // Note that, barring refcounting bugs, an atom can only go from a zero
379 // refcount to a non-zero refcount while the atom table lock is held, so
380 // so we won't try to resurrect a zero refcount atom while trying to delete
381 // it.
383 MOZ_ASSERT_IF(aKind == GCKind::Shutdown,
384 nsDynamicAtom::gUnusedAtomCount == 0);
387 size_t nsAtomTable::RacySlowCount() {
388 // Trigger a GC so that the result is deterministic modulo other threads.
389 GC(GCKind::RegularOperation);
390 size_t count = 0;
391 for (auto& table : mSubTables) {
392 MutexAutoLock lock(table.mLock);
393 count += table.mTable.EntryCount();
396 return count;
399 nsAtomSubTable::nsAtomSubTable()
400 : mLock("Atom Sub-Table Lock"),
401 mTable(&AtomTableOps, sizeof(AtomTableEntry), INITIAL_SUBTABLE_LENGTH) {}
403 void nsAtomSubTable::GCLocked(GCKind aKind) {
404 MOZ_ASSERT(NS_IsMainThread());
405 mLock.AssertCurrentThreadOwns();
407 int32_t removedCount = 0; // A non-atomic temporary for cheaper increments.
408 nsAutoCString nonZeroRefcountAtoms;
409 uint32_t nonZeroRefcountAtomsCount = 0;
410 for (auto i = mTable.Iter(); !i.Done(); i.Next()) {
411 auto entry = static_cast<AtomTableEntry*>(i.Get());
412 if (entry->mAtom->IsStatic()) {
413 continue;
416 nsAtom* atom = entry->mAtom;
417 if (atom->IsDynamic() && atom->AsDynamic()->mRefCnt == 0) {
418 i.Remove();
419 nsDynamicAtom::Destroy(atom->AsDynamic());
420 ++removedCount;
422 #ifdef NS_FREE_PERMANENT_DATA
423 else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
424 // Only report leaking atoms in leak-checking builds in a run where we
425 // are checking for leaks, during shutdown. If something is anomalous,
426 // then we'll assert later in this function.
427 nsAutoCString name;
428 atom->ToUTF8String(name);
429 if (nonZeroRefcountAtomsCount == 0) {
430 nonZeroRefcountAtoms = name;
431 } else if (nonZeroRefcountAtomsCount < 20) {
432 nonZeroRefcountAtoms += ","_ns + name;
433 } else if (nonZeroRefcountAtomsCount == 20) {
434 nonZeroRefcountAtoms += ",..."_ns;
436 nonZeroRefcountAtomsCount++;
438 #endif
440 if (nonZeroRefcountAtomsCount) {
441 nsPrintfCString msg("%d dynamic atom(s) with non-zero refcount: %s",
442 nonZeroRefcountAtomsCount, nonZeroRefcountAtoms.get());
443 NS_ASSERTION(nonZeroRefcountAtomsCount == 0, msg.get());
446 nsDynamicAtom::gUnusedAtomCount -= removedCount;
449 void nsDynamicAtom::GCAtomTable() {
450 MOZ_ASSERT(gAtomTable);
451 if (NS_IsMainThread()) {
452 gAtomTable->GC(GCKind::RegularOperation);
456 //----------------------------------------------------------------------
458 // Have the static atoms been inserted into the table?
459 static bool gStaticAtomsDone = false;
461 void NS_InitAtomTable() {
462 MOZ_ASSERT(NS_IsMainThread());
463 MOZ_ASSERT(!gAtomTable);
465 // We register static atoms immediately so they're available for use as early
466 // as possible.
467 gAtomTable = new nsAtomTable();
468 gAtomTable->RegisterStaticAtoms(nsGkAtoms::sAtoms, nsGkAtoms::sAtomsLen);
469 gStaticAtomsDone = true;
472 void NS_ShutdownAtomTable() {
473 MOZ_ASSERT(NS_IsMainThread());
474 MOZ_ASSERT(gAtomTable);
476 #ifdef NS_FREE_PERMANENT_DATA
477 // Do a final GC to satisfy leak checking. We skip this step in release
478 // builds.
479 gAtomTable->GC(GCKind::Shutdown);
480 #endif
482 delete gAtomTable;
483 gAtomTable = nullptr;
486 void NS_AddSizeOfAtoms(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes) {
487 MOZ_ASSERT(NS_IsMainThread());
488 MOZ_ASSERT(gAtomTable);
489 return gAtomTable->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
492 void nsAtomSubTable::AddSizeOfExcludingThisLocked(MallocSizeOf aMallocSizeOf,
493 AtomsSizes& aSizes) {
494 mLock.AssertCurrentThreadOwns();
495 aSizes.mTable += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
496 for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
497 auto entry = static_cast<AtomTableEntry*>(iter.Get());
498 entry->mAtom->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
502 void nsAtomTable::RegisterStaticAtoms(const nsStaticAtom* aAtoms,
503 size_t aAtomsLen) {
504 MOZ_ASSERT(NS_IsMainThread());
505 MOZ_RELEASE_ASSERT(!gStaticAtomsDone, "Static atom insertion is finished!");
507 for (uint32_t i = 0; i < aAtomsLen; ++i) {
508 const nsStaticAtom* atom = &aAtoms[i];
509 MOZ_ASSERT(IsAsciiNullTerminated(atom->String()));
510 MOZ_ASSERT(NS_strlen(atom->String()) == atom->GetLength());
511 MOZ_ASSERT(atom->IsAsciiLowercase() ==
512 ::IsAsciiLowercase(atom->String(), atom->GetLength()));
514 // This assertion ensures the static atom's precomputed hash value matches
515 // what would be computed by mozilla::HashString(aStr), which is what we use
516 // when atomizing strings. We compute this hash in Atom.py.
517 MOZ_ASSERT(HashString(atom->String()) == atom->hash());
519 AtomTableKey key(atom);
520 nsAtomSubTable& table = SelectSubTable(key);
521 MutexAutoLock lock(table.mLock);
522 AtomTableEntry* he = table.Add(key);
524 if (he->mAtom) {
525 // There are two ways we could get here.
526 // - Register two static atoms with the same string.
527 // - Create a dynamic atom and then register a static atom with the same
528 // string while the dynamic atom is alive.
529 // Both cases can cause subtle bugs, and are disallowed. We're
530 // programming in C++ here, not Smalltalk.
531 nsAutoCString name;
532 he->mAtom->ToUTF8String(name);
533 MOZ_CRASH_UNSAFE_PRINTF("Atom for '%s' already exists", name.get());
535 he->mAtom = const_cast<nsStaticAtom*>(atom);
539 already_AddRefed<nsAtom> NS_Atomize(const char* aUTF8String) {
540 MOZ_ASSERT(gAtomTable);
541 return gAtomTable->Atomize(nsDependentCString(aUTF8String));
544 already_AddRefed<nsAtom> nsAtomTable::Atomize(const nsACString& aUTF8String) {
545 bool err;
546 AtomTableKey key(aUTF8String.Data(), aUTF8String.Length(), &err);
547 if (MOZ_UNLIKELY(err)) {
548 MOZ_ASSERT_UNREACHABLE("Tried to atomize invalid UTF-8.");
549 // The input was invalid UTF-8. Let's replace the errors with U+FFFD
550 // and atomize the result.
551 nsString str;
552 CopyUTF8toUTF16(aUTF8String, str);
553 return Atomize(str);
555 nsAtomSubTable& table = SelectSubTable(key);
556 MutexAutoLock lock(table.mLock);
557 AtomTableEntry* he = table.Add(key);
559 if (he->mAtom) {
560 RefPtr<nsAtom> atom = he->mAtom;
561 return atom.forget();
564 nsString str;
565 CopyUTF8toUTF16(aUTF8String, str);
566 RefPtr<nsAtom> atom = dont_AddRef(nsDynamicAtom::Create(str, key.mHash));
568 he->mAtom = atom;
570 return atom.forget();
573 already_AddRefed<nsAtom> NS_Atomize(const nsACString& aUTF8String) {
574 MOZ_ASSERT(gAtomTable);
575 return gAtomTable->Atomize(aUTF8String);
578 already_AddRefed<nsAtom> NS_Atomize(const char16_t* aUTF16String) {
579 MOZ_ASSERT(gAtomTable);
580 return gAtomTable->Atomize(nsDependentString(aUTF16String));
583 already_AddRefed<nsAtom> nsAtomTable::Atomize(const nsAString& aUTF16String) {
584 AtomTableKey key(aUTF16String.Data(), aUTF16String.Length());
585 nsAtomSubTable& table = SelectSubTable(key);
586 MutexAutoLock lock(table.mLock);
587 AtomTableEntry* he = table.Add(key);
589 if (he->mAtom) {
590 RefPtr<nsAtom> atom = he->mAtom;
591 return atom.forget();
594 RefPtr<nsAtom> atom =
595 dont_AddRef(nsDynamicAtom::Create(aUTF16String, key.mHash));
596 he->mAtom = atom;
598 return atom.forget();
601 already_AddRefed<nsAtom> NS_Atomize(const nsAString& aUTF16String) {
602 MOZ_ASSERT(gAtomTable);
603 return gAtomTable->Atomize(aUTF16String);
606 already_AddRefed<nsAtom> nsAtomTable::AtomizeMainThread(
607 const nsAString& aUTF16String) {
608 MOZ_ASSERT(NS_IsMainThread());
609 RefPtr<nsAtom> retVal;
610 AtomTableKey key(aUTF16String.Data(), aUTF16String.Length());
611 auto p = sRecentlyUsedMainThreadAtoms.Lookup(key);
612 if (p) {
613 retVal = p.Data();
614 return retVal.forget();
617 nsAtomSubTable& table = SelectSubTable(key);
618 MutexAutoLock lock(table.mLock);
619 AtomTableEntry* he = table.Add(key);
621 if (he->mAtom) {
622 retVal = he->mAtom;
623 } else {
624 RefPtr<nsAtom> newAtom =
625 dont_AddRef(nsDynamicAtom::Create(aUTF16String, key.mHash));
626 he->mAtom = newAtom;
627 retVal = std::move(newAtom);
630 p.Set(retVal);
631 return retVal.forget();
634 already_AddRefed<nsAtom> NS_AtomizeMainThread(const nsAString& aUTF16String) {
635 MOZ_ASSERT(gAtomTable);
636 return gAtomTable->AtomizeMainThread(aUTF16String);
639 nsrefcnt NS_GetNumberOfAtoms(void) {
640 MOZ_ASSERT(gAtomTable);
641 return gAtomTable->RacySlowCount();
644 int32_t NS_GetUnusedAtomCount(void) { return nsDynamicAtom::gUnusedAtomCount; }
646 nsStaticAtom* NS_GetStaticAtom(const nsAString& aUTF16String) {
647 MOZ_ASSERT(gStaticAtomsDone, "Static atom setup not yet done.");
648 MOZ_ASSERT(gAtomTable);
649 return gAtomTable->GetStaticAtom(aUTF16String);
652 nsStaticAtom* nsAtomTable::GetStaticAtom(const nsAString& aUTF16String) {
653 AtomTableKey key(aUTF16String.Data(), aUTF16String.Length());
654 nsAtomSubTable& table = SelectSubTable(key);
655 MutexAutoLock lock(table.mLock);
656 AtomTableEntry* he = table.Search(key);
657 return he && he->mAtom->IsStatic() ? static_cast<nsStaticAtom*>(he->mAtom)
658 : nullptr;
661 void ToLowerCaseASCII(RefPtr<nsAtom>& aAtom) {
662 // Assume the common case is that the atom is already ASCII lowercase.
663 if (aAtom->IsAsciiLowercase()) {
664 return;
667 nsAutoString lowercased;
668 ToLowerCaseASCII(nsDependentAtomString(aAtom), lowercased);
669 aAtom = NS_Atomize(lowercased);