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