VirtualBox

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

Last change on this file since 101962 was 101962, checked in by vboxsync, 12 months ago

libs/xpcom: Convert PR_GetEnv/PR_SetEnv to IPRT RTEnv* API, bugref:10545

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette