mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
d55796be17
Inline Create() and Realloc() so that we don't get negative leaks, since were that code end up in mozglue, it wouldn't have access to the logging machinery. Differential Revision: https://phabricator.services.mozilla.com/D209663
300 lines
10 KiB
C++
300 lines
10 KiB
C++
/* -*- 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;
|
|
}
|
|
|
|
mozilla::StringBuffer* 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<mozilla::StringBuffer>, 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<mozilla::StringBuffer> 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
|