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/. */
10 #include "nsISupportsImpl.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/UniquePtr.h"
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.
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()),
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
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
;
93 // Used by nsStaticAtom.
94 constexpr nsAtom(uint32_t aLength
, uint32_t aHash
, bool aIsAsciiLowercase
)
97 mIsAsciiLowercase(aIsAsciiLowercase
),
100 // Used by nsDynamicAtom.
101 nsAtom(const nsAString
& aString
, uint32_t aHash
, bool aIsAsciiLowercase
)
102 : mLength(aString
.Length()),
104 mIsAsciiLowercase(aIsAsciiLowercase
),
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
{
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
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));
144 // This is an offset to the string chars, which must be at a lower address in
146 uint32_t mStringOffset
;
149 class nsDynamicAtom
: public nsAtom
{
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
;
162 MozExternalRefCountType
Release() {
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;
168 static const int32_t kAtomGCThreshold
= 10000;
171 MOZ_ASSERT(int32_t(mRefCnt
) > 0, "dup release");
172 nsrefcnt count
= --mRefCnt
;
174 if (++gUnusedAtomCount
>= kAtomGCThreshold
) {
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;
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
>
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
);
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
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
{
274 explicit nsAtomString(const nsAtom
* aAtom
) { aAtom
->ToString(*this); }
277 class nsAtomCString
: public nsCString
{
279 explicit nsAtomCString(const nsAtom
* aAtom
) { aAtom
->ToUTF8String(*this); }
282 class nsDependentAtomString
: public nsDependentString
{
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
292 void ToLowerCaseASCII(RefPtr
<nsAtom
>& aAtom
);