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