1#![allow(clippy::unreadable_literal)]
4
5#[doc(hidden)]
6pub extern crate hstr;
8#[doc(hidden)]
9pub extern crate once_cell;
11
12use std::{
13 borrow::{Borrow, Cow},
14 cell::UnsafeCell,
15 fmt::{self, Display, Formatter},
16 hash::{Hash, Hasher},
17 mem::transmute,
18 ops::Deref,
19 rc::Rc,
20};
21
22pub use hstr::wtf8;
23use once_cell::sync::Lazy;
24use serde::Serializer;
25use wtf8::Wtf8;
26
27pub use crate::{fast::UnsafeAtom, wtf8_atom::Wtf8Atom};
28
29mod fast;
30mod wtf8_atom;
31
32#[derive(Clone, Default)]
37#[repr(transparent)]
38pub struct Atom(hstr::Atom);
39
40impl PartialEq for Atom {
41 #[inline]
42 fn eq(&self, other: &Self) -> bool {
43 self.0 == other.0
44 }
45}
46
47impl Eq for Atom {}
48
49impl Hash for Atom {
50 #[inline]
51 fn hash<H: Hasher>(&self, state: &mut H) {
52 self.0.hash(state);
53 }
54}
55
56#[cfg(feature = "encoding-impl")]
57impl cbor4ii::core::enc::Encode for Atom {
58 #[inline]
59 fn encode<W: cbor4ii::core::enc::Write>(
60 &self,
61 writer: &mut W,
62 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
63 self.as_str().encode(writer)
64 }
65}
66
67#[cfg(feature = "encoding-impl")]
68impl<'de> cbor4ii::core::dec::Decode<'de> for Atom {
69 #[inline]
70 fn decode<R: cbor4ii::core::dec::Read<'de>>(
71 reader: &mut R,
72 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
73 let s = <&str>::decode(reader)?;
74 Ok(Atom::new(s))
75 }
76}
77
78#[cfg(feature = "arbitrary")]
79#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
80impl<'a> arbitrary::Arbitrary<'a> for Atom {
81 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
82 let sym = u.arbitrary::<String>()?;
83 if sym.is_empty() {
84 return Err(arbitrary::Error::NotEnoughData);
85 }
86 Ok(Self(hstr::Atom::from(sym)))
87 }
88}
89
90fn _asserts() {
91 fn _assert_send<T: Send>() {}
94 fn _assert_sync<T: Sync>() {}
95
96 _assert_send::<Atom>();
97 _assert_sync::<Atom>();
98}
99
100impl Atom {
101 #[inline(always)]
103 pub fn new<S>(s: S) -> Self
104 where
105 hstr::Atom: From<S>,
106 {
107 Atom(hstr::Atom::from(s))
108 }
109
110 #[inline]
111 pub fn to_ascii_lowercase(&self) -> Self {
112 Self(self.0.to_ascii_lowercase())
113 }
114
115 #[inline]
116 pub fn as_str(&self) -> &str {
117 &self.0
118 }
119
120 pub unsafe fn from_wtf8_unchecked(s: Wtf8Atom) -> Self {
134 Atom(unsafe { hstr::Atom::from_wtf8_unchecked(s.0) })
135 }
136}
137
138impl Deref for Atom {
139 type Target = str;
140
141 #[inline]
142 fn deref(&self) -> &Self::Target {
143 &self.0
144 }
145}
146
147macro_rules! impl_eq {
148 ($T:ty) => {
149 impl PartialEq<$T> for Atom {
150 #[inline]
151 fn eq(&self, other: &$T) -> bool {
152 &**self == &**other
153 }
154 }
155 };
156}
157
158macro_rules! impl_from {
159 ($T:ty) => {
160 impl From<$T> for Atom {
161 fn from(s: $T) -> Self {
162 Atom::new(s)
163 }
164 }
165 };
166}
167
168impl From<hstr::Atom> for Atom {
169 #[inline(always)]
170 fn from(s: hstr::Atom) -> Self {
171 Atom(s)
172 }
173}
174
175impl From<Atom> for hstr::Wtf8Atom {
176 #[inline(always)]
177 fn from(s: Atom) -> Self {
178 hstr::Wtf8Atom::from(&*s)
179 }
180}
181
182impl PartialEq<str> for Atom {
183 #[inline]
184 fn eq(&self, other: &str) -> bool {
185 self.0 == *other
186 }
187}
188
189impl_eq!(&'_ str);
190impl_eq!(Box<str>);
191impl_eq!(std::sync::Arc<str>);
192impl_eq!(Rc<str>);
193impl_eq!(Cow<'_, str>);
194impl_eq!(String);
195
196impl_from!(&'_ str);
197impl_from!(Box<str>);
198impl_from!(String);
199impl_from!(Cow<'_, str>);
200
201impl AsRef<str> for Atom {
202 fn as_ref(&self) -> &str {
203 self
204 }
205}
206
207impl fmt::Debug for Atom {
208 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
209 fmt::Debug::fmt(&**self, f)
210 }
211}
212
213impl Display for Atom {
214 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
215 Display::fmt(&**self, f)
216 }
217}
218
219impl PartialOrd for Atom {
220 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
221 Some(self.cmp(other))
222 }
223}
224
225impl Ord for Atom {
226 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
227 self.as_str().cmp(other.as_str())
228 }
229}
230
231impl Borrow<Wtf8Atom> for Atom {
232 fn borrow(&self) -> &Wtf8Atom {
233 const _: () = assert!(std::mem::size_of::<Atom>() == std::mem::size_of::<Wtf8Atom>());
240 const _: () = assert!(std::mem::align_of::<Atom>() == std::mem::align_of::<Wtf8Atom>());
241 unsafe { transmute::<&Atom, &Wtf8Atom>(self) }
242 }
243}
244
245impl serde::ser::Serialize for Atom {
246 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
247 where
248 S: Serializer,
249 {
250 serializer.serialize_str(self)
251 }
252}
253
254impl<'de> serde::de::Deserialize<'de> for Atom {
255 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
256 where
257 D: serde::Deserializer<'de>,
258 {
259 String::deserialize(deserializer).map(Self::new)
260 }
261}
262
263#[macro_export]
265macro_rules! atom {
266 ($s:tt) => {{
267 $crate::Atom::from($crate::hstr::atom!($s))
268 }};
269}
270
271#[macro_export]
273macro_rules! lazy_atom {
274 ($s:tt) => {{
275 $crate::Atom::from($crate::hstr::atom!($s))
276 }};
277}
278
279impl PartialEq<Atom> for str {
280 #[inline]
281 fn eq(&self, other: &Atom) -> bool {
282 *self == other.0
283 }
284}
285
286#[doc(hidden)]
287pub type CahcedAtom = Lazy<Atom>;
288
289pub type StaticString = Atom;
293
294#[derive(Default)]
295pub struct AtomStore(hstr::AtomStore);
296
297impl AtomStore {
298 #[inline]
299 pub fn atom<'a>(&mut self, s: impl Into<Cow<'a, str>>) -> Atom {
300 Atom(self.0.atom(s))
301 }
302
303 #[inline]
304 pub fn wtf8_atom<'a>(&mut self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
305 Wtf8Atom(self.0.wtf8_atom(s))
306 }
307}
308
309#[derive(Default)]
311pub struct AtomStoreCell(UnsafeCell<AtomStore>);
312
313impl AtomStoreCell {
314 #[inline]
315 pub fn atom<'a>(&self, s: impl Into<Cow<'a, str>>) -> Atom {
316 let s: Cow<'a, str> = s.into();
318 unsafe { (*self.0.get()).atom(s) }
323 }
324
325 #[inline]
326 pub fn wtf8_atom<'a>(&self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
327 let s: Cow<'a, Wtf8> = s.into();
329 unsafe { (*self.0.get()).wtf8_atom(s) }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn equality_variants() {
343 assert_eq!(Atom::from("inline"), Atom::from("inline"));
344 assert_ne!(Atom::from("inline"), Atom::from("inlinf"));
345
346 let mut store = AtomStore::default();
347 let dynamic = store.atom("Hello, beautiful world!");
348 let dynamic_same_store_equal = store.atom("Hello, beautiful world!");
349
350 let mut other_store = AtomStore::default();
351 let dynamic_other_store_equal = other_store.atom("Hello, beautiful world!");
352 let dynamic_different = other_store.atom("Hello, different world!");
353
354 assert_eq!(dynamic, dynamic_same_store_equal);
355 assert_eq!(dynamic, dynamic_other_store_equal);
356 assert_ne!(dynamic, dynamic_different);
357 assert_eq!(dynamic, "Hello, beautiful world!");
358 }
359}
360
361#[cfg(feature = "shrink-to-fit")]
363impl shrink_to_fit::ShrinkToFit for Atom {
364 #[inline(always)]
365 fn shrink_to_fit(&mut self) {}
366}