/rust/registry/src/index.crates.io-1949cf8c6b5b557f/string_cache-0.11.0/src/atom.rs
Line | Count | Source |
1 | | // Copyright 2014 The Servo Project Developers. See the COPYRIGHT |
2 | | // file at the top-level directory of this distribution. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
5 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
6 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
7 | | // option. This file may not be copied, modified, or distributed |
8 | | // except according to those terms. |
9 | | |
10 | | use crate::dynamic_set::{Entry, dynamic_set}; |
11 | | use crate::static_sets::StaticAtomSet; |
12 | | use debug_unreachable::debug_unreachable; |
13 | | |
14 | | use std::borrow::Cow; |
15 | | use std::cmp::Ordering::{self, Equal}; |
16 | | use std::fmt; |
17 | | use std::hash::{Hash, Hasher}; |
18 | | use std::marker::PhantomData; |
19 | | use std::mem; |
20 | | use std::num::NonZeroU64; |
21 | | use std::ops; |
22 | | use std::slice; |
23 | | use std::str; |
24 | | use std::sync::atomic::Ordering::SeqCst; |
25 | | |
26 | | const DYNAMIC_TAG: u8 = 0b_00; |
27 | | const INLINE_TAG: u8 = 0b_01; // len in upper nybble |
28 | | const STATIC_TAG: u8 = 0b_10; |
29 | | const TAG_MASK: u64 = 0b_11; |
30 | | |
31 | | /// With alignment, a `*const Entry` pointer always has zeroes in its lowest `TAG_BITS` bits |
32 | | const _: () = assert!(mem::align_of::<Entry>() >= TAG_MASK.next_power_of_two() as usize); |
33 | | |
34 | | const LEN_OFFSET: u64 = 4; |
35 | | const LEN_MASK: u64 = 0xF0; |
36 | | |
37 | | const MAX_INLINE_LEN: usize = 7; |
38 | | const STATIC_SHIFT_BITS: usize = 32; |
39 | | |
40 | | /// Represents a string that has been interned. |
41 | | /// |
42 | | /// While the type definition for `Atom` indicates that it generic on a particular |
43 | | /// implementation of an atom set, you don't need to worry about this. Atoms can be static |
44 | | /// and come from a `StaticAtomSet` generated by the `string_cache_codegen` crate, or they |
45 | | /// can be dynamic and created by you on an `EmptyStaticAtomSet`. |
46 | | /// |
47 | | /// `Atom` implements `Clone` but not `Copy`, since internally atoms are reference-counted; |
48 | | /// this means that you may need to `.clone()` an atom to keep copies to it in different |
49 | | /// places, or when passing it to a function that takes an `Atom` rather than an `&Atom`. |
50 | | /// |
51 | | /// ## Creating an atom at runtime |
52 | | /// |
53 | | /// If you use `string_cache_codegen` to generate a precomputed list of atoms, your code |
54 | | /// may then do something like read data from somewhere and extract tokens that need to be |
55 | | /// compared to the atoms. In this case, you can use `Atom::from(&str)` or |
56 | | /// `Atom::from(String)`. These create a reference-counted atom which will be |
57 | | /// automatically freed when all references to it are dropped. |
58 | | /// |
59 | | /// This means that your application can safely have a loop which tokenizes data, creates |
60 | | /// atoms from the tokens, and compares the atoms to a predefined set of keywords, without |
61 | | /// running the risk of arbitrary memory consumption from creating large numbers of atoms — |
62 | | /// as long as your application does not store clones of the atoms it creates along the |
63 | | /// way. |
64 | | /// |
65 | | /// For example, the following is safe and will not consume arbitrary amounts of memory: |
66 | | /// |
67 | | /// ```ignore |
68 | | /// let untrusted_data = "large amounts of text ..."; |
69 | | /// |
70 | | /// for token in untrusted_data.split_whitespace() { |
71 | | /// let atom = Atom::from(token); // interns the string |
72 | | /// |
73 | | /// if atom == Atom::from("keyword") { |
74 | | /// // handle that keyword |
75 | | /// } else if atom == Atom::from("another_keyword") { |
76 | | /// // handle that keyword |
77 | | /// } else { |
78 | | /// println!("unknown keyword"); |
79 | | /// } |
80 | | /// } // atom is dropped here, so it is not kept around in memory |
81 | | /// ``` |
82 | | /// |
83 | | /// ## Internal representation |
84 | | /// |
85 | | /// An `Atom` is always 64 bits / 8 bytes. |
86 | | /// The least-significant two bits form a tag to distinguish three different representations: |
87 | | /// |
88 | | /// * `0b01`: A short string up to 7 bytes, stored inline in most-significant 56 bits. |
89 | | /// Bits #4 to #7 (the upper nibble of the lower byte) are the length of the string. |
90 | | /// * `0b10`: A string part of a statically-known indexed set with [perfect hashing]. |
91 | | /// The most-significant 32 bits are the index in the set. |
92 | | /// * `0b00`: For other cases, the entire 64 bits are a heap-allocated pointer |
93 | | /// to an entry in a global hash map. |
94 | | /// Alignment of the allocation ensures the tag bits are indeed zero. |
95 | | /// The entry is atomically reference-counted. |
96 | | /// It is removed from the map and deallocated when its last `Atom` is dropped. |
97 | | /// The map exists so that interning the same string again gives another pointer to the same entry. |
98 | | /// |
99 | | /// In all cases, shallow 64-bit equality is equivalent to string equality. |
100 | | /// |
101 | | /// [perfect hashing]: https://docs.rs/phf/latest/phf/ |
102 | | #[derive(PartialEq, Eq)] |
103 | | // NOTE: Deriving PartialEq requires that a given string must always be interned the same way. |
104 | | pub struct Atom<Static> { |
105 | | unsafe_data: NonZeroU64, |
106 | | phantom: PhantomData<Static>, |
107 | | } |
108 | | |
109 | | /// Static and inline atoms don’t have any allocated space. Dynamic atoms do but |
110 | | /// are accounted for by [`malloc_size_of_dynamic_set`][crate::malloc_size_of_dynamic_set]. |
111 | | #[cfg(feature = "malloc_size_of")] |
112 | | impl<Static: StaticAtomSet> malloc_size_of::MallocSizeOf for Atom<Static> { |
113 | | fn size_of(&self, _ops: &mut malloc_size_of::MallocSizeOfOps) -> usize { |
114 | | 0 |
115 | | } |
116 | | } |
117 | | |
118 | | // FIXME: bound removed from the struct definition before of this error for pack_static: |
119 | | // "error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable" |
120 | | // https://github.com/rust-lang/rust/issues/57563 |
121 | | impl<Static> Atom<Static> { |
122 | | /// For the atom!() macros |
123 | | #[inline(always)] |
124 | | #[doc(hidden)] |
125 | 39.2k | pub const fn pack_static(n: u32) -> Self { |
126 | 39.2k | Self { |
127 | 39.2k | unsafe_data: unsafe { |
128 | 39.2k | // STATIC_TAG ensures this is non-zero |
129 | 39.2k | NonZeroU64::new_unchecked((STATIC_TAG as u64) | ((n as u64) << STATIC_SHIFT_BITS)) |
130 | 39.2k | }, |
131 | 39.2k | phantom: PhantomData, |
132 | 39.2k | } |
133 | 39.2k | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::pack_static <string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::pack_static Line | Count | Source | 125 | 36.2k | pub const fn pack_static(n: u32) -> Self { | 126 | 36.2k | Self { | 127 | 36.2k | unsafe_data: unsafe { | 128 | 36.2k | // STATIC_TAG ensures this is non-zero | 129 | 36.2k | NonZeroU64::new_unchecked((STATIC_TAG as u64) | ((n as u64) << STATIC_SHIFT_BITS)) | 130 | 36.2k | }, | 131 | 36.2k | phantom: PhantomData, | 132 | 36.2k | } | 133 | 36.2k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::pack_static Line | Count | Source | 125 | 3.02k | pub const fn pack_static(n: u32) -> Self { | 126 | 3.02k | Self { | 127 | 3.02k | unsafe_data: unsafe { | 128 | 3.02k | // STATIC_TAG ensures this is non-zero | 129 | 3.02k | NonZeroU64::new_unchecked((STATIC_TAG as u64) | ((n as u64) << STATIC_SHIFT_BITS)) | 130 | 3.02k | }, | 131 | 3.02k | phantom: PhantomData, | 132 | 3.02k | } | 133 | 3.02k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::pack_static |
134 | | |
135 | | /// For the atom!() macros |
136 | | #[inline(always)] |
137 | | #[doc(hidden)] |
138 | 0 | pub const fn pack_inline(mut n: u64, len: u8) -> Self { |
139 | 0 | if cfg!(target_endian = "big") { |
140 | 0 | // Reverse order of top 7 bytes. |
141 | 0 | // Bottom 8 bits of `n` are zero, and we need that to remain so. |
142 | 0 | // String data is stored in top 7 bytes, tag and length in bottom byte. |
143 | 0 | n = n.to_le() << 8; |
144 | 0 | } |
145 | | |
146 | 0 | let data: u64 = (INLINE_TAG as u64) | ((len as u64) << LEN_OFFSET) | n; |
147 | 0 | Self { |
148 | 0 | // INLINE_TAG ensures this is never zero |
149 | 0 | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, |
150 | 0 | phantom: PhantomData, |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | 9.84M | fn tag(&self) -> u8 { |
155 | 9.84M | (self.unsafe_data.get() & TAG_MASK) as u8 |
156 | 9.84M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::tag Line | Count | Source | 154 | 1.08M | fn tag(&self) -> u8 { | 155 | 1.08M | (self.unsafe_data.get() & TAG_MASK) as u8 | 156 | 1.08M | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::tag Line | Count | Source | 154 | 4.20M | fn tag(&self) -> u8 { | 155 | 4.20M | (self.unsafe_data.get() & TAG_MASK) as u8 | 156 | 4.20M | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::tag Line | Count | Source | 154 | 4.55M | fn tag(&self) -> u8 { | 155 | 4.55M | (self.unsafe_data.get() & TAG_MASK) as u8 | 156 | 4.55M | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::tag |
157 | | |
158 | | /// Assuming DYNAMIC_TAG, return the pointer to `Entry` |
159 | 2.46M | fn dynamic_ptr(&self) -> *const Entry { |
160 | 2.46M | std::ptr::with_exposed_provenance(self.unsafe_data.get() as usize) |
161 | 2.46M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::dynamic_ptr Line | Count | Source | 159 | 131 | fn dynamic_ptr(&self) -> *const Entry { | 160 | 131 | std::ptr::with_exposed_provenance(self.unsafe_data.get() as usize) | 161 | 131 | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::dynamic_ptr Line | Count | Source | 159 | 362k | fn dynamic_ptr(&self) -> *const Entry { | 160 | 362k | std::ptr::with_exposed_provenance(self.unsafe_data.get() as usize) | 161 | 362k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::dynamic_ptr Line | Count | Source | 159 | 2.10M | fn dynamic_ptr(&self) -> *const Entry { | 160 | 2.10M | std::ptr::with_exposed_provenance(self.unsafe_data.get() as usize) | 161 | 2.10M | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::dynamic_ptr |
162 | | } |
163 | | |
164 | | impl<Static: StaticAtomSet> Atom<Static> { |
165 | | /// Return the internal representation. For testing. |
166 | | #[doc(hidden)] |
167 | 1.46M | pub fn unsafe_data(&self) -> u64 { |
168 | 1.46M | self.unsafe_data.get() |
169 | 1.46M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::unsafe_data Line | Count | Source | 167 | 397k | pub fn unsafe_data(&self) -> u64 { | 168 | 397k | self.unsafe_data.get() | 169 | 397k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::unsafe_data Line | Count | Source | 167 | 767k | pub fn unsafe_data(&self) -> u64 { | 168 | 767k | self.unsafe_data.get() | 169 | 767k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::unsafe_data Line | Count | Source | 167 | 298k | pub fn unsafe_data(&self) -> u64 { | 168 | 298k | self.unsafe_data.get() | 169 | 298k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::unsafe_data |
170 | | |
171 | | /// Return true if this is a static Atom. For testing. |
172 | | #[doc(hidden)] |
173 | 0 | pub fn is_static(&self) -> bool { |
174 | 0 | self.tag() == STATIC_TAG |
175 | 0 | } |
176 | | |
177 | | /// Return true if this is a dynamic Atom. For testing. |
178 | | #[doc(hidden)] |
179 | 0 | pub fn is_dynamic(&self) -> bool { |
180 | 0 | self.tag() == DYNAMIC_TAG |
181 | 0 | } |
182 | | |
183 | | /// Return true if this is an inline Atom. For testing. |
184 | | #[doc(hidden)] |
185 | 0 | pub fn is_inline(&self) -> bool { |
186 | 0 | self.tag() == INLINE_TAG |
187 | 0 | } |
188 | | |
189 | 105k | fn static_index(&self) -> u64 { |
190 | 105k | self.unsafe_data.get() >> STATIC_SHIFT_BITS |
191 | 105k | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::static_index <string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::static_index Line | Count | Source | 189 | 53.1k | fn static_index(&self) -> u64 { | 190 | 53.1k | self.unsafe_data.get() >> STATIC_SHIFT_BITS | 191 | 53.1k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::static_index Line | Count | Source | 189 | 52.7k | fn static_index(&self) -> u64 { | 190 | 52.7k | self.unsafe_data.get() >> STATIC_SHIFT_BITS | 191 | 52.7k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::static_index |
192 | | |
193 | | /// Returns a hash of the string |
194 | | /// |
195 | | /// For static or dynamic atoms, it is a pre-computed high-quality hash. |
196 | | /// |
197 | | /// For inline atoms however (short strings 7 bytes or less), |
198 | | /// the returned value is the literal inline representation |
199 | | /// with string bytes packed directly in the `u64` value, |
200 | | /// which makes it a relatively poor-quality hash if used directly. |
201 | 221k | pub fn get_hash(&self) -> u64 { |
202 | 221k | match self.tag() { |
203 | | DYNAMIC_TAG => { |
204 | 59.3k | let entry = self.dynamic_ptr(); |
205 | 59.3k | unsafe { (*entry).hash } |
206 | | } |
207 | 335 | STATIC_TAG => Static::get().hashes[self.static_index() as usize], |
208 | 162k | INLINE_TAG => self.unsafe_data.get(), |
209 | 0 | _ => unsafe { debug_unreachable!() }, |
210 | | } |
211 | 221k | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::get_hash <string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::get_hash Line | Count | Source | 201 | 110k | pub fn get_hash(&self) -> u64 { | 202 | 110k | match self.tag() { | 203 | | DYNAMIC_TAG => { | 204 | 7.55k | let entry = self.dynamic_ptr(); | 205 | 7.55k | unsafe { (*entry).hash } | 206 | | } | 207 | 225 | STATIC_TAG => Static::get().hashes[self.static_index() as usize], | 208 | 103k | INLINE_TAG => self.unsafe_data.get(), | 209 | 0 | _ => unsafe { debug_unreachable!() }, | 210 | | } | 211 | 110k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::get_hash Line | Count | Source | 201 | 110k | pub fn get_hash(&self) -> u64 { | 202 | 110k | match self.tag() { | 203 | | DYNAMIC_TAG => { | 204 | 51.8k | let entry = self.dynamic_ptr(); | 205 | 51.8k | unsafe { (*entry).hash } | 206 | | } | 207 | 110 | STATIC_TAG => Static::get().hashes[self.static_index() as usize], | 208 | 58.9k | INLINE_TAG => self.unsafe_data.get(), | 209 | 0 | _ => unsafe { debug_unreachable!() }, | 210 | | } | 211 | 110k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::get_hash |
212 | | |
213 | 0 | pub fn try_static(string_to_add: &str) -> Option<Self> { |
214 | 0 | Self::try_static_internal(string_to_add).ok() |
215 | 0 | } |
216 | | |
217 | 132k | fn try_static_internal(string_to_add: &str) -> Result<Self, phf_shared::Hashes> { |
218 | 132k | let static_set = Static::get(); |
219 | 132k | let hash = phf_shared::hash(string_to_add, &static_set.key); |
220 | 132k | let index = phf_shared::get_index(&hash, static_set.disps, static_set.atoms.len()); |
221 | | |
222 | 132k | if static_set.atoms[index as usize] == string_to_add { |
223 | 39.2k | Ok(Self::pack_static(index)) |
224 | | } else { |
225 | 93.1k | Err(hash) |
226 | | } |
227 | 132k | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::try_static_internal Line | Count | Source | 217 | 8 | fn try_static_internal(string_to_add: &str) -> Result<Self, phf_shared::Hashes> { | 218 | 8 | let static_set = Static::get(); | 219 | 8 | let hash = phf_shared::hash(string_to_add, &static_set.key); | 220 | 8 | let index = phf_shared::get_index(&hash, static_set.disps, static_set.atoms.len()); | 221 | | | 222 | 8 | if static_set.atoms[index as usize] == string_to_add { | 223 | 0 | Ok(Self::pack_static(index)) | 224 | | } else { | 225 | 8 | Err(hash) | 226 | | } | 227 | 8 | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::try_static_internal Line | Count | Source | 217 | 91.1k | fn try_static_internal(string_to_add: &str) -> Result<Self, phf_shared::Hashes> { | 218 | 91.1k | let static_set = Static::get(); | 219 | 91.1k | let hash = phf_shared::hash(string_to_add, &static_set.key); | 220 | 91.1k | let index = phf_shared::get_index(&hash, static_set.disps, static_set.atoms.len()); | 221 | | | 222 | 91.1k | if static_set.atoms[index as usize] == string_to_add { | 223 | 36.2k | Ok(Self::pack_static(index)) | 224 | | } else { | 225 | 54.9k | Err(hash) | 226 | | } | 227 | 91.1k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::try_static_internal Line | Count | Source | 217 | 41.2k | fn try_static_internal(string_to_add: &str) -> Result<Self, phf_shared::Hashes> { | 218 | 41.2k | let static_set = Static::get(); | 219 | 41.2k | let hash = phf_shared::hash(string_to_add, &static_set.key); | 220 | 41.2k | let index = phf_shared::get_index(&hash, static_set.disps, static_set.atoms.len()); | 221 | | | 222 | 41.2k | if static_set.atoms[index as usize] == string_to_add { | 223 | 3.02k | Ok(Self::pack_static(index)) | 224 | | } else { | 225 | 38.2k | Err(hash) | 226 | | } | 227 | 41.2k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::try_static_internal |
228 | | |
229 | | /// Get a reference to the underlying str. |
230 | | #[inline] |
231 | 1.24M | pub fn as_str(&self) -> &str { |
232 | 1.24M | self // auto-deref |
233 | 1.24M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::as_str Line | Count | Source | 231 | 398k | pub fn as_str(&self) -> &str { | 232 | 398k | self // auto-deref | 233 | 398k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::as_str Line | Count | Source | 231 | 834k | pub fn as_str(&self) -> &str { | 232 | 834k | self // auto-deref | 233 | 834k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::as_str Line | Count | Source | 231 | 16.8k | pub fn as_str(&self) -> &str { | 232 | 16.8k | self // auto-deref | 233 | 16.8k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::as_str |
234 | | |
235 | | /// Get a reference to the bytes of the underlying str. |
236 | | #[inline] |
237 | 893k | pub fn as_bytes(&self) -> &[u8] { |
238 | 893k | self.as_str().as_bytes() |
239 | 893k | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet>>::as_bytes Line | Count | Source | 237 | 41.7k | pub fn as_bytes(&self) -> &[u8] { | 238 | 41.7k | self.as_str().as_bytes() | 239 | 41.7k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet>>::as_bytes Line | Count | Source | 237 | 834k | pub fn as_bytes(&self) -> &[u8] { | 238 | 834k | self.as_str().as_bytes() | 239 | 834k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet>>::as_bytes Line | Count | Source | 237 | 16.8k | pub fn as_bytes(&self) -> &[u8] { | 238 | 16.8k | self.as_str().as_bytes() | 239 | 16.8k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_>>::as_bytes |
240 | | } |
241 | | |
242 | | impl<Static: StaticAtomSet> Default for Atom<Static> { |
243 | | #[inline] |
244 | 0 | fn default() -> Self { |
245 | 0 | Atom::pack_inline(0, 0) |
246 | 0 | } |
247 | | } |
248 | | |
249 | | impl<Static: StaticAtomSet> Hash for Atom<Static> { |
250 | | #[inline] |
251 | 221k | fn hash<H>(&self, state: &mut H) |
252 | 221k | where |
253 | 221k | H: Hasher, |
254 | | { |
255 | 221k | state.write_u64(self.get_hash()) |
256 | 221k | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher> <string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher> Line | Count | Source | 251 | 110k | fn hash<H>(&self, state: &mut H) | 252 | 110k | where | 253 | 110k | H: Hasher, | 254 | | { | 255 | 110k | state.write_u64(self.get_hash()) | 256 | 110k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher> Line | Count | Source | 251 | 110k | fn hash<H>(&self, state: &mut H) | 252 | 110k | where | 253 | 110k | H: Hasher, | 254 | | { | 255 | 110k | state.write_u64(self.get_hash()) | 256 | 110k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::hash::Hash>::hash::<_> |
257 | | } |
258 | | |
259 | | impl<'a, Static: StaticAtomSet> From<Cow<'a, str>> for Atom<Static> { |
260 | 869k | fn from(string_to_add: Cow<'a, str>) -> Self { |
261 | 869k | let len = string_to_add.len(); |
262 | 869k | if len <= MAX_INLINE_LEN { |
263 | 737k | let mut data: u64 = (INLINE_TAG as u64) | ((len as u64) << LEN_OFFSET); |
264 | 737k | { |
265 | 737k | let dest = inline_atom_slice_mut(&mut data); |
266 | 737k | dest[..len].copy_from_slice(string_to_add.as_bytes()); |
267 | 737k | } |
268 | 737k | Atom { |
269 | 737k | // INLINE_TAG ensures this is never zero |
270 | 737k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, |
271 | 737k | phantom: PhantomData, |
272 | 737k | } |
273 | | } else { |
274 | 132k | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { |
275 | | // Reconstitute 64-bit `Hash128::h1` |
276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 |
277 | 93.1k | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); |
278 | 93.1k | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); |
279 | 93.1k | let data = ptr.as_ptr().expose_provenance() as u64; |
280 | 93.1k | debug_assert!(0 == data & TAG_MASK); |
281 | 93.1k | Atom { |
282 | 93.1k | // The address of a ptr::NonNull is non-zero |
283 | 93.1k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, |
284 | 93.1k | phantom: PhantomData, |
285 | 93.1k | } |
286 | 93.1k | }) <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from::{closure#0}Line | Count | Source | 274 | 8 | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | 8 | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | 8 | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | 8 | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | 8 | debug_assert!(0 == data & TAG_MASK); | 281 | 8 | Atom { | 282 | 8 | // The address of a ptr::NonNull is non-zero | 283 | 8 | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | 8 | phantom: PhantomData, | 285 | 8 | } | 286 | 8 | }) |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from::{closure#0}Line | Count | Source | 274 | 54.9k | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | 54.9k | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | 54.9k | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | 54.9k | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | 54.9k | debug_assert!(0 == data & TAG_MASK); | 281 | 54.9k | Atom { | 282 | 54.9k | // The address of a ptr::NonNull is non-zero | 283 | 54.9k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | 54.9k | phantom: PhantomData, | 285 | 54.9k | } | 286 | 54.9k | }) |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from::{closure#0}Line | Count | Source | 274 | 38.2k | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | 38.2k | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | 38.2k | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | 38.2k | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | 38.2k | debug_assert!(0 == data & TAG_MASK); | 281 | 38.2k | Atom { | 282 | 38.2k | // The address of a ptr::NonNull is non-zero | 283 | 38.2k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | 38.2k | phantom: PhantomData, | 285 | 38.2k | } | 286 | 38.2k | }) |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::convert::From<alloc::borrow::Cow<str>>>::from::{closure#0} |
287 | | } |
288 | 869k | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from Line | Count | Source | 260 | 102k | fn from(string_to_add: Cow<'a, str>) -> Self { | 261 | 102k | let len = string_to_add.len(); | 262 | 102k | if len <= MAX_INLINE_LEN { | 263 | 102k | let mut data: u64 = (INLINE_TAG as u64) | ((len as u64) << LEN_OFFSET); | 264 | 102k | { | 265 | 102k | let dest = inline_atom_slice_mut(&mut data); | 266 | 102k | dest[..len].copy_from_slice(string_to_add.as_bytes()); | 267 | 102k | } | 268 | 102k | Atom { | 269 | 102k | // INLINE_TAG ensures this is never zero | 270 | 102k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 271 | 102k | phantom: PhantomData, | 272 | 102k | } | 273 | | } else { | 274 | 8 | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | | debug_assert!(0 == data & TAG_MASK); | 281 | | Atom { | 282 | | // The address of a ptr::NonNull is non-zero | 283 | | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | | phantom: PhantomData, | 285 | | } | 286 | | }) | 287 | | } | 288 | 102k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from Line | Count | Source | 260 | 710k | fn from(string_to_add: Cow<'a, str>) -> Self { | 261 | 710k | let len = string_to_add.len(); | 262 | 710k | if len <= MAX_INLINE_LEN { | 263 | 619k | let mut data: u64 = (INLINE_TAG as u64) | ((len as u64) << LEN_OFFSET); | 264 | 619k | { | 265 | 619k | let dest = inline_atom_slice_mut(&mut data); | 266 | 619k | dest[..len].copy_from_slice(string_to_add.as_bytes()); | 267 | 619k | } | 268 | 619k | Atom { | 269 | 619k | // INLINE_TAG ensures this is never zero | 270 | 619k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 271 | 619k | phantom: PhantomData, | 272 | 619k | } | 273 | | } else { | 274 | 91.1k | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | | debug_assert!(0 == data & TAG_MASK); | 281 | | Atom { | 282 | | // The address of a ptr::NonNull is non-zero | 283 | | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | | phantom: PhantomData, | 285 | | } | 286 | | }) | 287 | | } | 288 | 710k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::convert::From<alloc::borrow::Cow<str>>>::from Line | Count | Source | 260 | 57.1k | fn from(string_to_add: Cow<'a, str>) -> Self { | 261 | 57.1k | let len = string_to_add.len(); | 262 | 57.1k | if len <= MAX_INLINE_LEN { | 263 | 15.8k | let mut data: u64 = (INLINE_TAG as u64) | ((len as u64) << LEN_OFFSET); | 264 | 15.8k | { | 265 | 15.8k | let dest = inline_atom_slice_mut(&mut data); | 266 | 15.8k | dest[..len].copy_from_slice(string_to_add.as_bytes()); | 267 | 15.8k | } | 268 | 15.8k | Atom { | 269 | 15.8k | // INLINE_TAG ensures this is never zero | 270 | 15.8k | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 271 | 15.8k | phantom: PhantomData, | 272 | 15.8k | } | 273 | | } else { | 274 | 41.2k | Self::try_static_internal(&string_to_add).unwrap_or_else(|hash| { | 275 | | // Reconstitute 64-bit `Hash128::h1` | 276 | | // https://docs.rs/phf_shared/0.14.0/src/phf_shared/lib.rs.html#45-54 | 277 | | let hash = (hash.g as u64) << 32 | (hash.f1 as u64); | 278 | | let ptr: std::ptr::NonNull<Entry> = dynamic_set().insert(string_to_add, hash); | 279 | | let data = ptr.as_ptr().expose_provenance() as u64; | 280 | | debug_assert!(0 == data & TAG_MASK); | 281 | | Atom { | 282 | | // The address of a ptr::NonNull is non-zero | 283 | | unsafe_data: unsafe { NonZeroU64::new_unchecked(data) }, | 284 | | phantom: PhantomData, | 285 | | } | 286 | | }) | 287 | | } | 288 | 57.1k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::convert::From<alloc::borrow::Cow<str>>>::from |
289 | | } |
290 | | |
291 | | impl<Static: StaticAtomSet> Clone for Atom<Static> { |
292 | | #[inline(always)] |
293 | 2.44M | fn clone(&self) -> Self { |
294 | 2.44M | if self.tag() == DYNAMIC_TAG { |
295 | 863k | let entry = self.dynamic_ptr(); |
296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` |
297 | | // kept alive by `self`'s reference count. We can safely dereference it. |
298 | 863k | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { |
299 | 0 | std::process::abort(); |
300 | 863k | } |
301 | 1.58M | } |
302 | 2.44M | Atom { ..*self } |
303 | 2.44M | } <string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::clone::Clone>::clone Line | Count | Source | 293 | 1.27M | fn clone(&self) -> Self { | 294 | 1.27M | if self.tag() == DYNAMIC_TAG { | 295 | 741k | let entry = self.dynamic_ptr(); | 296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` | 297 | | // kept alive by `self`'s reference count. We can safely dereference it. | 298 | 741k | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { | 299 | 0 | std::process::abort(); | 300 | 741k | } | 301 | 537k | } | 302 | 1.27M | Atom { ..*self } | 303 | 1.27M | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::clone::Clone>::clone Line | Count | Source | 293 | 996k | fn clone(&self) -> Self { | 294 | 996k | if self.tag() == DYNAMIC_TAG { | 295 | 91.1k | let entry = self.dynamic_ptr(); | 296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` | 297 | | // kept alive by `self`'s reference count. We can safely dereference it. | 298 | 91.1k | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { | 299 | 0 | std::process::abort(); | 300 | 91.1k | } | 301 | 905k | } | 302 | 996k | Atom { ..*self } | 303 | 996k | } |
Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::clone::Clone>::clone Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::clone::Clone>::clone <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::clone::Clone>::clone Line | Count | Source | 293 | 106k | fn clone(&self) -> Self { | 294 | 106k | if self.tag() == DYNAMIC_TAG { | 295 | 27 | let entry = self.dynamic_ptr(); | 296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` | 297 | | // kept alive by `self`'s reference count. We can safely dereference it. | 298 | 27 | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { | 299 | 0 | std::process::abort(); | 300 | 27 | } | 301 | 106k | } | 302 | 106k | Atom { ..*self } | 303 | 106k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::clone::Clone>::clone Line | Count | Source | 293 | 32.4k | fn clone(&self) -> Self { | 294 | 32.4k | if self.tag() == DYNAMIC_TAG { | 295 | 15.8k | let entry = self.dynamic_ptr(); | 296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` | 297 | | // kept alive by `self`'s reference count. We can safely dereference it. | 298 | 15.8k | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { | 299 | 0 | std::process::abort(); | 300 | 15.8k | } | 301 | 16.6k | } | 302 | 32.4k | Atom { ..*self } | 303 | 32.4k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::clone::Clone>::clone Line | Count | Source | 293 | 32.4k | fn clone(&self) -> Self { | 294 | 32.4k | if self.tag() == DYNAMIC_TAG { | 295 | 15.0k | let entry = self.dynamic_ptr(); | 296 | | // SAFETY: `self` is a valid Atom, meaning its `unsafe_data` points to a live `Entry` | 297 | | // kept alive by `self`'s reference count. We can safely dereference it. | 298 | 15.0k | if unsafe { &*entry }.ref_count.fetch_add(1, SeqCst) == isize::MAX { | 299 | 0 | std::process::abort(); | 300 | 15.0k | } | 301 | 17.4k | } | 302 | 32.4k | Atom { ..*self } | 303 | 32.4k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::clone::Clone>::clone |
304 | | } |
305 | | |
306 | | impl<Static> Drop for Atom<Static> { |
307 | | #[inline] |
308 | 5.04M | fn drop(&mut self) { |
309 | 5.04M | if self.tag() == DYNAMIC_TAG { |
310 | 956k | let entry = self.dynamic_ptr(); |
311 | 956k | if unsafe { &*entry }.ref_count.fetch_sub(1, SeqCst) == 1 { |
312 | 31.4k | drop_slow(self) |
313 | 925k | } |
314 | 4.09M | } |
315 | | |
316 | | // Out of line to guide inlining. |
317 | | #[cold] |
318 | 31.4k | fn drop_slow<Static>(this: &mut Atom<Static>) { |
319 | 31.4k | dynamic_set().remove(this.dynamic_ptr().cast_mut()); |
320 | 31.4k | } <string_cache::atom::Atom<_> as core::ops::drop::Drop>::drop::drop_slow::<web_atoms::PrefixStaticSet> Line | Count | Source | 318 | 6 | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | 6 | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | 6 | } |
<string_cache::atom::Atom<_> as core::ops::drop::Drop>::drop::drop_slow::<web_atoms::LocalNameStaticSet> Line | Count | Source | 318 | 30.6k | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | 30.6k | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | 30.6k | } |
<string_cache::atom::Atom<_> as core::ops::drop::Drop>::drop::drop_slow::<web_atoms::NamespaceStaticSet> Line | Count | Source | 318 | 802 | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | 802 | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | 802 | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::ops::drop::Drop>::drop::drop_slow::<_> |
321 | 5.04M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::ops::drop::Drop>::drop Line | Count | Source | 308 | 580k | fn drop(&mut self) { | 309 | 580k | if self.tag() == DYNAMIC_TAG { | 310 | 35 | let entry = self.dynamic_ptr(); | 311 | 35 | if unsafe { &*entry }.ref_count.fetch_sub(1, SeqCst) == 1 { | 312 | 6 | drop_slow(self) | 313 | 29 | } | 314 | 580k | } | 315 | | | 316 | | // Out of line to guide inlining. | 317 | | #[cold] | 318 | | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | | } | 321 | 580k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::ops::drop::Drop>::drop Line | Count | Source | 308 | 2.18M | fn drop(&mut self) { | 309 | 2.18M | if self.tag() == DYNAMIC_TAG { | 310 | 161k | let entry = self.dynamic_ptr(); | 311 | 161k | if unsafe { &*entry }.ref_count.fetch_sub(1, SeqCst) == 1 { | 312 | 30.6k | drop_slow(self) | 313 | 131k | } | 314 | 2.02M | } | 315 | | | 316 | | // Out of line to guide inlining. | 317 | | #[cold] | 318 | | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | | } | 321 | 2.18M | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::ops::drop::Drop>::drop Line | Count | Source | 308 | 2.27M | fn drop(&mut self) { | 309 | 2.27M | if self.tag() == DYNAMIC_TAG { | 310 | 794k | let entry = self.dynamic_ptr(); | 311 | 794k | if unsafe { &*entry }.ref_count.fetch_sub(1, SeqCst) == 1 { | 312 | 802 | drop_slow(self) | 313 | 794k | } | 314 | 1.48M | } | 315 | | | 316 | | // Out of line to guide inlining. | 317 | | #[cold] | 318 | | fn drop_slow<Static>(this: &mut Atom<Static>) { | 319 | | dynamic_set().remove(this.dynamic_ptr().cast_mut()); | 320 | | } | 321 | 2.27M | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::ops::drop::Drop>::drop |
322 | | } |
323 | | |
324 | | impl<Static: StaticAtomSet> ops::Deref for Atom<Static> { |
325 | | type Target = str; |
326 | | |
327 | | #[inline] |
328 | 2.12M | fn deref(&self) -> &str { |
329 | | unsafe { |
330 | 2.12M | match self.tag() { |
331 | | DYNAMIC_TAG => { |
332 | 556k | let entry = self.dynamic_ptr(); |
333 | 556k | &(*entry).string |
334 | | } |
335 | | INLINE_TAG => { |
336 | 1.46M | let len = (self.unsafe_data() & LEN_MASK) >> LEN_OFFSET; |
337 | 1.46M | debug_assert!(len as usize <= MAX_INLINE_LEN); |
338 | 1.46M | let src = inline_atom_slice(&self.unsafe_data); |
339 | 1.46M | str::from_utf8_unchecked(src.get_unchecked(..(len as usize))) |
340 | | } |
341 | 105k | STATIC_TAG => Static::get().atoms[self.static_index() as usize], |
342 | 0 | _ => debug_unreachable!(), |
343 | | } |
344 | | } |
345 | 2.12M | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::ops::deref::Deref>::deref Line | Count | Source | 328 | 398k | fn deref(&self) -> &str { | 329 | | unsafe { | 330 | 398k | match self.tag() { | 331 | | DYNAMIC_TAG => { | 332 | 63 | let entry = self.dynamic_ptr(); | 333 | 63 | &(*entry).string | 334 | | } | 335 | | INLINE_TAG => { | 336 | 397k | let len = (self.unsafe_data() & LEN_MASK) >> LEN_OFFSET; | 337 | 397k | debug_assert!(len as usize <= MAX_INLINE_LEN); | 338 | 397k | let src = inline_atom_slice(&self.unsafe_data); | 339 | 397k | str::from_utf8_unchecked(src.get_unchecked(..(len as usize))) | 340 | | } | 341 | 0 | STATIC_TAG => Static::get().atoms[self.static_index() as usize], | 342 | 0 | _ => debug_unreachable!(), | 343 | | } | 344 | | } | 345 | 398k | } |
<string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::ops::deref::Deref>::deref Line | Count | Source | 328 | 875k | fn deref(&self) -> &str { | 329 | | unsafe { | 330 | 875k | match self.tag() { | 331 | | DYNAMIC_TAG => { | 332 | 55.1k | let entry = self.dynamic_ptr(); | 333 | 55.1k | &(*entry).string | 334 | | } | 335 | | INLINE_TAG => { | 336 | 767k | let len = (self.unsafe_data() & LEN_MASK) >> LEN_OFFSET; | 337 | 767k | debug_assert!(len as usize <= MAX_INLINE_LEN); | 338 | 767k | let src = inline_atom_slice(&self.unsafe_data); | 339 | 767k | str::from_utf8_unchecked(src.get_unchecked(..(len as usize))) | 340 | | } | 341 | 52.9k | STATIC_TAG => Static::get().atoms[self.static_index() as usize], | 342 | 0 | _ => debug_unreachable!(), | 343 | | } | 344 | | } | 345 | 875k | } |
<string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::ops::deref::Deref>::deref Line | Count | Source | 328 | 852k | fn deref(&self) -> &str { | 329 | | unsafe { | 330 | 852k | match self.tag() { | 331 | | DYNAMIC_TAG => { | 332 | 501k | let entry = self.dynamic_ptr(); | 333 | 501k | &(*entry).string | 334 | | } | 335 | | INLINE_TAG => { | 336 | 298k | let len = (self.unsafe_data() & LEN_MASK) >> LEN_OFFSET; | 337 | 298k | debug_assert!(len as usize <= MAX_INLINE_LEN); | 338 | 298k | let src = inline_atom_slice(&self.unsafe_data); | 339 | 298k | str::from_utf8_unchecked(src.get_unchecked(..(len as usize))) | 340 | | } | 341 | 52.6k | STATIC_TAG => Static::get().atoms[self.static_index() as usize], | 342 | 0 | _ => debug_unreachable!(), | 343 | | } | 344 | | } | 345 | 852k | } |
Unexecuted instantiation: <string_cache::atom::Atom<_> as core::ops::deref::Deref>::deref |
346 | | } |
347 | | |
348 | | impl<Static: StaticAtomSet> fmt::Debug for Atom<Static> { |
349 | | #[inline] |
350 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
351 | 0 | let ty_str = unsafe { |
352 | 0 | match self.tag() { |
353 | 0 | DYNAMIC_TAG => "dynamic", |
354 | 0 | INLINE_TAG => "inline", |
355 | 0 | STATIC_TAG => "static", |
356 | 0 | _ => debug_unreachable!(), |
357 | | } |
358 | | }; |
359 | | |
360 | 0 | write!(f, "Atom('{}' type={})", self, ty_str) |
361 | 0 | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::fmt::Debug>::fmt Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::fmt::Debug>::fmt Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::fmt::Debug>::fmt Unexecuted instantiation: <string_cache::atom::Atom<_> as core::fmt::Debug>::fmt |
362 | | } |
363 | | |
364 | | impl<Static: StaticAtomSet> PartialOrd for Atom<Static> { |
365 | | #[inline] |
366 | 0 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
367 | 0 | Some(self.cmp(other)) |
368 | 0 | } Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::cmp::PartialOrd>::partial_cmp Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::cmp::PartialOrd>::partial_cmp Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::cmp::PartialOrd>::partial_cmp Unexecuted instantiation: <string_cache::atom::Atom<_> as core::cmp::PartialOrd>::partial_cmp |
369 | | } |
370 | | |
371 | | impl<Static: StaticAtomSet> Ord for Atom<Static> { |
372 | | #[inline] |
373 | 224k | fn cmp(&self, other: &Self) -> Ordering { |
374 | 224k | if self.unsafe_data == other.unsafe_data { |
375 | 46.0k | return Equal; |
376 | 178k | } |
377 | 178k | self.as_str().cmp(other.as_ref()) |
378 | 224k | } <string_cache::atom::Atom<web_atoms::PrefixStaticSet> as core::cmp::Ord>::cmp Line | Count | Source | 373 | 224k | fn cmp(&self, other: &Self) -> Ordering { | 374 | 224k | if self.unsafe_data == other.unsafe_data { | 375 | 46.0k | return Equal; | 376 | 178k | } | 377 | 178k | self.as_str().cmp(other.as_ref()) | 378 | 224k | } |
Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::LocalNameStaticSet> as core::cmp::Ord>::cmp Unexecuted instantiation: <string_cache::atom::Atom<web_atoms::NamespaceStaticSet> as core::cmp::Ord>::cmp Unexecuted instantiation: <string_cache::atom::Atom<_> as core::cmp::Ord>::cmp |
379 | | } |
380 | | |
381 | | // AsciiExt requires mutating methods, so we just implement the non-mutating ones. |
382 | | // We don't need to implement is_ascii because there's no performance improvement |
383 | | // over the one from &str. |
384 | | impl<Static: StaticAtomSet> Atom<Static> { |
385 | 0 | fn from_mutated_str<F: FnOnce(&mut str)>(s: &str, f: F) -> Self { |
386 | 0 | let mut buffer = [const { mem::MaybeUninit::<u8>::uninit() }; 64]; |
387 | | |
388 | 0 | if let Some(buffer_prefix) = buffer.get_mut(..s.len()) { |
389 | 0 | let buffer_ptr = buffer_prefix.as_mut_ptr().cast::<u8>(); |
390 | | // SAFETY: `buffer_ptr` points to the `MaybeUninit` array. |
391 | | // We use `copy_nonoverlapping` to write valid data into it, |
392 | | // and then create a slice covering ONLY the initialized portion. |
393 | 0 | let as_str = unsafe { |
394 | 0 | buffer_ptr.copy_from_nonoverlapping(s.as_ptr(), s.len()); |
395 | 0 | let buffer_slice = slice::from_raw_parts_mut(buffer_ptr, s.len()); |
396 | 0 | std::str::from_utf8_unchecked_mut(buffer_slice) |
397 | | }; |
398 | 0 | f(as_str); |
399 | 0 | Atom::from(&*as_str) |
400 | | } else { |
401 | 0 | let mut string = s.to_owned(); |
402 | 0 | f(&mut string); |
403 | 0 | Atom::from(string) |
404 | | } |
405 | 0 | } |
406 | | |
407 | | /// Like [`to_ascii_uppercase`]. |
408 | | /// |
409 | | /// [`to_ascii_uppercase`]: https://doc.rust-lang.org/std/ascii/trait.AsciiExt.html#tymethod.to_ascii_uppercase |
410 | 0 | pub fn to_ascii_uppercase(&self) -> Self { |
411 | 0 | for (i, b) in self.bytes().enumerate() { |
412 | 0 | if let b'a'..=b'z' = b { |
413 | 0 | return Atom::from_mutated_str(self, |s| s[i..].make_ascii_uppercase()); |
414 | 0 | } |
415 | | } |
416 | 0 | self.clone() |
417 | 0 | } |
418 | | |
419 | | /// Like [`to_ascii_lowercase`]. |
420 | | /// |
421 | | /// [`to_ascii_lowercase`]: https://doc.rust-lang.org/std/ascii/trait.AsciiExt.html#tymethod.to_ascii_lowercase |
422 | 0 | pub fn to_ascii_lowercase(&self) -> Self { |
423 | 0 | for (i, b) in self.bytes().enumerate() { |
424 | 0 | if let b'A'..=b'Z' = b { |
425 | 0 | return Atom::from_mutated_str(self, |s| s[i..].make_ascii_lowercase()); |
426 | 0 | } |
427 | | } |
428 | 0 | self.clone() |
429 | 0 | } |
430 | | |
431 | | /// Like [`eq_ignore_ascii_case`]. |
432 | | /// |
433 | | /// [`eq_ignore_ascii_case`]: https://doc.rust-lang.org/std/ascii/trait.AsciiExt.html#tymethod.eq_ignore_ascii_case |
434 | 0 | pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { |
435 | 0 | (self == other) || self.eq_str_ignore_ascii_case(other) |
436 | 0 | } |
437 | | |
438 | | /// Like [`eq_ignore_ascii_case`], but takes an unhashed string as `other`. |
439 | | /// |
440 | | /// [`eq_ignore_ascii_case`]: https://doc.rust-lang.org/std/ascii/trait.AsciiExt.html#tymethod.eq_ignore_ascii_case |
441 | 0 | pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool { |
442 | 0 | self.as_str().eq_ignore_ascii_case(other) |
443 | 0 | } |
444 | | } |
445 | | |
446 | | #[inline(always)] |
447 | 1.46M | fn inline_atom_slice(x: &NonZeroU64) -> &[u8] { |
448 | 1.46M | let x: *const NonZeroU64 = x; |
449 | 1.46M | let mut data = x as *const u8; |
450 | | // All except the lowest byte, which is first in little-endian, last in big-endian. |
451 | 1.46M | if cfg!(target_endian = "little") { |
452 | 1.46M | data = unsafe { data.offset(1) }; |
453 | 1.46M | } |
454 | 1.46M | let len = 7; |
455 | 1.46M | unsafe { slice::from_raw_parts(data, len) } |
456 | 1.46M | } |
457 | | |
458 | | #[inline(always)] |
459 | 737k | fn inline_atom_slice_mut(x: &mut u64) -> &mut [u8] { |
460 | 737k | let x: *mut u64 = x; |
461 | 737k | let mut data = x as *mut u8; |
462 | | // All except the lowest byte, which is first in little-endian, last in big-endian. |
463 | 737k | if cfg!(target_endian = "little") { |
464 | 737k | data = unsafe { data.offset(1) }; |
465 | 737k | } |
466 | 737k | let len = 7; |
467 | 737k | unsafe { slice::from_raw_parts_mut(data, len) } |
468 | 737k | } |