xpcom/ds/nsAtomTable.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/ds/nsAtomTable.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,717 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +// vim:cindent:ts=2:et:sw=2:
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/Assertions.h"
    1.11 +#include "mozilla/Attributes.h"
    1.12 +#include "mozilla/Compiler.h"
    1.13 +#include "mozilla/HashFunctions.h"
    1.14 +#include "mozilla/MemoryReporting.h"
    1.15 +#include "mozilla/DebugOnly.h"
    1.16 +#include "mozilla/unused.h"
    1.17 +
    1.18 +#include "nsAtomTable.h"
    1.19 +#include "nsStaticAtom.h"
    1.20 +#include "nsString.h"
    1.21 +#include "nsCRT.h"
    1.22 +#include "pldhash.h"
    1.23 +#include "prenv.h"
    1.24 +#include "nsThreadUtils.h"
    1.25 +#include "nsDataHashtable.h"
    1.26 +#include "nsHashKeys.h"
    1.27 +#include "nsAutoPtr.h"
    1.28 +#include "nsUnicharUtils.h"
    1.29 +
    1.30 +using namespace mozilla;
    1.31 +
    1.32 +#if defined(__clang__)
    1.33 +#  pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
    1.34 +#elif MOZ_IS_GCC
    1.35 +#  if MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
    1.36 +#    pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
    1.37 +#  endif
    1.38 +#endif
    1.39 +
    1.40 +/**
    1.41 + * The shared hash table for atom lookups.
    1.42 + *
    1.43 + * XXX This should be manipulated in a threadsafe way or we should make
    1.44 + * sure it's only manipulated from the main thread.  Probably the latter
    1.45 + * is better, since the former would hurt performance.
    1.46 + *
    1.47 + * If |gAtomTable.ops| is 0, then the table is uninitialized.
    1.48 + */
    1.49 +static PLDHashTable gAtomTable;
    1.50 +
    1.51 +/**
    1.52 + * A hashtable of static atoms that existed at app startup. This hashtable helps 
    1.53 + * nsHtml5AtomTable.
    1.54 + */
    1.55 +static nsDataHashtable<nsStringHashKey, nsIAtom*>* gStaticAtomTable = 0;
    1.56 +
    1.57 +/**
    1.58 + * Whether it is still OK to add atoms to gStaticAtomTable.
    1.59 + */
    1.60 +static bool gStaticAtomTableSealed = false;
    1.61 +
    1.62 +//----------------------------------------------------------------------
    1.63 +
    1.64 +/**
    1.65 + * Note that AtomImpl objects are sometimes converted into PermanentAtomImpl
    1.66 + * objects using placement new and just overwriting the vtable pointer.
    1.67 + */
    1.68 +
    1.69 +class AtomImpl : public nsIAtom {
    1.70 +public:
    1.71 +  AtomImpl(const nsAString& aString, uint32_t aHash);
    1.72 +
    1.73 +  // This is currently only used during startup when creating a permanent atom
    1.74 +  // from NS_RegisterStaticAtoms
    1.75 +  AtomImpl(nsStringBuffer* aData, uint32_t aLength, uint32_t aHash);
    1.76 +
    1.77 +protected:
    1.78 +  // This is only intended to be used when a normal atom is turned into a
    1.79 +  // permanent one.
    1.80 +  AtomImpl() {
    1.81 +    // We can't really assert that mString is a valid nsStringBuffer string,
    1.82 +    // so do the best we can do and check for some consistencies.
    1.83 +    NS_ASSERTION((mLength + 1) * sizeof(char16_t) <=
    1.84 +                 nsStringBuffer::FromData(mString)->StorageSize() &&
    1.85 +                 mString[mLength] == 0,
    1.86 +                 "Not initialized atom");
    1.87 +  }
    1.88 +
    1.89 +  // We don't need a virtual destructor here because PermanentAtomImpl
    1.90 +  // deletions aren't handled through Release().
    1.91 +  ~AtomImpl();
    1.92 +
    1.93 +public:
    1.94 +  NS_DECL_ISUPPORTS
    1.95 +  NS_DECL_NSIATOM
    1.96 +
    1.97 +  enum { REFCNT_PERMANENT_SENTINEL = UINT32_MAX };
    1.98 +
    1.99 +  virtual bool IsPermanent();
   1.100 +
   1.101 +  // We can't use the virtual function in the base class destructor.
   1.102 +  bool IsPermanentInDestructor() {
   1.103 +    return mRefCnt == REFCNT_PERMANENT_SENTINEL;
   1.104 +  }
   1.105 +
   1.106 +  // for |#ifdef NS_BUILD_REFCNT_LOGGING| access to reference count
   1.107 +  nsrefcnt GetRefCount() { return mRefCnt; }
   1.108 +
   1.109 +  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   1.110 +};
   1.111 +
   1.112 +/**
   1.113 + * A non-refcounted implementation of nsIAtom.
   1.114 + */
   1.115 +
   1.116 +class PermanentAtomImpl MOZ_FINAL : public AtomImpl {
   1.117 +public:
   1.118 +  PermanentAtomImpl(const nsAString& aString, PLDHashNumber aKeyHash)
   1.119 +    : AtomImpl(aString, aKeyHash)
   1.120 +  {}
   1.121 +  PermanentAtomImpl(nsStringBuffer* aData, uint32_t aLength,
   1.122 +                    PLDHashNumber aKeyHash)
   1.123 +    : AtomImpl(aData, aLength, aKeyHash)
   1.124 +  {}
   1.125 +  PermanentAtomImpl()
   1.126 +  {}
   1.127 +
   1.128 +  ~PermanentAtomImpl();
   1.129 +  NS_IMETHOD_(MozExternalRefCountType) AddRef();
   1.130 +  NS_IMETHOD_(MozExternalRefCountType) Release();
   1.131 +
   1.132 +  virtual bool IsPermanent();
   1.133 +
   1.134 +  // SizeOfIncludingThis() isn't needed -- the one inherited from AtomImpl is
   1.135 +  // good enough, because PermanentAtomImpl doesn't add any new data members.
   1.136 +
   1.137 +  void* operator new(size_t size, AtomImpl* aAtom) CPP_THROW_NEW;
   1.138 +  void* operator new(size_t size) CPP_THROW_NEW
   1.139 +  {
   1.140 +    return ::operator new(size);
   1.141 +  }
   1.142 +};
   1.143 +
   1.144 +//----------------------------------------------------------------------
   1.145 +
   1.146 +struct AtomTableEntry : public PLDHashEntryHdr {
   1.147 +  AtomImpl* mAtom;
   1.148 +};
   1.149 +
   1.150 +struct AtomTableKey
   1.151 +{
   1.152 +  AtomTableKey(const char16_t* aUTF16String, uint32_t aLength,
   1.153 +               /*inout*/ uint32_t& aHash)
   1.154 +    : mUTF16String(aUTF16String),
   1.155 +      mUTF8String(nullptr),
   1.156 +      mLength(aLength)
   1.157 +  {
   1.158 +    if (aHash) {
   1.159 +      MOZ_ASSERT(aHash == HashString(mUTF16String, mLength));
   1.160 +      mHash = aHash;
   1.161 +    } else {
   1.162 +      UpdateHashKey();
   1.163 +      aHash = mHash;
   1.164 +    }
   1.165 +  }
   1.166 +
   1.167 +  AtomTableKey(const char* aUTF8String, uint32_t aLength,
   1.168 +               /*inout*/ uint32_t& aHash)
   1.169 +    : mUTF16String(nullptr),
   1.170 +      mUTF8String(aUTF8String),
   1.171 +      mLength(aLength)
   1.172 +  {
   1.173 +    if (aHash) {
   1.174 +      mozilla::DebugOnly<bool> err;
   1.175 +      MOZ_ASSERT(aHash == HashUTF8AsUTF16(mUTF8String, mLength, &err));
   1.176 +      mHash = aHash;
   1.177 +    } else {
   1.178 +      UpdateHashKey();
   1.179 +      aHash = mHash;
   1.180 +    }
   1.181 +  }
   1.182 +
   1.183 +  const char16_t* mUTF16String;
   1.184 +  const char* mUTF8String;
   1.185 +  uint32_t mLength;
   1.186 +  uint32_t mHash;
   1.187 +
   1.188 +  void UpdateHashKey()
   1.189 +  {
   1.190 +    if (mUTF8String) {
   1.191 +      bool err;
   1.192 +      mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
   1.193 +      if (err) {
   1.194 +        mUTF8String = nullptr;
   1.195 +        mLength = 0;
   1.196 +        mHash = 0;
   1.197 +      }
   1.198 +    } else {
   1.199 +      mHash = HashString(mUTF16String, mLength);
   1.200 +    }
   1.201 +  }
   1.202 +};
   1.203 +
   1.204 +static PLDHashNumber
   1.205 +AtomTableGetHash(PLDHashTable *table, const void *key)
   1.206 +{
   1.207 +  const AtomTableKey *k = static_cast<const AtomTableKey*>(key);
   1.208 +  return k->mHash;
   1.209 +}
   1.210 +
   1.211 +static bool
   1.212 +AtomTableMatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry,
   1.213 +                  const void *key)
   1.214 +{
   1.215 +  const AtomTableEntry *he = static_cast<const AtomTableEntry*>(entry);
   1.216 +  const AtomTableKey *k = static_cast<const AtomTableKey*>(key);
   1.217 +
   1.218 +  if (k->mUTF8String) {
   1.219 +    return
   1.220 +      CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
   1.221 +                                               k->mUTF8String + k->mLength),
   1.222 +                         nsDependentAtomString(he->mAtom)) == 0;
   1.223 +  }
   1.224 +
   1.225 +  uint32_t length = he->mAtom->GetLength();
   1.226 +  if (length != k->mLength) {
   1.227 +    return false;
   1.228 +  }
   1.229 +
   1.230 +  return memcmp(he->mAtom->GetUTF16String(),
   1.231 +                k->mUTF16String, length * sizeof(char16_t)) == 0;
   1.232 +}
   1.233 +
   1.234 +static void
   1.235 +AtomTableClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
   1.236 +{
   1.237 +  // Normal |AtomImpl| atoms are deleted when their refcount hits 0, and
   1.238 +  // they then remove themselves from the table.  In other words, they
   1.239 +  // are owned by the callers who own references to them.
   1.240 +  // |PermanentAtomImpl| permanent atoms ignore their refcount and are
   1.241 +  // deleted when they are removed from the table at table destruction.
   1.242 +  // In other words, they are owned by the atom table.
   1.243 +
   1.244 +  AtomImpl *atom = static_cast<AtomTableEntry*>(entry)->mAtom;
   1.245 +  if (atom->IsPermanent()) {
   1.246 +    // Note that the cast here is important since AtomImpls doesn't have a
   1.247 +    // virtual dtor.
   1.248 +    delete static_cast<PermanentAtomImpl*>(atom);
   1.249 +  }
   1.250 +}
   1.251 +
   1.252 +static bool
   1.253 +AtomTableInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
   1.254 +                   const void *key)
   1.255 +{
   1.256 +  static_cast<AtomTableEntry*>(entry)->mAtom = nullptr;
   1.257 +
   1.258 +  return true;
   1.259 +}
   1.260 +
   1.261 +
   1.262 +static const PLDHashTableOps AtomTableOps = {
   1.263 +  PL_DHashAllocTable,
   1.264 +  PL_DHashFreeTable,
   1.265 +  AtomTableGetHash,
   1.266 +  AtomTableMatchKey,
   1.267 +  PL_DHashMoveEntryStub,
   1.268 +  AtomTableClearEntry,
   1.269 +  PL_DHashFinalizeStub,
   1.270 +  AtomTableInitEntry
   1.271 +};
   1.272 +
   1.273 +
   1.274 +#ifdef DEBUG
   1.275 +static PLDHashOperator
   1.276 +DumpAtomLeaks(PLDHashTable *table, PLDHashEntryHdr *he,
   1.277 +              uint32_t index, void *arg)
   1.278 +{
   1.279 +  AtomTableEntry *entry = static_cast<AtomTableEntry*>(he);
   1.280 +  
   1.281 +  AtomImpl* atom = entry->mAtom;
   1.282 +  if (!atom->IsPermanent()) {
   1.283 +    ++*static_cast<uint32_t*>(arg);
   1.284 +    nsAutoCString str;
   1.285 +    atom->ToUTF8String(str);
   1.286 +    fputs(str.get(), stdout);
   1.287 +    fputs("\n", stdout);
   1.288 +  }
   1.289 +  return PL_DHASH_NEXT;
   1.290 +}
   1.291 +#endif
   1.292 +
   1.293 +static inline
   1.294 +void PromoteToPermanent(AtomImpl* aAtom)
   1.295 +{
   1.296 +#ifdef NS_BUILD_REFCNT_LOGGING
   1.297 +  {
   1.298 +    nsrefcnt refcount = aAtom->GetRefCount();
   1.299 +    do {
   1.300 +      NS_LOG_RELEASE(aAtom, --refcount, "AtomImpl");
   1.301 +    } while (refcount);
   1.302 +  }
   1.303 +#endif
   1.304 +  aAtom = new (aAtom) PermanentAtomImpl();
   1.305 +}
   1.306 +
   1.307 +void
   1.308 +NS_PurgeAtomTable()
   1.309 +{
   1.310 +  delete gStaticAtomTable;
   1.311 +
   1.312 +  if (gAtomTable.ops) {
   1.313 +#ifdef DEBUG
   1.314 +    const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
   1.315 +    if (dumpAtomLeaks && *dumpAtomLeaks) {
   1.316 +      uint32_t leaked = 0;
   1.317 +      printf("*** %d atoms still exist (including permanent):\n",
   1.318 +             gAtomTable.entryCount);
   1.319 +      PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
   1.320 +      printf("*** %u non-permanent atoms leaked\n", leaked);
   1.321 +    }
   1.322 +#endif
   1.323 +    PL_DHashTableFinish(&gAtomTable);
   1.324 +    gAtomTable.entryCount = 0;
   1.325 +    gAtomTable.ops = nullptr;
   1.326 +  }
   1.327 +}
   1.328 +
   1.329 +AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash)
   1.330 +{
   1.331 +  mLength = aString.Length();
   1.332 +  nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
   1.333 +  if (buf) {
   1.334 +    mString = static_cast<char16_t*>(buf->Data());
   1.335 +  } else {
   1.336 +    buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t));
   1.337 +    mString = static_cast<char16_t*>(buf->Data());
   1.338 +    CopyUnicodeTo(aString, 0, mString, mLength);
   1.339 +    mString[mLength] = char16_t(0);
   1.340 +  }
   1.341 +
   1.342 +  mHash = aHash;
   1.343 +  MOZ_ASSERT(mHash == HashString(mString, mLength));
   1.344 +
   1.345 +  NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
   1.346 +  NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t),
   1.347 +               "enough storage");
   1.348 +  NS_ASSERTION(Equals(aString), "correct data");
   1.349 +
   1.350 +  // Take ownership of buffer
   1.351 +  mozilla::unused << buf.forget();
   1.352 +}
   1.353 +
   1.354 +AtomImpl::AtomImpl(nsStringBuffer* aStringBuffer, uint32_t aLength,
   1.355 +                   uint32_t aHash)
   1.356 +{
   1.357 +  mLength = aLength;
   1.358 +  mString = static_cast<char16_t*>(aStringBuffer->Data());
   1.359 +  // Technically we could currently avoid doing this addref by instead making
   1.360 +  // the static atom buffers have an initial refcount of 2.
   1.361 +  aStringBuffer->AddRef();
   1.362 +
   1.363 +  mHash = aHash;
   1.364 +  MOZ_ASSERT(mHash == HashString(mString, mLength));
   1.365 +
   1.366 +  NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
   1.367 +  NS_ASSERTION(aStringBuffer &&
   1.368 +               aStringBuffer->StorageSize() == (mLength+1) * sizeof(char16_t),
   1.369 +               "correct storage");
   1.370 +}
   1.371 +
   1.372 +AtomImpl::~AtomImpl()
   1.373 +{
   1.374 +  NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
   1.375 +  // Permanent atoms are removed from the hashtable at shutdown, and we
   1.376 +  // don't want to remove them twice.  See comment above in
   1.377 +  // |AtomTableClearEntry|.
   1.378 +  if (!IsPermanentInDestructor()) {
   1.379 +    AtomTableKey key(mString, mLength, mHash);
   1.380 +    PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE);
   1.381 +    if (gAtomTable.entryCount == 0) {
   1.382 +      PL_DHashTableFinish(&gAtomTable);
   1.383 +      NS_ASSERTION(gAtomTable.entryCount == 0,
   1.384 +                   "PL_DHashTableFinish changed the entry count");
   1.385 +    }
   1.386 +  }
   1.387 +
   1.388 +  nsStringBuffer::FromData(mString)->Release();
   1.389 +}
   1.390 +
   1.391 +NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom)
   1.392 +
   1.393 +PermanentAtomImpl::~PermanentAtomImpl()
   1.394 +{
   1.395 +  // So we can tell if we were permanent while running the base class dtor.
   1.396 +  mRefCnt = REFCNT_PERMANENT_SENTINEL;
   1.397 +}
   1.398 +
   1.399 +NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::AddRef()
   1.400 +{
   1.401 +  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   1.402 +  return 2;
   1.403 +}
   1.404 +
   1.405 +NS_IMETHODIMP_(MozExternalRefCountType) PermanentAtomImpl::Release()
   1.406 +{
   1.407 +  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   1.408 +  return 1;
   1.409 +}
   1.410 +
   1.411 +/* virtual */ bool
   1.412 +AtomImpl::IsPermanent()
   1.413 +{
   1.414 +  return false;
   1.415 +}
   1.416 +
   1.417 +/* virtual */ bool
   1.418 +PermanentAtomImpl::IsPermanent()
   1.419 +{
   1.420 +  return true;
   1.421 +}
   1.422 +
   1.423 +void* PermanentAtomImpl::operator new ( size_t size, AtomImpl* aAtom ) CPP_THROW_NEW {
   1.424 +  MOZ_ASSERT(!aAtom->IsPermanent(),
   1.425 +             "converting atom that's already permanent");
   1.426 +
   1.427 +  // Just let the constructor overwrite the vtable pointer.
   1.428 +  return aAtom;
   1.429 +}
   1.430 +
   1.431 +NS_IMETHODIMP 
   1.432 +AtomImpl::ScriptableToString(nsAString& aBuf)
   1.433 +{
   1.434 +  nsStringBuffer::FromData(mString)->ToString(mLength, aBuf);
   1.435 +  return NS_OK;
   1.436 +}
   1.437 +
   1.438 +NS_IMETHODIMP
   1.439 +AtomImpl::ToUTF8String(nsACString& aBuf)
   1.440 +{
   1.441 +  CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
   1.442 +  return NS_OK;
   1.443 +}
   1.444 +
   1.445 +NS_IMETHODIMP_(bool)
   1.446 +AtomImpl::EqualsUTF8(const nsACString& aString)
   1.447 +{
   1.448 +  return CompareUTF8toUTF16(aString,
   1.449 +                            nsDependentString(mString, mLength)) == 0;
   1.450 +}
   1.451 +
   1.452 +NS_IMETHODIMP
   1.453 +AtomImpl::ScriptableEquals(const nsAString& aString, bool* aResult)
   1.454 +{
   1.455 +  *aResult = aString.Equals(nsDependentString(mString, mLength));
   1.456 +  return NS_OK;
   1.457 +}
   1.458 +
   1.459 +NS_IMETHODIMP_(bool)
   1.460 +AtomImpl::IsStaticAtom()
   1.461 +{
   1.462 +  return IsPermanent();
   1.463 +}
   1.464 +
   1.465 +size_t
   1.466 +AtomImpl::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.467 +{
   1.468 +  return aMallocSizeOf(this) +
   1.469 +         nsStringBuffer::FromData(mString)->
   1.470 +           SizeOfIncludingThisIfUnshared(aMallocSizeOf);
   1.471 +}
   1.472 +
   1.473 +//----------------------------------------------------------------------
   1.474 +
   1.475 +static size_t
   1.476 +SizeOfAtomTableEntryExcludingThis(PLDHashEntryHdr *aHdr,
   1.477 +                                  MallocSizeOf aMallocSizeOf,
   1.478 +                                  void *aArg)
   1.479 +{
   1.480 +  AtomTableEntry* entry = static_cast<AtomTableEntry*>(aHdr);
   1.481 +  return entry->mAtom->SizeOfIncludingThis(aMallocSizeOf);
   1.482 +}
   1.483 +
   1.484 +static size_t
   1.485 +SizeOfStaticAtomTableEntryExcludingThis(const nsAString& aKey,
   1.486 +                                        nsIAtom* const& aData,
   1.487 +                                        MallocSizeOf aMallocSizeOf,
   1.488 +                                        void* aArg)
   1.489 +{
   1.490 +  return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   1.491 +}
   1.492 +
   1.493 +size_t
   1.494 +NS_SizeOfAtomTablesIncludingThis(MallocSizeOf aMallocSizeOf) {
   1.495 +  size_t n = 0;
   1.496 +  if (gAtomTable.ops) {
   1.497 +      n += PL_DHashTableSizeOfExcludingThis(&gAtomTable,
   1.498 +                                            SizeOfAtomTableEntryExcludingThis,
   1.499 +                                            aMallocSizeOf);
   1.500 +  }
   1.501 +  if (gStaticAtomTable) {
   1.502 +    n += gStaticAtomTable->SizeOfIncludingThis(SizeOfStaticAtomTableEntryExcludingThis,
   1.503 +                                               aMallocSizeOf);
   1.504 +  }
   1.505 +  return n;
   1.506 +}
   1.507 +
   1.508 +#define ATOM_HASHTABLE_INITIAL_SIZE  4096
   1.509 +
   1.510 +static inline void
   1.511 +EnsureTableExists()
   1.512 +{
   1.513 +  if (!gAtomTable.ops) {
   1.514 +    PL_DHashTableInit(&gAtomTable, &AtomTableOps, 0,
   1.515 +                      sizeof(AtomTableEntry), ATOM_HASHTABLE_INITIAL_SIZE);
   1.516 +  }
   1.517 +}
   1.518 +
   1.519 +static inline AtomTableEntry*
   1.520 +GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t& aHash)
   1.521 +{
   1.522 +  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   1.523 +  EnsureTableExists();
   1.524 +  AtomTableKey key(aString, aLength, aHash);
   1.525 +  AtomTableEntry* e =
   1.526 +    static_cast<AtomTableEntry*>
   1.527 +               (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   1.528 +  if (!e) {
   1.529 +    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
   1.530 +  }
   1.531 +  return e;
   1.532 +}
   1.533 +
   1.534 +static inline AtomTableEntry*
   1.535 +GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t& aHash)
   1.536 +{
   1.537 +  MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   1.538 +  EnsureTableExists();
   1.539 +  AtomTableKey key(aString, aLength, aHash);
   1.540 +  AtomTableEntry* e =
   1.541 +    static_cast<AtomTableEntry*>
   1.542 +               (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   1.543 +  if (!e) {
   1.544 +    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
   1.545 +  }
   1.546 +  return e;
   1.547 +}
   1.548 +
   1.549 +class CheckStaticAtomSizes
   1.550 +{
   1.551 +  CheckStaticAtomSizes() {
   1.552 +    static_assert((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
   1.553 +                   sizeof(nsStringBuffer().mRefCount)) &&
   1.554 +                  (sizeof(nsFakeStringBuffer<1>().mSize) ==
   1.555 +                   sizeof(nsStringBuffer().mStorageSize)) &&
   1.556 +                  (offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
   1.557 +                   offsetof(nsStringBuffer, mRefCount)) &&
   1.558 +                  (offsetof(nsFakeStringBuffer<1>, mSize) ==
   1.559 +                   offsetof(nsStringBuffer, mStorageSize)) &&
   1.560 +                  (offsetof(nsFakeStringBuffer<1>, mStringData) ==
   1.561 +                   sizeof(nsStringBuffer)),
   1.562 +                  "mocked-up strings' representations should be compatible");
   1.563 +  }
   1.564 +};
   1.565 +
   1.566 +nsresult
   1.567 +RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
   1.568 +{
   1.569 +  // this does three things:
   1.570 +  // 1) wraps each static atom in a wrapper, if necessary
   1.571 +  // 2) initializes the address pointed to by each mBits slot
   1.572 +  // 3) puts the atom into the static atom table as well
   1.573 +  
   1.574 +  if (!gStaticAtomTable && !gStaticAtomTableSealed) {
   1.575 +    gStaticAtomTable = new nsDataHashtable<nsStringHashKey, nsIAtom*>();
   1.576 +  }
   1.577 +  
   1.578 +  for (uint32_t i=0; i<aAtomCount; i++) {
   1.579 +    NS_ASSERTION(nsCRT::IsAscii((char16_t*)aAtoms[i].mStringBuffer->Data()),
   1.580 +                 "Static atoms must be ASCII!");
   1.581 +
   1.582 +    uint32_t stringLen =
   1.583 +      aAtoms[i].mStringBuffer->StorageSize() / sizeof(char16_t) - 1;
   1.584 +
   1.585 +    uint32_t hash = 0;
   1.586 +    AtomTableEntry *he =
   1.587 +      GetAtomHashEntry((char16_t*)aAtoms[i].mStringBuffer->Data(),
   1.588 +                       stringLen, hash);
   1.589 +
   1.590 +    if (he->mAtom) {
   1.591 +      // there already is an atom with this name in the table.. but we
   1.592 +      // still have to update mBits
   1.593 +      if (!he->mAtom->IsPermanent()) {
   1.594 +        // since we wanted to create a static atom but there is
   1.595 +        // already one there, we convert it to a non-refcounting
   1.596 +        // permanent atom
   1.597 +        PromoteToPermanent(he->mAtom);
   1.598 +      }
   1.599 +      
   1.600 +      *aAtoms[i].mAtom = he->mAtom;
   1.601 +    }
   1.602 +    else {
   1.603 +      AtomImpl* atom = new PermanentAtomImpl(aAtoms[i].mStringBuffer,
   1.604 +                                             stringLen,
   1.605 +                                             hash);
   1.606 +      he->mAtom = atom;
   1.607 +      *aAtoms[i].mAtom = atom;
   1.608 +
   1.609 +      if (!gStaticAtomTableSealed) {
   1.610 +        gStaticAtomTable->Put(nsAtomString(atom), atom);
   1.611 +      }
   1.612 +    }
   1.613 +  }
   1.614 +  return NS_OK;
   1.615 +}
   1.616 +
   1.617 +already_AddRefed<nsIAtom>
   1.618 +NS_NewAtom(const char* aUTF8String)
   1.619 +{
   1.620 +  return NS_NewAtom(nsDependentCString(aUTF8String));
   1.621 +}
   1.622 +
   1.623 +already_AddRefed<nsIAtom>
   1.624 +NS_NewAtom(const nsACString& aUTF8String)
   1.625 +{
   1.626 +  uint32_t hash = 0;
   1.627 +  AtomTableEntry *he = GetAtomHashEntry(aUTF8String.Data(),
   1.628 +                                        aUTF8String.Length(),
   1.629 +                                        hash);
   1.630 +
   1.631 +  if (he->mAtom) {
   1.632 +    nsCOMPtr<nsIAtom> atom = he->mAtom;
   1.633 +
   1.634 +    return atom.forget();
   1.635 +  }
   1.636 +
   1.637 +  // This results in an extra addref/release of the nsStringBuffer.
   1.638 +  // Unfortunately there doesn't seem to be any APIs to avoid that.
   1.639 +  // Actually, now there is, sort of: ForgetSharedBuffer.
   1.640 +  nsString str;
   1.641 +  CopyUTF8toUTF16(aUTF8String, str);
   1.642 +  nsRefPtr<AtomImpl> atom = new AtomImpl(str, hash);
   1.643 +
   1.644 +  he->mAtom = atom;
   1.645 +
   1.646 +  return atom.forget();
   1.647 +}
   1.648 +
   1.649 +already_AddRefed<nsIAtom>
   1.650 +NS_NewAtom(const char16_t* aUTF16String)
   1.651 +{
   1.652 +  return NS_NewAtom(nsDependentString(aUTF16String));
   1.653 +}
   1.654 +
   1.655 +already_AddRefed<nsIAtom>
   1.656 +NS_NewAtom(const nsAString& aUTF16String)
   1.657 +{
   1.658 +  uint32_t hash = 0;
   1.659 +  AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(),
   1.660 +                                        aUTF16String.Length(),
   1.661 +                                        hash);
   1.662 +
   1.663 +  if (he->mAtom) {
   1.664 +    nsCOMPtr<nsIAtom> atom = he->mAtom;
   1.665 +
   1.666 +    return atom.forget();
   1.667 +  }
   1.668 +
   1.669 +  nsRefPtr<AtomImpl> atom = new AtomImpl(aUTF16String, hash);
   1.670 +  he->mAtom = atom;
   1.671 +
   1.672 +  return atom.forget();
   1.673 +}
   1.674 +
   1.675 +nsIAtom*
   1.676 +NS_NewPermanentAtom(const nsAString& aUTF16String)
   1.677 +{
   1.678 +  uint32_t hash = 0;
   1.679 +  AtomTableEntry *he = GetAtomHashEntry(aUTF16String.Data(),
   1.680 +                                        aUTF16String.Length(),
   1.681 +                                        hash);
   1.682 +
   1.683 +  AtomImpl* atom = he->mAtom;
   1.684 +  if (atom) {
   1.685 +    if (!atom->IsPermanent()) {
   1.686 +      PromoteToPermanent(atom);
   1.687 +    }
   1.688 +  }
   1.689 +  else {
   1.690 +    atom = new PermanentAtomImpl(aUTF16String, hash);
   1.691 +    he->mAtom = atom;
   1.692 +  }
   1.693 +
   1.694 +  // No need to addref since permanent atoms aren't refcounted anyway
   1.695 +  return atom;
   1.696 +}
   1.697 +
   1.698 +nsrefcnt
   1.699 +NS_GetNumberOfAtoms(void)
   1.700 +{
   1.701 +  return gAtomTable.entryCount;
   1.702 +}
   1.703 +
   1.704 +nsIAtom*
   1.705 +NS_GetStaticAtom(const nsAString& aUTF16String)
   1.706 +{
   1.707 +  NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
   1.708 +  NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
   1.709 +  nsIAtom* atom;
   1.710 +  if (!gStaticAtomTable->Get(aUTF16String, &atom)) {
   1.711 +    atom = nullptr;
   1.712 +  }
   1.713 +  return atom;
   1.714 +}
   1.715 +
   1.716 +void
   1.717 +NS_SealStaticAtomTable()
   1.718 +{
   1.719 +  gStaticAtomTableSealed = true;
   1.720 +}

mercurial