Bug 1609862 - Display more detailed memory info in resource usage report. r=froydnj
[gecko.git] / xpcom / ds / nsAtom.h
blob7b73fd4a48cc429489da00bdd8da79fe70b49156
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsAtom_h
8 #define nsAtom_h
10 #include "nsISupportsImpl.h"
11 #include "nsString.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/UniquePtr.h"
15 namespace mozilla {
16 struct AtomsSizes;
19 class nsStaticAtom;
20 class nsDynamicAtom;
22 // This class encompasses both static and dynamic atoms.
24 // - In places where static and dynamic atoms can be used, use RefPtr<nsAtom>.
25 // This is by far the most common case.
27 // - In places where only static atoms can appear, use nsStaticAtom* to avoid
28 // unnecessary refcounting. This is a moderately common case.
30 // - In places where only dynamic atoms can appear, it doesn't matter much
31 // whether you use RefPtr<nsAtom> or RefPtr<nsDynamicAtom>. This is an
32 // extremely rare case.
34 class nsAtom {
35 public:
36 void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
37 mozilla::AtomsSizes& aSizes) const;
39 bool Equals(char16ptr_t aString, uint32_t aLength) const {
40 return mLength == aLength &&
41 memcmp(GetUTF16String(), aString, mLength * sizeof(char16_t)) == 0;
44 bool Equals(const nsAString& aString) const {
45 return Equals(aString.BeginReading(), aString.Length());
48 bool IsStatic() const { return mIsStatic; }
49 bool IsDynamic() const { return !IsStatic(); }
51 inline const nsStaticAtom* AsStatic() const;
52 inline const nsDynamicAtom* AsDynamic() const;
53 inline nsDynamicAtom* AsDynamic();
55 char16ptr_t GetUTF16String() const;
57 uint32_t GetLength() const { return mLength; }
59 operator mozilla::Span<const char16_t>() const {
60 return mozilla::MakeSpan(static_cast<const char16_t*>(GetUTF16String()),
61 GetLength());
64 void ToString(nsAString& aString) const;
65 void ToUTF8String(nsACString& aString) const;
67 // A hashcode that is better distributed than the actual atom pointer, for
68 // use in situations that need a well-distributed hashcode. It's called hash()
69 // rather than Hash() so we can use mozilla::BloomFilter<N, nsAtom>, because
70 // BloomFilter requires elements to implement a function called hash().
72 uint32_t hash() const { return mHash; }
74 // This function returns true if ToLowercaseASCII would return the string
75 // unchanged.
76 bool IsAsciiLowercase() const { return mIsAsciiLowercase; }
78 // This function returns true if this is the empty atom. This is exactly
79 // equivalent to `this == nsGkAtoms::_empty`, but it's a bit less foot-gunny,
80 // since we also have `nsGkAtoms::empty`.
82 // Defined in nsGkAtoms.h
83 inline bool IsEmpty() const;
85 // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
86 // of this type is special.
87 inline MozExternalRefCountType AddRef();
88 inline MozExternalRefCountType Release();
90 typedef mozilla::TrueType HasThreadSafeRefCnt;
92 protected:
93 // Used by nsStaticAtom.
94 constexpr nsAtom(uint32_t aLength, uint32_t aHash, bool aIsAsciiLowercase)
95 : mLength(aLength),
96 mIsStatic(true),
97 mIsAsciiLowercase(aIsAsciiLowercase),
98 mHash(aHash) {}
100 // Used by nsDynamicAtom.
101 nsAtom(const nsAString& aString, uint32_t aHash, bool aIsAsciiLowercase)
102 : mLength(aString.Length()),
103 mIsStatic(false),
104 mIsAsciiLowercase(aIsAsciiLowercase),
105 mHash(aHash) {}
107 ~nsAtom() = default;
109 const uint32_t mLength : 30;
110 const uint32_t mIsStatic : 1;
111 const uint32_t mIsAsciiLowercase : 1;
112 const uint32_t mHash;
115 // This class would be |final| if it wasn't for nsCSSAnonBoxPseudoStaticAtom
116 // and nsCSSPseudoElementStaticAtom, which are trivial subclasses used to
117 // ensure only certain static atoms are passed to certain functions.
118 class nsStaticAtom : public nsAtom {
119 public:
120 // These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw
121 // nsStaticAtom pointers should be used instead.
122 MozExternalRefCountType AddRef() = delete;
123 MozExternalRefCountType Release() = delete;
125 // The static atom's precomputed hash value is an argument here, but it
126 // must be the same as would be computed by mozilla::HashString(aStr),
127 // which is what we use when atomizing strings. We compute this hash in
128 // Atom.py and assert in nsAtomTable::RegisterStaticAtoms that the two
129 // hashes match.
130 constexpr nsStaticAtom(uint32_t aLength, uint32_t aHash,
131 uint32_t aStringOffset, bool aIsAsciiLowercase)
132 : nsAtom(aLength, aHash, aIsAsciiLowercase),
133 mStringOffset(aStringOffset) {}
135 const char16_t* String() const {
136 return reinterpret_cast<const char16_t*>(uintptr_t(this) - mStringOffset);
139 already_AddRefed<nsAtom> ToAddRefed() {
140 return already_AddRefed<nsAtom>(static_cast<nsAtom*>(this));
143 private:
144 // This is an offset to the string chars, which must be at a lower address in
145 // memory.
146 uint32_t mStringOffset;
149 class nsDynamicAtom : public nsAtom {
150 public:
151 // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
152 // of this type is special.
153 MozExternalRefCountType AddRef() {
154 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
155 nsrefcnt count = ++mRefCnt;
156 if (count == 1) {
157 gUnusedAtomCount--;
159 return count;
162 MozExternalRefCountType Release() {
163 #ifdef DEBUG
164 // We set a lower GC threshold for atoms in debug builds so that we exercise
165 // the GC machinery more often.
166 static const int32_t kAtomGCThreshold = 20;
167 #else
168 static const int32_t kAtomGCThreshold = 10000;
169 #endif
171 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
172 nsrefcnt count = --mRefCnt;
173 if (count == 0) {
174 if (++gUnusedAtomCount >= kAtomGCThreshold) {
175 GCAtomTable();
179 return count;
182 const char16_t* String() const {
183 return reinterpret_cast<const char16_t*>(this + 1);
186 static nsDynamicAtom* FromChars(char16_t* chars) {
187 return reinterpret_cast<nsDynamicAtom*>(chars) - 1;
190 private:
191 friend class nsAtomTable;
192 friend class nsAtomSubTable;
193 friend int32_t NS_GetUnusedAtomCount();
195 static mozilla::Atomic<int32_t, mozilla::ReleaseAcquire,
196 mozilla::recordreplay::Behavior::DontPreserve>
197 gUnusedAtomCount;
198 static void GCAtomTable();
200 // These shouldn't be used directly, even by friend classes. The
201 // Create()/Destroy() methods use them.
202 nsDynamicAtom(const nsAString& aString, uint32_t aHash,
203 bool aIsAsciiLowercase);
204 ~nsDynamicAtom() {}
206 static nsDynamicAtom* Create(const nsAString& aString, uint32_t aHash);
207 static void Destroy(nsDynamicAtom* aAtom);
209 mozilla::ThreadSafeAutoRefCnt mRefCnt;
211 // The atom's chars are stored at the end of the struct.
214 const nsStaticAtom* nsAtom::AsStatic() const {
215 MOZ_ASSERT(IsStatic());
216 return static_cast<const nsStaticAtom*>(this);
219 const nsDynamicAtom* nsAtom::AsDynamic() const {
220 MOZ_ASSERT(IsDynamic());
221 return static_cast<const nsDynamicAtom*>(this);
224 nsDynamicAtom* nsAtom::AsDynamic() {
225 MOZ_ASSERT(IsDynamic());
226 return static_cast<nsDynamicAtom*>(this);
229 MozExternalRefCountType nsAtom::AddRef() {
230 return IsStatic() ? 2 : AsDynamic()->AddRef();
233 MozExternalRefCountType nsAtom::Release() {
234 return IsStatic() ? 1 : AsDynamic()->Release();
237 // The four forms of NS_Atomize (for use with |RefPtr<nsAtom>|) return the
238 // atom for the string given. At any given time there will always be one atom
239 // representing a given string. Atoms are intended to make string comparison
240 // cheaper by simplifying it to pointer equality. A pointer to the atom that
241 // does not own a reference is not guaranteed to be valid.
243 // Find an atom that matches the given UTF-8 string. The string is assumed to
244 // be zero terminated. Never returns null.
245 already_AddRefed<nsAtom> NS_Atomize(const char* aUTF8String);
247 // Find an atom that matches the given UTF-8 string. Never returns null.
248 already_AddRefed<nsAtom> NS_Atomize(const nsACString& aUTF8String);
250 // Find an atom that matches the given UTF-16 string. The string is assumed to
251 // be zero terminated. Never returns null.
252 already_AddRefed<nsAtom> NS_Atomize(const char16_t* aUTF16String);
254 // Find an atom that matches the given UTF-16 string. Never returns null.
255 already_AddRefed<nsAtom> NS_Atomize(const nsAString& aUTF16String);
257 // An optimized version of the method above for the main thread.
258 already_AddRefed<nsAtom> NS_AtomizeMainThread(const nsAString& aUTF16String);
260 // Return a count of the total number of atoms currently alive in the system.
262 // Note that the result is imprecise and racy if other threads are currently
263 // operating on atoms. It's also slow, since it triggers a GC before counting.
264 // Currently this function is only used in tests, which should probably remain
265 // the case.
266 nsrefcnt NS_GetNumberOfAtoms();
268 // Return a pointer for a static atom for the string or null if there's no
269 // static atom for this string.
270 nsStaticAtom* NS_GetStaticAtom(const nsAString& aUTF16String);
272 class nsAtomString : public nsString {
273 public:
274 explicit nsAtomString(const nsAtom* aAtom) { aAtom->ToString(*this); }
277 class nsAtomCString : public nsCString {
278 public:
279 explicit nsAtomCString(const nsAtom* aAtom) { aAtom->ToUTF8String(*this); }
282 class nsDependentAtomString : public nsDependentString {
283 public:
284 explicit nsDependentAtomString(const nsAtom* aAtom)
285 : nsDependentString(aAtom->GetUTF16String(), aAtom->GetLength()) {}
288 // Checks if the ascii chars in a given atom are already lowercase.
289 // If they are, no-op. Otherwise, converts all the ascii uppercase
290 // chars to lowercase and atomizes, storing the result in the inout
291 // param.
292 void ToLowerCaseASCII(RefPtr<nsAtom>& aAtom);
294 #endif // nsAtom_h