VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/ds/nsAtomTable.cpp@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.3 KB
Line 
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 "nsAtomTable.h"
40#include "nsStaticAtom.h"
41#include "nsString.h"
42#include "nsReadableUtils.h"
43#include "nsCRT.h"
44#include "pldhash.h"
45#include "prenv.h"
46#include "nsVoidArray.h"
47
48#define PL_ARENA_CONST_ALIGN_MASK 3
49#include "plarena.h"
50
51class nsStaticAtomWrapper;
52
53/**
54 * The shared hash table for atom lookups.
55 *
56 * XXX This should be manipulated in a threadsafe way or we should make
57 * sure it's only manipulated from the main thread. Probably the latter
58 * is better, since the former would hurt performance.
59 *
60 * If |gAtomTable.ops| is 0, then the table is uninitialized.
61 */
62static PLDHashTable gAtomTable;
63
64// this is where we keep the nsStaticAtomWrapper objects
65
66static PLArenaPool* gStaticAtomArena = 0;
67
68class nsStaticAtomWrapper : public nsIAtom
69{
70public:
71 nsStaticAtomWrapper(const nsStaticAtom* aAtom) :
72 mStaticAtom(aAtom)
73 {
74 MOZ_COUNT_CTOR(nsStaticAtomWrapper);
75 }
76 ~nsStaticAtomWrapper() { // no subclasses -> not virtual
77 // this is arena allocated and won't be called except in debug
78 // builds. If this function ever does anything non-debug, be sure
79 // to get rid of the ifdefs in AtomTableClearEntry!
80 MOZ_COUNT_DTOR(nsStaticAtomWrapper);
81 }
82
83 NS_IMETHOD QueryInterface(REFNSIID aIID,
84 void** aInstancePtr);
85 NS_IMETHOD_(nsrefcnt) AddRef(void);
86 NS_IMETHOD_(nsrefcnt) Release(void);
87
88 NS_DECL_NSIATOM
89
90 const nsStaticAtom* GetStaticAtom() {
91 return mStaticAtom;
92 }
93private:
94 const nsStaticAtom* mStaticAtom;
95};
96
97// the atomtableentry can contain either an AtomImpl or a
98// nsStaticAtomWrapper, indicated by the first bit of PtrBits
99typedef unsigned long PtrBits;
100
101struct AtomTableEntry : public PLDHashEntryHdr {
102 // mAtom & 0x1 means (mAtom & ~0x1) points to an nsStaticAtomWrapper
103 // else it points to an nsAtomImpl
104 PtrBits mAtom;
105
106 inline PRBool IsStaticAtom() const {
107 return (mAtom & 0x1) != 0;
108 }
109
110 inline void SetAtomImpl(AtomImpl* aAtom) {
111 NS_ASSERTION(aAtom, "Setting null atom");
112 mAtom = PtrBits(aAtom);
113 }
114
115 inline void SetStaticAtomWrapper(nsStaticAtomWrapper* aAtom) {
116 NS_ASSERTION(aAtom, "Setting null atom");
117 NS_ASSERTION((PtrBits(aAtom) & ~0x1) == PtrBits(aAtom),
118 "Pointers must align or this is broken");
119
120 mAtom = PtrBits(aAtom) | 0x1;
121 }
122
123 inline void ClearAtom() {
124 mAtom = nsnull;
125 }
126
127 inline PRBool HasValue() const {
128 return (mAtom & ~0x1) != 0;
129 }
130
131 // these accessors assume that you already know the type
132 inline AtomImpl *GetAtomImpl() const {
133 NS_ASSERTION(!IsStaticAtom(), "This is a static atom, not an AtomImpl");
134 return (AtomImpl*) (mAtom & ~0x1);
135 }
136
137 inline nsStaticAtomWrapper *GetStaticAtomWrapper() const {
138 NS_ASSERTION(IsStaticAtom(), "This is an AtomImpl, not a static atom");
139 return (nsStaticAtomWrapper*) (mAtom & ~0x1);
140 }
141
142 inline const nsStaticAtom* GetStaticAtom() const {
143 return GetStaticAtomWrapper()->GetStaticAtom();
144 }
145
146 // type-agnostic accessors
147
148 // get the string buffer
149 inline const char* get() const {
150 return IsStaticAtom() ? GetStaticAtom()->mString : GetAtomImpl()->mString;
151 }
152
153 // get an addreffed nsIAtom - not using already_AddRef'ed atom
154 // because the callers are not (and should not be) using nsCOMPtr
155 inline nsIAtom* GetAtom() const {
156 nsIAtom* result;
157
158 if (IsStaticAtom())
159 result = GetStaticAtomWrapper();
160 else {
161 result = GetAtomImpl();
162 NS_ADDREF(result);
163 }
164
165 return result;
166 }
167};
168
169PR_STATIC_CALLBACK(const void *)
170AtomTableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
171{
172 AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry);
173 NS_ASSERTION(he->HasValue(), "Empty atom. how did that happen?");
174 return he->get();
175}
176
177PR_STATIC_CALLBACK(PRBool)
178AtomTableMatchKey(PLDHashTable *table,
179 const PLDHashEntryHdr *entry,
180 const void *key)
181{
182 const AtomTableEntry *he = NS_STATIC_CAST(const AtomTableEntry*, entry);
183 const char* keyStr = NS_STATIC_CAST(const char*, key);
184 return nsCRT::strcmp(keyStr, he->get()) == 0;
185}
186
187PR_STATIC_CALLBACK(void)
188AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
189{
190 AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry);
191
192 he->keyHash = 0;
193
194 if (!he->IsStaticAtom()) {
195 AtomImpl *atom = he->GetAtomImpl();
196 // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and
197 // they then remove themselves from the table. In other words, they
198 // are owned by the callers who own references to them.
199 // |PermanentAtomImpl| permanent atoms ignore their refcount and are
200 // deleted when they are removed from the table at table destruction.
201 // In other words, they are owned by the atom table.
202 if (atom->IsPermanent())
203 delete NS_STATIC_CAST(PermanentAtomImpl*, atom);
204 }
205 else {
206 he->GetStaticAtomWrapper()->~nsStaticAtomWrapper();
207 }
208
209 he->ClearAtom();
210}
211
212static const PLDHashTableOps AtomTableOps = {
213 PL_DHashAllocTable,
214 PL_DHashFreeTable,
215 AtomTableGetKey,
216 PL_DHashStringKey,
217 AtomTableMatchKey,
218 PL_DHashMoveEntryStub,
219 AtomTableClearEntry,
220 PL_DHashFinalizeStub,
221 NULL
222};
223
224
225#ifdef DEBUG
226
227PR_STATIC_CALLBACK(PLDHashOperator)
228DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he,
229 PRUint32 index, void *arg)
230{
231 AtomTableEntry *entry = NS_STATIC_CAST(AtomTableEntry*, he);
232
233 if (entry->IsStaticAtom())
234 return PL_DHASH_NEXT;
235
236 AtomImpl* atom = entry->GetAtomImpl();
237 if (!atom->IsPermanent()) {
238 ++*NS_STATIC_CAST(PRUint32*, arg);
239 const char *str;
240 atom->GetUTF8String(&str);
241 fputs(str, stdout);
242 fputs("\n", stdout);
243 }
244 return PL_DHASH_NEXT;
245}
246
247#endif
248
249static inline
250void PromoteToPermanent(AtomImpl* aAtom)
251{
252#ifdef NS_BUILD_REFCNT_LOGGING
253 {
254 nsrefcnt refcount = aAtom->GetRefCount();
255 do {
256 NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl");
257 } while (refcount);
258 }
259#endif
260 aAtom = new (aAtom) PermanentAtomImpl();
261}
262
263void NS_PurgeAtomTable()
264{
265 if (gAtomTable.ops) {
266#ifdef DEBUG
267 if (PR_GetEnv("MOZ_DUMP_ATOM_LEAKS")) {
268 PRUint32 leaked = 0;
269 printf("*** %d atoms still exist (including permanent):\n",
270 gAtomTable.entryCount);
271 PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
272 printf("*** %u non-permanent atoms leaked\n", leaked);
273 }
274#endif
275 PL_DHashTableFinish(&gAtomTable);
276 gAtomTable.entryCount = 0;
277 gAtomTable.ops = nsnull;
278
279 if (gStaticAtomArena) {
280 PL_FinishArenaPool(gStaticAtomArena);
281 delete gStaticAtomArena;
282 gStaticAtomArena = nsnull;
283 }
284 }
285}
286
287AtomImpl::AtomImpl()
288{
289}
290
291AtomImpl::~AtomImpl()
292{
293 NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
294 // Permanent atoms are removed from the hashtable at shutdown, and we
295 // don't want to remove them twice. See comment above in
296 // |AtomTableClearEntry|.
297 if (!IsPermanent()) {
298 PL_DHashTableOperate(&gAtomTable, mString, PL_DHASH_REMOVE);
299 if (gAtomTable.entryCount == 0) {
300 PL_DHashTableFinish(&gAtomTable);
301 NS_ASSERTION(gAtomTable.entryCount == 0,
302 "PL_DHashTableFinish changed the entry count");
303 }
304 }
305}
306
307NS_IMPL_THREADSAFE_ISUPPORTS1(AtomImpl, nsIAtom)
308
309NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::AddRef()
310{
311 return 2;
312}
313
314NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::Release()
315{
316 return 1;
317}
318
319/* virtual */ PRBool
320AtomImpl::IsPermanent()
321{
322 return PR_FALSE;
323}
324
325/* virtual */ PRBool
326PermanentAtomImpl::IsPermanent()
327{
328 return PR_TRUE;
329}
330
331void* AtomImpl::operator new ( size_t size, const nsACString& aString ) CPP_THROW_NEW
332{
333 /*
334 Note: since the |size| will initially also include the |PRUnichar| member
335 |mString|, our size calculation will give us one character too many.
336 We use that extra character for a zero-terminator.
337
338 Note: this construction is not guaranteed to be possible by the C++
339 compiler. A more reliable scheme is used by |nsShared[C]String|s, see
340 http://lxr.mozilla.org/seamonkey/source/xpcom/ds/nsSharedString.h#174
341 */
342 size += aString.Length() * sizeof(char);
343 AtomImpl* ii = NS_STATIC_CAST(AtomImpl*, ::operator new(size));
344
345 char* toBegin = &ii->mString[0];
346 nsACString::const_iterator fromBegin, fromEnd;
347 *copy_string(aString.BeginReading(fromBegin), aString.EndReading(fromEnd), toBegin) = '\0';
348 return ii;
349}
350
351void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW {
352 NS_ASSERTION(!aAtom->IsPermanent(),
353 "converting atom that's already permanent");
354
355 // Just let the constructor overwrite the vtable pointer.
356 return aAtom;
357}
358
359NS_IMETHODIMP
360AtomImpl::ToString(nsAString& aBuf)
361{
362 CopyUTF8toUTF16(nsDependentCString(mString), aBuf);
363 return NS_OK;
364}
365
366NS_IMETHODIMP
367AtomImpl::ToUTF8String(nsACString& aBuf)
368{
369 aBuf.Assign(mString);
370 return NS_OK;
371}
372
373NS_IMETHODIMP
374AtomImpl::GetUTF8String(const char **aResult)
375{
376 NS_PRECONDITION(aResult, "null out param");
377 *aResult = mString;
378 return NS_OK;
379}
380
381NS_IMETHODIMP
382AtomImpl::EqualsUTF8(const nsACString& aString, PRBool* aResult)
383{
384 *aResult = aString.Equals(mString);
385 return NS_OK;
386}
387
388NS_IMETHODIMP
389AtomImpl::Equals(const nsAString& aString, PRBool* aResult)
390{
391 *aResult = NS_ConvertUTF16toUTF8(aString).Equals(mString);
392 return NS_OK;
393}
394
395//----------------------------------------------------------------------
396
397// wrapper class for the nsStaticAtom struct
398
399NS_IMETHODIMP_(nsrefcnt)
400nsStaticAtomWrapper::AddRef()
401{
402 return 2;
403}
404
405NS_IMETHODIMP_(nsrefcnt)
406nsStaticAtomWrapper::Release()
407{
408 return 1;
409}
410
411NS_IMPL_QUERY_INTERFACE1(nsStaticAtomWrapper, nsIAtom)
412
413NS_IMETHODIMP
414nsStaticAtomWrapper::GetUTF8String(const char** aResult)
415{
416 *aResult = mStaticAtom->mString;
417 return NS_OK;
418}
419
420NS_IMETHODIMP
421nsStaticAtomWrapper::ToString(nsAString& aBuf)
422{
423 // static should always be always ASCII, to allow tools like gperf
424 // to generate the tables, and to avoid unnecessary conversion
425 NS_ASSERTION(nsCRT::IsAscii(mStaticAtom->mString),
426 "Data loss - atom should be ASCII");
427 CopyASCIItoUCS2(nsDependentCString(mStaticAtom->mString), aBuf);
428 return NS_OK;
429}
430
431NS_IMETHODIMP
432nsStaticAtomWrapper::ToUTF8String(nsACString& aBuf)
433{
434 aBuf.Assign(mStaticAtom->mString);
435 return NS_OK;
436}
437
438NS_IMETHODIMP
439nsStaticAtomWrapper::EqualsUTF8(const nsACString& aString, PRBool* aResult)
440{
441 *aResult = aString.Equals(mStaticAtom->mString);
442 return NS_OK;
443}
444
445NS_IMETHODIMP
446nsStaticAtomWrapper::Equals(const nsAString& aString, PRBool* aResult)
447{
448 *aResult = NS_ConvertUCS2toUTF8(aString).Equals(mStaticAtom->mString);
449 return NS_OK;
450}
451//----------------------------------------------------------------------
452
453NS_COM nsIAtom* NS_NewAtom(const char* isolatin1)
454{
455 return NS_NewAtom(nsDependentCString(isolatin1));
456}
457
458NS_COM nsIAtom* NS_NewPermanentAtom(const char* isolatin1)
459{
460 return NS_NewPermanentAtom(NS_ConvertASCIItoUCS2(isolatin1));
461}
462
463static nsStaticAtomWrapper*
464WrapStaticAtom(const nsStaticAtom* aAtom)
465{
466 if (!gStaticAtomArena) {
467 gStaticAtomArena = new PLArenaPool;
468 if (!gStaticAtomArena)
469 return nsnull;
470
471 PL_INIT_ARENA_POOL(gStaticAtomArena, "nsStaticAtomArena", 4096);
472 }
473
474 void* mem;
475 PL_ARENA_ALLOCATE(mem, gStaticAtomArena, sizeof(nsStaticAtom));
476
477 nsStaticAtomWrapper* wrapper =
478 new (mem) nsStaticAtomWrapper(aAtom);
479
480 return wrapper;
481}
482
483static AtomTableEntry* GetAtomHashEntry(const char* aString)
484{
485 if (!gAtomTable.ops &&
486 !PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
487 sizeof(AtomTableEntry), 2048)) {
488 gAtomTable.ops = nsnull;
489 return nsnull;
490 }
491 return NS_STATIC_CAST(AtomTableEntry*,
492 PL_DHashTableOperate(&gAtomTable,
493 aString,
494 PL_DHASH_ADD));
495}
496
497NS_COM nsresult
498NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, PRUint32 aAtomCount)
499{
500 // this does two things:
501 // 1) wraps each static atom in a wrapper, if necessary
502 // 2) initializes the address pointed to by each mAtom slot
503
504 for (PRUint32 i=0; i<aAtomCount; i++) {
505 NS_ASSERTION(nsCRT::IsAscii(aAtoms[i].mString),
506 "Static atoms must be ASCII!");
507 AtomTableEntry *he =
508 GetAtomHashEntry(aAtoms[i].mString);
509
510 if (he->HasValue() && aAtoms[i].mAtom) {
511 // there already is an atom with this name in the table.. but we
512 // still have to update mAtom
513 if (!he->IsStaticAtom() && !he->GetAtomImpl()->IsPermanent()) {
514 // since we wanted to create a static atom but there is
515 // already one there, we convert it to a non-refcounting
516 // permanent atom
517 PromoteToPermanent(he->GetAtomImpl());
518 }
519
520 // and now, if the consumer wants to remember this value in a
521 // slot, we do so
522 if (aAtoms[i].mAtom)
523 *aAtoms[i].mAtom = he->GetAtom();
524 }
525
526 else {
527 nsStaticAtomWrapper* atom = WrapStaticAtom(&aAtoms[i]);
528 NS_ASSERTION(atom, "Failed to wrap static atom");
529
530 // but even if atom is null, no real difference in code..
531 he->SetStaticAtomWrapper(atom);
532 if (aAtoms[i].mAtom)
533 *aAtoms[i].mAtom = atom;
534 }
535 }
536 return NS_OK;
537}
538
539NS_COM nsIAtom* NS_NewAtom( const nsAString& aString )
540{
541 NS_ConvertUCS2toUTF8 utf8String(aString);
542
543 return NS_NewAtom(utf8String);
544}
545
546NS_COM
547nsIAtom*
548NS_NewAtom( const nsACString& aString )
549{
550 AtomTableEntry *he = GetAtomHashEntry(PromiseFlatCString(aString).get());
551
552 if (he->HasValue())
553 return he->GetAtom();
554
555 AtomImpl* atom = new (aString) AtomImpl();
556 he->SetAtomImpl(atom);
557 if (!atom) {
558 PL_DHashTableRawRemove(&gAtomTable, he);
559 return nsnull;
560 }
561
562 NS_ADDREF(atom);
563 return atom;
564}
565
566NS_COM nsIAtom* NS_NewPermanentAtom( const nsAString& aString )
567{
568 return NS_NewPermanentAtom(NS_ConvertUCS2toUTF8(aString));
569}
570
571NS_COM
572nsIAtom* NS_NewPermanentAtom( const nsACString& aString )
573{
574 AtomTableEntry *he = GetAtomHashEntry(PromiseFlatCString(aString).get());
575
576 if (he->HasValue() && he->IsStaticAtom())
577 return he->GetStaticAtomWrapper();
578
579 // either there is no atom and we'll create an AtomImpl,
580 // or there is an existing AtomImpl
581 AtomImpl* atom = he->GetAtomImpl();
582
583 if (atom) {
584 // ensure that it's permanent
585 if (!atom->IsPermanent()) {
586 PromoteToPermanent(atom);
587 }
588 } else {
589 // otherwise, make a new atom
590 atom = new (aString) PermanentAtomImpl();
591 he->SetAtomImpl(atom);
592 if ( !atom ) {
593 PL_DHashTableRawRemove(&gAtomTable, he);
594 return nsnull;
595 }
596 }
597
598 NS_ADDREF(atom);
599 return atom;
600}
601
602NS_COM nsIAtom* NS_NewAtom( const PRUnichar* str )
603{
604 return NS_NewAtom(NS_ConvertUCS2toUTF8(str));
605}
606
607NS_COM nsIAtom* NS_NewPermanentAtom( const PRUnichar* str )
608{
609 return NS_NewPermanentAtom(nsDependentString(str));
610}
611
612NS_COM nsrefcnt NS_GetNumberOfAtoms(void)
613{
614 return gAtomTable.entryCount;
615}
616
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use