Back to index

lightning-sunbird  0.9+nobinonly
nsAtomTable.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 // vim:cindent:ts=2:et:sw=2:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsAtomTable.h"
00040 #include "nsStaticAtom.h"
00041 #include "nsString.h"
00042 #include "nsReadableUtils.h"
00043 #include "nsCRT.h"
00044 #include "pldhash.h"
00045 #include "prenv.h"
00046 #include "nsVoidArray.h"
00047 
00048 #define PL_ARENA_CONST_ALIGN_MASK 3
00049 #include "plarena.h"
00050 
00051 class nsStaticAtomWrapper;
00052 
00062 static PLDHashTable gAtomTable;
00063 
00064 // this is where we keep the nsStaticAtomWrapper objects
00065 
00066 static PLArenaPool* gStaticAtomArena = 0;
00067 
00068 class nsStaticAtomWrapper : public nsIAtom
00069 {
00070 public:
00071   nsStaticAtomWrapper(const nsStaticAtom* aAtom) :
00072     mStaticAtom(aAtom)
00073   {
00074     MOZ_COUNT_CTOR(nsStaticAtomWrapper);
00075   }
00076   ~nsStaticAtomWrapper() {   // no subclasses -> not virtual
00077     // this is arena allocated and won't be called except in debug
00078     // builds. If this function ever does anything non-debug, be sure
00079     // to get rid of the ifdefs in AtomTableClearEntry!
00080     MOZ_COUNT_DTOR(nsStaticAtomWrapper);
00081   }
00082 
00083   NS_IMETHOD QueryInterface(REFNSIID aIID,
00084                             void** aInstancePtr);
00085   NS_IMETHOD_(nsrefcnt) AddRef(void);
00086   NS_IMETHOD_(nsrefcnt) Release(void);
00087 
00088   NS_DECL_NSIATOM
00089 
00090   const nsStaticAtom* GetStaticAtom() {
00091     return mStaticAtom;
00092   }
00093 private:
00094   const nsStaticAtom* mStaticAtom;
00095 };
00096 
00097 // the atomtableentry can contain either an AtomImpl or a
00098 // nsStaticAtomWrapper, indicated by the first bit of PtrBits
00099 typedef unsigned long PtrBits;
00100 
00101 struct AtomTableEntry : public PLDHashEntryHdr {
00102   // mAtom & 0x1 means (mAtom & ~0x1) points to an nsStaticAtomWrapper
00103   // else it points to an nsAtomImpl
00104   PtrBits mAtom;
00105 
00106   inline PRBool IsStaticAtom() const {
00107     return (mAtom & 0x1) != 0;
00108   }
00109   
00110   inline void SetAtomImpl(AtomImpl* aAtom) {
00111     NS_ASSERTION(aAtom, "Setting null atom");
00112     mAtom = PtrBits(aAtom);
00113   }
00114 
00115   inline void SetStaticAtomWrapper(nsStaticAtomWrapper* aAtom) {
00116     NS_ASSERTION(aAtom, "Setting null atom");
00117     NS_ASSERTION((PtrBits(aAtom) & ~0x1) == PtrBits(aAtom),
00118                  "Pointers must align or this is broken");
00119     
00120     mAtom = PtrBits(aAtom) | 0x1;
00121   }
00122   
00123   inline void ClearAtom() {
00124     mAtom = nsnull;
00125   }
00126 
00127   inline PRBool HasValue() const {
00128     return (mAtom & ~0x1) != 0;
00129   }
00130 
00131   // these accessors assume that you already know the type
00132   inline AtomImpl *GetAtomImpl() const {
00133     NS_ASSERTION(!IsStaticAtom(), "This is a static atom, not an AtomImpl");
00134     return (AtomImpl*) (mAtom & ~0x1);
00135   }
00136   
00137   inline nsStaticAtomWrapper *GetStaticAtomWrapper() const {
00138     NS_ASSERTION(IsStaticAtom(), "This is an AtomImpl, not a static atom");
00139     return (nsStaticAtomWrapper*) (mAtom & ~0x1);
00140   }
00141 
00142   inline const nsStaticAtom* GetStaticAtom() const {
00143     return GetStaticAtomWrapper()->GetStaticAtom();
00144   }
00145 
00146   // type-agnostic accessors
00147 
00148   // get the string buffer
00149   inline const char* get() const {
00150     return IsStaticAtom() ? GetStaticAtom()->mString : GetAtomImpl()->mString;
00151   }
00152 
00153   // get an addreffed nsIAtom - not using already_AddRef'ed atom
00154   // because the callers are not (and should not be) using nsCOMPtr
00155   inline nsIAtom* GetAtom() const {
00156     nsIAtom* result;
00157     
00158     if (IsStaticAtom())
00159       result = GetStaticAtomWrapper();
00160     else {
00161       result = GetAtomImpl();
00162       NS_ADDREF(result);
00163     }
00164     
00165     return result;
00166   }
00167 };
00168 
00169 PR_STATIC_CALLBACK(const void *)
00170 AtomTableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00171 {
00172   AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry);
00173   NS_ASSERTION(he->HasValue(), "Empty atom. how did that happen?");
00174   return he->get();
00175 }
00176 
00177 PR_STATIC_CALLBACK(PRBool)
00178 AtomTableMatchKey(PLDHashTable *table,
00179                   const PLDHashEntryHdr *entry,
00180                   const void *key)
00181 {
00182   const AtomTableEntry *he = NS_STATIC_CAST(const AtomTableEntry*, entry);
00183   const char* keyStr = NS_STATIC_CAST(const char*, key);
00184   return nsCRT::strcmp(keyStr, he->get()) == 0;
00185 }
00186 
00187 PR_STATIC_CALLBACK(void)
00188 AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
00189 {
00190   AtomTableEntry *he = NS_STATIC_CAST(AtomTableEntry*, entry);
00191   
00192   he->keyHash = 0;
00193 
00194   if (!he->IsStaticAtom()) {
00195     AtomImpl *atom = he->GetAtomImpl();
00196     // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and
00197     // they then remove themselves from the table.  In other words, they
00198     // are owned by the callers who own references to them.
00199     // |PermanentAtomImpl| permanent atoms ignore their refcount and are
00200     // deleted when they are removed from the table at table destruction.
00201     // In other words, they are owned by the atom table.
00202     if (atom->IsPermanent())
00203       delete NS_STATIC_CAST(PermanentAtomImpl*, atom);
00204   }
00205   else {
00206     he->GetStaticAtomWrapper()->~nsStaticAtomWrapper();
00207   }
00208   
00209   he->ClearAtom();
00210 }
00211 
00212 static const PLDHashTableOps AtomTableOps = {
00213   PL_DHashAllocTable,
00214   PL_DHashFreeTable,
00215   AtomTableGetKey,
00216   PL_DHashStringKey,
00217   AtomTableMatchKey,
00218   PL_DHashMoveEntryStub,
00219   AtomTableClearEntry,
00220   PL_DHashFinalizeStub,
00221   NULL
00222 };
00223 
00224 
00225 #ifdef DEBUG
00226 
00227 PR_STATIC_CALLBACK(PLDHashOperator)
00228 DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he,
00229               PRUint32 index, void *arg)
00230 {
00231   AtomTableEntry *entry = NS_STATIC_CAST(AtomTableEntry*, he);
00232   
00233   if (entry->IsStaticAtom())
00234     return PL_DHASH_NEXT;
00235   
00236   AtomImpl* atom = entry->GetAtomImpl();
00237   if (!atom->IsPermanent()) {
00238     ++*NS_STATIC_CAST(PRUint32*, arg);
00239     const char *str;
00240     atom->GetUTF8String(&str);
00241     fputs(str, stdout);
00242     fputs("\n", stdout);
00243   }
00244   return PL_DHASH_NEXT;
00245 }
00246 
00247 #endif
00248 
00249 static inline
00250 void PromoteToPermanent(AtomImpl* aAtom)
00251 {
00252 #ifdef NS_BUILD_REFCNT_LOGGING
00253   {
00254     nsrefcnt refcount = aAtom->GetRefCount();
00255     do {
00256       NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl");
00257     } while (refcount);
00258   }
00259 #endif
00260   aAtom = new (aAtom) PermanentAtomImpl();
00261 }
00262 
00263 void NS_PurgeAtomTable()
00264 {
00265   if (gAtomTable.ops) {
00266 #ifdef DEBUG
00267     if (PR_GetEnv("MOZ_DUMP_ATOM_LEAKS")) {
00268       PRUint32 leaked = 0;
00269       printf("*** %d atoms still exist (including permanent):\n",
00270              gAtomTable.entryCount);
00271       PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
00272       printf("*** %u non-permanent atoms leaked\n", leaked);
00273     }
00274 #endif
00275     PL_DHashTableFinish(&gAtomTable);
00276     gAtomTable.entryCount = 0;
00277     gAtomTable.ops = nsnull;
00278 
00279     if (gStaticAtomArena) {
00280       PL_FinishArenaPool(gStaticAtomArena);
00281       delete gStaticAtomArena;
00282       gStaticAtomArena = nsnull;
00283     }
00284   }
00285 }
00286 
00287 AtomImpl::AtomImpl()
00288 {
00289 }
00290 
00291 AtomImpl::~AtomImpl()
00292 {
00293   NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
00294   // Permanent atoms are removed from the hashtable at shutdown, and we
00295   // don't want to remove them twice.  See comment above in
00296   // |AtomTableClearEntry|.
00297   if (!IsPermanentInDestructor()) {
00298     PL_DHashTableOperate(&gAtomTable, mString, PL_DHASH_REMOVE);
00299     if (gAtomTable.entryCount == 0) {
00300       PL_DHashTableFinish(&gAtomTable);
00301       NS_ASSERTION(gAtomTable.entryCount == 0,
00302                    "PL_DHashTableFinish changed the entry count");
00303     }
00304   }
00305 }
00306 
00307 NS_IMPL_THREADSAFE_ISUPPORTS1(AtomImpl, nsIAtom)
00308 
00309 PermanentAtomImpl::PermanentAtomImpl()
00310   : AtomImpl()
00311 {
00312 }
00313 
00314 PermanentAtomImpl::~PermanentAtomImpl()
00315 {
00316   // So we can tell if we were permanent while running the base class dtor.
00317   mRefCnt = REFCNT_PERMANENT_SENTINEL;
00318 }
00319 
00320 NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::AddRef()
00321 {
00322   return 2;
00323 }
00324 
00325 NS_IMETHODIMP_(nsrefcnt) PermanentAtomImpl::Release()
00326 {
00327   return 1;
00328 }
00329 
00330 /* virtual */ PRBool
00331 AtomImpl::IsPermanent()
00332 {
00333   return PR_FALSE;
00334 }
00335 
00336 /* virtual */ PRBool
00337 PermanentAtomImpl::IsPermanent()
00338 {
00339   return PR_TRUE;
00340 }
00341 
00342 void* AtomImpl::operator new ( size_t size, const nsACString& aString ) CPP_THROW_NEW
00343 {
00344     /*
00345       Note: since the |size| will initially also include the |PRUnichar| member
00346         |mString|, our size calculation will give us one character too many.
00347         We use that extra character for a zero-terminator.
00348 
00349       Note: this construction is not guaranteed to be possible by the C++
00350         compiler.  A more reliable scheme is used by |nsShared[C]String|s, see
00351         http://lxr.mozilla.org/seamonkey/source/xpcom/ds/nsSharedString.h#174
00352      */
00353   size += aString.Length() * sizeof(char);
00354   AtomImpl* ii = NS_STATIC_CAST(AtomImpl*, ::operator new(size));
00355 
00356   char* toBegin = &ii->mString[0];
00357   nsACString::const_iterator fromBegin, fromEnd;
00358   *copy_string(aString.BeginReading(fromBegin), aString.EndReading(fromEnd), toBegin) = '\0';
00359   return ii;
00360 }
00361 
00362 void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW {
00363   NS_ASSERTION(!aAtom->IsPermanent(),
00364                "converting atom that's already permanent");
00365 
00366   // Just let the constructor overwrite the vtable pointer.
00367   return aAtom;
00368 }
00369 
00370 NS_IMETHODIMP 
00371 AtomImpl::ToString(nsAString& aBuf)
00372 {
00373   CopyUTF8toUTF16(nsDependentCString(mString), aBuf);
00374   return NS_OK;
00375 }
00376 
00377 NS_IMETHODIMP
00378 AtomImpl::ToUTF8String(nsACString& aBuf)
00379 {
00380   aBuf.Assign(mString);
00381   return NS_OK;
00382 }
00383 
00384 NS_IMETHODIMP 
00385 AtomImpl::GetUTF8String(const char **aResult)
00386 {
00387   NS_PRECONDITION(aResult, "null out param");
00388   *aResult = mString;
00389   return NS_OK;
00390 }
00391 
00392 NS_IMETHODIMP
00393 AtomImpl::EqualsUTF8(const nsACString& aString, PRBool* aResult)
00394 {
00395   *aResult = aString.Equals(mString);
00396   return NS_OK;
00397 }
00398 
00399 NS_IMETHODIMP
00400 AtomImpl::Equals(const nsAString& aString, PRBool* aResult)
00401 {
00402   *aResult = NS_ConvertUTF16toUTF8(aString).Equals(mString);
00403   return NS_OK;
00404 }
00405 
00406 //----------------------------------------------------------------------
00407 
00408 // wrapper class for the nsStaticAtom struct
00409 
00410 NS_IMETHODIMP_(nsrefcnt)
00411 nsStaticAtomWrapper::AddRef()
00412 {
00413   return 2;
00414 }
00415 
00416 NS_IMETHODIMP_(nsrefcnt)
00417 nsStaticAtomWrapper::Release()
00418 {
00419   return 1;
00420 }
00421 
00422 NS_IMPL_QUERY_INTERFACE1(nsStaticAtomWrapper, nsIAtom)
00423 
00424 NS_IMETHODIMP
00425 nsStaticAtomWrapper::GetUTF8String(const char** aResult)
00426 {
00427   *aResult = mStaticAtom->mString;
00428   return NS_OK;
00429 }
00430 
00431 NS_IMETHODIMP
00432 nsStaticAtomWrapper::ToString(nsAString& aBuf)
00433 {
00434   // static should always be always ASCII, to allow tools like gperf
00435   // to generate the tables, and to avoid unnecessary conversion
00436   NS_ASSERTION(nsCRT::IsAscii(mStaticAtom->mString),
00437                "Data loss - atom should be ASCII");
00438   CopyASCIItoUCS2(nsDependentCString(mStaticAtom->mString), aBuf);
00439   return NS_OK;
00440 }
00441 
00442 NS_IMETHODIMP
00443 nsStaticAtomWrapper::ToUTF8String(nsACString& aBuf)
00444 {
00445   aBuf.Assign(mStaticAtom->mString);
00446   return NS_OK;
00447 }
00448 
00449 NS_IMETHODIMP
00450 nsStaticAtomWrapper::EqualsUTF8(const nsACString& aString, PRBool* aResult)
00451 {
00452   *aResult = aString.Equals(mStaticAtom->mString);
00453   return NS_OK;
00454 }
00455 
00456 NS_IMETHODIMP
00457 nsStaticAtomWrapper::Equals(const nsAString& aString, PRBool* aResult)
00458 {
00459   *aResult = NS_ConvertUCS2toUTF8(aString).Equals(mStaticAtom->mString);
00460   return NS_OK;
00461 }
00462 //----------------------------------------------------------------------
00463 
00464 NS_COM nsIAtom* NS_NewAtom(const char* isolatin1)
00465 {
00466   return NS_NewAtom(nsDependentCString(isolatin1));
00467 }
00468 
00469 NS_COM nsIAtom* NS_NewPermanentAtom(const char* isolatin1)
00470 {
00471   return NS_NewPermanentAtom(NS_ConvertASCIItoUCS2(isolatin1));
00472 }
00473 
00474 static nsStaticAtomWrapper*
00475 WrapStaticAtom(const nsStaticAtom* aAtom)
00476 {
00477   if (!gStaticAtomArena) {
00478     gStaticAtomArena = new PLArenaPool;
00479     if (!gStaticAtomArena)
00480       return nsnull;
00481     
00482     PL_INIT_ARENA_POOL(gStaticAtomArena, "nsStaticAtomArena", 4096);
00483   }
00484   
00485   void* mem;
00486   PL_ARENA_ALLOCATE(mem, gStaticAtomArena, sizeof(nsStaticAtom));
00487   
00488   nsStaticAtomWrapper* wrapper =
00489     new (mem) nsStaticAtomWrapper(aAtom);
00490   
00491   return wrapper;
00492 }
00493 
00494 static AtomTableEntry* GetAtomHashEntry(const char* aString)
00495 {
00496   if (!gAtomTable.ops &&
00497       !PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
00498                          sizeof(AtomTableEntry), 2048)) {
00499     gAtomTable.ops = nsnull;
00500     return nsnull;
00501   }
00502   return NS_STATIC_CAST(AtomTableEntry*,
00503                         PL_DHashTableOperate(&gAtomTable,
00504                                              aString,
00505                                              PL_DHASH_ADD));
00506 }
00507 
00508 NS_COM nsresult
00509 NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, PRUint32 aAtomCount)
00510 {
00511   // this does two things:
00512   // 1) wraps each static atom in a wrapper, if necessary
00513   // 2) initializes the address pointed to by each mAtom slot
00514   
00515   for (PRUint32 i=0; i<aAtomCount; i++) {
00516     NS_ASSERTION(nsCRT::IsAscii(aAtoms[i].mString),
00517                  "Static atoms must be ASCII!");
00518     AtomTableEntry *he =
00519       GetAtomHashEntry(aAtoms[i].mString);
00520     
00521     if (he->HasValue() && aAtoms[i].mAtom) {
00522       // there already is an atom with this name in the table.. but we
00523       // still have to update mAtom
00524       if (!he->IsStaticAtom() && !he->GetAtomImpl()->IsPermanent()) {
00525         // since we wanted to create a static atom but there is
00526         // already one there, we convert it to a non-refcounting
00527         // permanent atom
00528         PromoteToPermanent(he->GetAtomImpl());
00529       }
00530       
00531       // and now, if the consumer wants to remember this value in a
00532       // slot, we do so
00533       if (aAtoms[i].mAtom)
00534         *aAtoms[i].mAtom = he->GetAtom();
00535     }
00536     
00537     else {
00538       nsStaticAtomWrapper* atom = WrapStaticAtom(&aAtoms[i]);
00539       NS_ASSERTION(atom, "Failed to wrap static atom");
00540 
00541       // but even if atom is null, no real difference in code..
00542       he->SetStaticAtomWrapper(atom);
00543       if (aAtoms[i].mAtom)
00544         *aAtoms[i].mAtom = atom;
00545     }
00546   }
00547   return NS_OK;
00548 }
00549 
00550 NS_COM nsIAtom* NS_NewAtom( const nsAString& aString )
00551 {
00552   NS_ConvertUCS2toUTF8 utf8String(aString);
00553 
00554   return NS_NewAtom(utf8String);
00555 }
00556 
00557 NS_COM
00558 nsIAtom*
00559 NS_NewAtom( const nsACString& aString )
00560 {
00561   AtomTableEntry *he = GetAtomHashEntry(PromiseFlatCString(aString).get());
00562 
00563   if (he->HasValue())
00564     return he->GetAtom();
00565 
00566   AtomImpl* atom = new (aString) AtomImpl();
00567   he->SetAtomImpl(atom);
00568   if (!atom) {
00569     PL_DHashTableRawRemove(&gAtomTable, he);
00570     return nsnull;
00571   }
00572 
00573   NS_ADDREF(atom);
00574   return atom;
00575 }
00576 
00577 NS_COM nsIAtom* NS_NewPermanentAtom( const nsAString& aString )
00578 {
00579   return NS_NewPermanentAtom(NS_ConvertUCS2toUTF8(aString));
00580 }
00581 
00582 NS_COM
00583 nsIAtom* NS_NewPermanentAtom( const nsACString& aString )
00584 {
00585   AtomTableEntry *he = GetAtomHashEntry(PromiseFlatCString(aString).get());
00586 
00587   if (he->HasValue() && he->IsStaticAtom())
00588     return he->GetStaticAtomWrapper();
00589   
00590   // either there is no atom and we'll create an AtomImpl,
00591   // or there is an existing AtomImpl
00592   AtomImpl* atom = he->GetAtomImpl();
00593   
00594   if (atom) {
00595     // ensure that it's permanent
00596     if (!atom->IsPermanent()) {
00597       PromoteToPermanent(atom);
00598     }
00599   } else {
00600     // otherwise, make a new atom
00601     atom = new (aString) PermanentAtomImpl();
00602     he->SetAtomImpl(atom);
00603     if ( !atom ) {
00604       PL_DHashTableRawRemove(&gAtomTable, he);
00605       return nsnull;
00606     }
00607   }
00608 
00609   NS_ADDREF(atom);
00610   return atom;
00611 }
00612 
00613 NS_COM nsIAtom* NS_NewAtom( const PRUnichar* str )
00614 {
00615   return NS_NewAtom(NS_ConvertUCS2toUTF8(str));
00616 }
00617 
00618 NS_COM nsIAtom* NS_NewPermanentAtom( const PRUnichar* str )
00619 {
00620   return NS_NewPermanentAtom(nsDependentString(str));
00621 }
00622 
00623 NS_COM nsrefcnt NS_GetNumberOfAtoms(void)
00624 {
00625   return gAtomTable.entryCount;
00626 }
00627