diff options
Diffstat (limited to 'xpcom/ds/nsAtom.h')
-rw-r--r-- | xpcom/ds/nsAtom.h | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/xpcom/ds/nsAtom.h b/xpcom/ds/nsAtom.h new file mode 100644 index 0000000000..aa416e81ef --- /dev/null +++ b/xpcom/ds/nsAtom.h @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAtom_h +#define nsAtom_h + +#include <type_traits> + +#include "mozilla/Atomics.h" +#include "mozilla/Char16.h" +#include "mozilla/MemoryReporting.h" +#include "nsISupports.h" +#include "nsString.h" + +namespace mozilla { +struct AtomsSizes; +} // namespace mozilla + +class nsStaticAtom; +class nsDynamicAtom; + +// This class encompasses both static and dynamic atoms. +// +// - In places where static and dynamic atoms can be used, use RefPtr<nsAtom>. +// This is by far the most common case. +// +// - In places where only static atoms can appear, use nsStaticAtom* to avoid +// unnecessary refcounting. This is a moderately common case. +// +// - In places where only dynamic atoms can appear, it doesn't matter much +// whether you use RefPtr<nsAtom> or RefPtr<nsDynamicAtom>. This is an +// extremely rare case. +// +class nsAtom { + public: + void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, + mozilla::AtomsSizes& aSizes) const; + + bool Equals(char16ptr_t aString, uint32_t aLength) const { + return mLength == aLength && + memcmp(GetUTF16String(), aString, mLength * sizeof(char16_t)) == 0; + } + + bool Equals(const nsAString& aString) const { + return Equals(aString.BeginReading(), aString.Length()); + } + + bool IsStatic() const { return mIsStatic; } + bool IsDynamic() const { return !IsStatic(); } + + inline const nsStaticAtom* AsStatic() const; + inline const nsDynamicAtom* AsDynamic() const; + inline nsDynamicAtom* AsDynamic(); + + char16ptr_t GetUTF16String() const; + + uint32_t GetLength() const { return mLength; } + + operator mozilla::Span<const char16_t>() const { + // Explicitly specify template argument here to avoid instantiating + // Span<char16_t> first and then implicitly converting to Span<const + // char16_t> + return mozilla::Span<const char16_t>{GetUTF16String(), GetLength()}; + } + + void ToString(nsAString& aString) const; + void ToUTF8String(nsACString& aString) const; + + // A hashcode that is better distributed than the actual atom pointer, for + // use in situations that need a well-distributed hashcode. It's called hash() + // rather than Hash() so we can use mozilla::BloomFilter<N, nsAtom>, because + // BloomFilter requires elements to implement a function called hash(). + // + uint32_t hash() const { return mHash; } + + // This function returns true if ToLowercaseASCII would return the string + // unchanged. + bool IsAsciiLowercase() const { return mIsAsciiLowercase; } + + // This function returns true if this is the empty atom. This is exactly + // equivalent to `this == nsGkAtoms::_empty`, but it's a bit less foot-gunny, + // since we also have `nsGkAtoms::empty`. + // + // Defined in nsGkAtoms.h + inline bool IsEmpty() const; + + // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting + // of this type is special. + inline MozExternalRefCountType AddRef(); + inline MozExternalRefCountType Release(); + + using HasThreadSafeRefCnt = std::true_type; + + protected: + constexpr nsAtom(uint32_t aLength, bool aIsStatic, uint32_t aHash, + bool aIsAsciiLowercase) + : mLength(aLength), + mIsStatic(aIsStatic), + mIsAsciiLowercase(aIsAsciiLowercase), + mHash(aHash) {} + + ~nsAtom() = default; + + const uint32_t mLength : 30; + const uint32_t mIsStatic : 1; + const uint32_t mIsAsciiLowercase : 1; + const uint32_t mHash; +}; + +// This class would be |final| if it wasn't for nsCSSAnonBoxPseudoStaticAtom +// and nsCSSPseudoElementStaticAtom, which are trivial subclasses used to +// ensure only certain static atoms are passed to certain functions. +class nsStaticAtom : public nsAtom { + public: + // These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw + // nsStaticAtom pointers should be used instead. + MozExternalRefCountType AddRef() = delete; + MozExternalRefCountType Release() = delete; + + // The static atom's precomputed hash value is an argument here, but it + // must be the same as would be computed by mozilla::HashString(aStr), + // which is what we use when atomizing strings. We compute this hash in + // Atom.py and assert in nsAtomTable::RegisterStaticAtoms that the two + // hashes match. + constexpr nsStaticAtom(uint32_t aLength, uint32_t aHash, + uint32_t aStringOffset, bool aIsAsciiLowercase) + : nsAtom(aLength, /* aIsStatic = */ true, aHash, aIsAsciiLowercase), + mStringOffset(aStringOffset) {} + + const char16_t* String() const { + return reinterpret_cast<const char16_t*>(uintptr_t(this) - mStringOffset); + } + + already_AddRefed<nsAtom> ToAddRefed() { + return already_AddRefed<nsAtom>(static_cast<nsAtom*>(this)); + } + + private: + // This is an offset to the string chars, which must be at a lower address in + // memory. + uint32_t mStringOffset; +}; + +class nsDynamicAtom : public nsAtom { + public: + // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting + // of this type is special. + MozExternalRefCountType AddRef() { + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt count = ++mRefCnt; + if (count == 1) { + gUnusedAtomCount--; + } + return count; + } + + MozExternalRefCountType Release() { +#ifdef DEBUG + // We set a lower GC threshold for atoms in debug builds so that we exercise + // the GC machinery more often. + static const int32_t kAtomGCThreshold = 20; +#else + static const int32_t kAtomGCThreshold = 10000; +#endif + + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + nsrefcnt count = --mRefCnt; + if (count == 0) { + if (++gUnusedAtomCount >= kAtomGCThreshold) { + GCAtomTable(); + } + } + + return count; + } + + nsStringBuffer* StringBuffer() const { return mStringBuffer; } + + const char16_t* String() const { + return reinterpret_cast<const char16_t*>(mStringBuffer->Data()); + } + + private: + friend class nsAtomTable; + friend class nsAtomSubTable; + friend int32_t NS_GetUnusedAtomCount(); + + static mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> gUnusedAtomCount; + static void GCAtomTable(); + + // These shouldn't be used directly, even by friend classes. The + // Create()/Destroy() methods use them. + nsDynamicAtom(already_AddRefed<nsStringBuffer>, uint32_t aLength, + uint32_t aHash, bool aIsAsciiLowercase); + ~nsDynamicAtom() = default; + + static nsDynamicAtom* Create(const nsAString& aString, uint32_t aHash); + static void Destroy(nsDynamicAtom* aAtom); + + mozilla::ThreadSafeAutoRefCnt mRefCnt; + RefPtr<nsStringBuffer> mStringBuffer; +}; + +const nsStaticAtom* nsAtom::AsStatic() const { + MOZ_ASSERT(IsStatic()); + return static_cast<const nsStaticAtom*>(this); +} + +const nsDynamicAtom* nsAtom::AsDynamic() const { + MOZ_ASSERT(IsDynamic()); + return static_cast<const nsDynamicAtom*>(this); +} + +nsDynamicAtom* nsAtom::AsDynamic() { + MOZ_ASSERT(IsDynamic()); + return static_cast<nsDynamicAtom*>(this); +} + +MozExternalRefCountType nsAtom::AddRef() { + return IsStatic() ? 2 : AsDynamic()->AddRef(); +} + +MozExternalRefCountType nsAtom::Release() { + return IsStatic() ? 1 : AsDynamic()->Release(); +} + +// The four forms of NS_Atomize (for use with |RefPtr<nsAtom>|) return the +// atom for the string given. At any given time there will always be one atom +// representing a given string. Atoms are intended to make string comparison +// cheaper by simplifying it to pointer equality. A pointer to the atom that +// does not own a reference is not guaranteed to be valid. + +// Find an atom that matches the given UTF-8 string. The string is assumed to +// be zero terminated. Never returns null. +already_AddRefed<nsAtom> NS_Atomize(const char* aUTF8String); + +// Find an atom that matches the given UTF-8 string. Never returns null. +already_AddRefed<nsAtom> NS_Atomize(const nsACString& aUTF8String); + +// Find an atom that matches the given UTF-16 string. The string is assumed to +// be zero terminated. Never returns null. +already_AddRefed<nsAtom> NS_Atomize(const char16_t* aUTF16String); + +// Find an atom that matches the given UTF-16 string. Never returns null. +already_AddRefed<nsAtom> NS_Atomize(const nsAString& aUTF16String); + +// Find an atom that matches the given UTF-16 string, with a known +// already-computed hash via mozilla::HashString. Never returns null. +already_AddRefed<nsAtom> NS_Atomize(const nsAString& aUTF16String, + uint32_t aKnownHash); + +// An optimized version of the method above for the main thread. +already_AddRefed<nsAtom> NS_AtomizeMainThread(const nsAString& aUTF16String); + +// Return a count of the total number of atoms currently alive in the system. +// +// Note that the result is imprecise and racy if other threads are currently +// operating on atoms. It's also slow, since it triggers a GC before counting. +// Currently this function is only used in tests, which should probably remain +// the case. +nsrefcnt NS_GetNumberOfAtoms(); + +// Return a pointer for a static atom for the string or null if there's no +// static atom for this string. +nsStaticAtom* NS_GetStaticAtom(const nsAString& aUTF16String); + +class nsAtomString : public nsString { + public: + explicit nsAtomString(const nsAtom* aAtom) { aAtom->ToString(*this); } +}; + +class nsAtomCString : public nsCString { + public: + explicit nsAtomCString(const nsAtom* aAtom) { aAtom->ToUTF8String(*this); } +}; + +class nsAutoAtomCString : public nsAutoCString { + public: + explicit nsAutoAtomCString(const nsAtom* aAtom) { + aAtom->ToUTF8String(*this); + } +}; + +class nsDependentAtomString : public nsDependentString { + public: + explicit nsDependentAtomString(const nsAtom* aAtom) + : nsDependentString(aAtom->GetUTF16String(), aAtom->GetLength()) {} +}; + +// Checks if the ascii chars in a given atom are already lowercase. +// If they are, no-op. Otherwise, converts all the ascii uppercase +// chars to lowercase and atomizes, storing the result in the inout +// param. +void ToLowerCaseASCII(RefPtr<nsAtom>& aAtom); + +#endif // nsAtom_h |