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 <type_traits>
12 #include "mozilla/Atomics.h"
13 #include "mozilla/Char16.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "nsISupports.h"
20 } // namespace mozilla
25 // This class encompasses both static and dynamic atoms.
27 // - In places where static and dynamic atoms can be used, use RefPtr<nsAtom>.
28 // This is by far the most common case.
30 // - In places where only static atoms can appear, use nsStaticAtom* to avoid
31 // unnecessary refcounting. This is a moderately common case.
33 // - In places where only dynamic atoms can appear, it doesn't matter much
34 // whether you use RefPtr<nsAtom> or RefPtr<nsDynamicAtom>. This is an
35 // extremely rare case.
39 void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
,
40 mozilla::AtomsSizes
& aSizes
) const;
42 bool Equals(char16ptr_t aString
, uint32_t aLength
) const {
43 return mLength
== aLength
&&
44 memcmp(GetUTF16String(), aString
, mLength
* sizeof(char16_t
)) == 0;
47 bool Equals(const nsAString
& aString
) const {
48 return Equals(aString
.BeginReading(), aString
.Length());
51 bool IsStatic() const { return mIsStatic
; }
52 bool IsDynamic() const { return !IsStatic(); }
54 inline const nsStaticAtom
* AsStatic() const;
55 inline const nsDynamicAtom
* AsDynamic() const;
56 inline nsDynamicAtom
* AsDynamic();
58 char16ptr_t
GetUTF16String() const;
60 uint32_t GetLength() const { return mLength
; }
62 operator mozilla::Span
<const char16_t
>() const {
63 // Explicitly specify template argument here to avoid instantiating
64 // Span<char16_t> first and then implicitly converting to Span<const
66 return mozilla::Span
<const char16_t
>{GetUTF16String(), GetLength()};
69 void ToString(nsAString
& aString
) const;
70 void ToUTF8String(nsACString
& aString
) const;
72 // A hashcode that is better distributed than the actual atom pointer, for
73 // use in situations that need a well-distributed hashcode. It's called hash()
74 // rather than Hash() so we can use mozilla::BloomFilter<N, nsAtom>, because
75 // BloomFilter requires elements to implement a function called hash().
77 uint32_t hash() const { return mHash
; }
79 // This function returns true if ToLowercaseASCII would return the string
81 bool IsAsciiLowercase() const { return mIsAsciiLowercase
; }
83 // This function returns true if this is the empty atom. This is exactly
84 // equivalent to `this == nsGkAtoms::_empty`, but it's a bit less foot-gunny,
85 // since we also have `nsGkAtoms::empty`.
87 // Defined in nsGkAtoms.h
88 inline bool IsEmpty() const;
90 // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
91 // of this type is special.
92 inline MozExternalRefCountType
AddRef();
93 inline MozExternalRefCountType
Release();
95 using HasThreadSafeRefCnt
= std::true_type
;
98 // Used by nsStaticAtom.
99 constexpr nsAtom(uint32_t aLength
, uint32_t aHash
, bool aIsAsciiLowercase
)
102 mIsAsciiLowercase(aIsAsciiLowercase
),
105 // Used by nsDynamicAtom.
106 nsAtom(const nsAString
& aString
, uint32_t aHash
, bool aIsAsciiLowercase
)
107 : mLength(aString
.Length()),
109 mIsAsciiLowercase(aIsAsciiLowercase
),
114 const uint32_t mLength
: 30;
115 const uint32_t mIsStatic
: 1;
116 const uint32_t mIsAsciiLowercase
: 1;
117 const uint32_t mHash
;
120 // This class would be |final| if it wasn't for nsCSSAnonBoxPseudoStaticAtom
121 // and nsCSSPseudoElementStaticAtom, which are trivial subclasses used to
122 // ensure only certain static atoms are passed to certain functions.
123 class nsStaticAtom
: public nsAtom
{
125 // These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw
126 // nsStaticAtom pointers should be used instead.
127 MozExternalRefCountType
AddRef() = delete;
128 MozExternalRefCountType
Release() = delete;
130 // The static atom's precomputed hash value is an argument here, but it
131 // must be the same as would be computed by mozilla::HashString(aStr),
132 // which is what we use when atomizing strings. We compute this hash in
133 // Atom.py and assert in nsAtomTable::RegisterStaticAtoms that the two
135 constexpr nsStaticAtom(uint32_t aLength
, uint32_t aHash
,
136 uint32_t aStringOffset
, bool aIsAsciiLowercase
)
137 : nsAtom(aLength
, aHash
, aIsAsciiLowercase
),
138 mStringOffset(aStringOffset
) {}
140 const char16_t
* String() const {
141 return reinterpret_cast<const char16_t
*>(uintptr_t(this) - mStringOffset
);
144 already_AddRefed
<nsAtom
> ToAddRefed() {
145 return already_AddRefed
<nsAtom
>(static_cast<nsAtom
*>(this));
149 // This is an offset to the string chars, which must be at a lower address in
151 uint32_t mStringOffset
;
154 class nsDynamicAtom
: public nsAtom
{
156 // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
157 // of this type is special.
158 MozExternalRefCountType
AddRef() {
159 MOZ_ASSERT(int32_t(mRefCnt
) >= 0, "illegal refcnt");
160 nsrefcnt count
= ++mRefCnt
;
167 MozExternalRefCountType
Release() {
169 // We set a lower GC threshold for atoms in debug builds so that we exercise
170 // the GC machinery more often.
171 static const int32_t kAtomGCThreshold
= 20;
173 static const int32_t kAtomGCThreshold
= 10000;
176 MOZ_ASSERT(int32_t(mRefCnt
) > 0, "dup release");
177 nsrefcnt count
= --mRefCnt
;
179 if (++gUnusedAtomCount
>= kAtomGCThreshold
) {
187 const char16_t
* String() const {
188 return reinterpret_cast<const char16_t
*>(this + 1);
191 static nsDynamicAtom
* FromChars(char16_t
* chars
) {
192 return reinterpret_cast<nsDynamicAtom
*>(chars
) - 1;
196 friend class nsAtomTable
;
197 friend class nsAtomSubTable
;
198 friend int32_t NS_GetUnusedAtomCount();
200 static mozilla::Atomic
<int32_t, mozilla::ReleaseAcquire
> gUnusedAtomCount
;
201 static void GCAtomTable();
203 // These shouldn't be used directly, even by friend classes. The
204 // Create()/Destroy() methods use them.
205 nsDynamicAtom(const nsAString
& aString
, uint32_t aHash
,
206 bool aIsAsciiLowercase
);
207 ~nsDynamicAtom() = default;
209 static nsDynamicAtom
* Create(const nsAString
& aString
, uint32_t aHash
);
210 static void Destroy(nsDynamicAtom
* aAtom
);
212 mozilla::ThreadSafeAutoRefCnt mRefCnt
;
214 // The atom's chars are stored at the end of the struct.
217 const nsStaticAtom
* nsAtom::AsStatic() const {
218 MOZ_ASSERT(IsStatic());
219 return static_cast<const nsStaticAtom
*>(this);
222 const nsDynamicAtom
* nsAtom::AsDynamic() const {
223 MOZ_ASSERT(IsDynamic());
224 return static_cast<const nsDynamicAtom
*>(this);
227 nsDynamicAtom
* nsAtom::AsDynamic() {
228 MOZ_ASSERT(IsDynamic());
229 return static_cast<nsDynamicAtom
*>(this);
232 MozExternalRefCountType
nsAtom::AddRef() {
233 return IsStatic() ? 2 : AsDynamic()->AddRef();
236 MozExternalRefCountType
nsAtom::Release() {
237 return IsStatic() ? 1 : AsDynamic()->Release();
240 // The four forms of NS_Atomize (for use with |RefPtr<nsAtom>|) return the
241 // atom for the string given. At any given time there will always be one atom
242 // representing a given string. Atoms are intended to make string comparison
243 // cheaper by simplifying it to pointer equality. A pointer to the atom that
244 // does not own a reference is not guaranteed to be valid.
246 // Find an atom that matches the given UTF-8 string. The string is assumed to
247 // be zero terminated. Never returns null.
248 already_AddRefed
<nsAtom
> NS_Atomize(const char* aUTF8String
);
250 // Find an atom that matches the given UTF-8 string. Never returns null.
251 already_AddRefed
<nsAtom
> NS_Atomize(const nsACString
& aUTF8String
);
253 // Find an atom that matches the given UTF-16 string. The string is assumed to
254 // be zero terminated. Never returns null.
255 already_AddRefed
<nsAtom
> NS_Atomize(const char16_t
* aUTF16String
);
257 // Find an atom that matches the given UTF-16 string. Never returns null.
258 already_AddRefed
<nsAtom
> NS_Atomize(const nsAString
& aUTF16String
);
260 // An optimized version of the method above for the main thread.
261 already_AddRefed
<nsAtom
> NS_AtomizeMainThread(const nsAString
& aUTF16String
);
263 // Return a count of the total number of atoms currently alive in the system.
265 // Note that the result is imprecise and racy if other threads are currently
266 // operating on atoms. It's also slow, since it triggers a GC before counting.
267 // Currently this function is only used in tests, which should probably remain
269 nsrefcnt
NS_GetNumberOfAtoms();
271 // Return a pointer for a static atom for the string or null if there's no
272 // static atom for this string.
273 nsStaticAtom
* NS_GetStaticAtom(const nsAString
& aUTF16String
);
275 class nsAtomString
: public nsString
{
277 explicit nsAtomString(const nsAtom
* aAtom
) { aAtom
->ToString(*this); }
280 class nsAutoAtomString
: public nsAutoString
{
282 explicit nsAutoAtomString(const nsAtom
* aAtom
) { aAtom
->ToString(*this); }
285 class nsAtomCString
: public nsCString
{
287 explicit nsAtomCString(const nsAtom
* aAtom
) { aAtom
->ToUTF8String(*this); }
290 class nsAutoAtomCString
: public nsAutoCString
{
292 explicit nsAutoAtomCString(const nsAtom
* aAtom
) {
293 aAtom
->ToUTF8String(*this);
297 class nsDependentAtomString
: public nsDependentString
{
299 explicit nsDependentAtomString(const nsAtom
* aAtom
)
300 : nsDependentString(aAtom
->GetUTF16String(), aAtom
->GetLength()) {}
303 // Checks if the ascii chars in a given atom are already lowercase.
304 // If they are, no-op. Otherwise, converts all the ascii uppercase
305 // chars to lowercase and atomizes, storing the result in the inout
307 void ToLowerCaseASCII(RefPtr
<nsAtom
>& aAtom
);