hstr/
wtf8_atom.rs

1use std::{
2    fmt::Debug,
3    hash::Hash,
4    mem::{forget, transmute},
5    ops::Deref,
6};
7
8use debug_unreachable::debug_unreachable;
9
10use crate::{
11    macros::{get_hash, impl_from_alias, partial_eq},
12    tagged_value::TaggedValue,
13    wtf8::Wtf8,
14    DYNAMIC_TAG, INLINE_TAG, LEN_MASK, LEN_OFFSET, TAG_MASK,
15};
16
17/// A WTF-8 encoded atom. This is like [Atom], but can contain unpaired
18/// surrogates.
19///
20/// [Atom]: crate::Atom
21pub struct Wtf8Atom {
22    pub(crate) unsafe_data: TaggedValue,
23}
24
25impl Wtf8Atom {
26    #[inline(always)]
27    pub fn new<S>(s: S) -> Self
28    where
29        Self: From<S>,
30    {
31        Self::from(s)
32    }
33
34    #[inline(always)]
35    fn tag(&self) -> u8 {
36        self.unsafe_data.tag() & TAG_MASK
37    }
38
39    /// Return true if this is a dynamic Atom.
40    #[inline(always)]
41    fn is_dynamic(&self) -> bool {
42        self.tag() == DYNAMIC_TAG
43    }
44}
45
46impl Default for Wtf8Atom {
47    #[inline(never)]
48    fn default() -> Self {
49        Wtf8Atom::new("")
50    }
51}
52
53/// Immutable, so it's safe to be shared between threads
54unsafe impl Send for Wtf8Atom {}
55
56/// Immutable, so it's safe to be shared between threads
57unsafe impl Sync for Wtf8Atom {}
58
59impl Debug for Wtf8Atom {
60    #[inline]
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        Debug::fmt(&self.to_string_lossy(), f)
63    }
64}
65
66#[cfg(feature = "serde")]
67impl serde::ser::Serialize for Wtf8Atom {
68    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69    where
70        S: serde::ser::Serializer,
71    {
72        serializer.serialize_bytes(self.as_bytes())
73    }
74}
75
76#[cfg(feature = "serde")]
77impl<'de> serde::de::Deserialize<'de> for Wtf8Atom {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: serde::Deserializer<'de>,
81    {
82        String::deserialize(deserializer).map(Self::new)
83    }
84}
85
86impl PartialEq for Wtf8Atom {
87    #[inline(never)]
88    fn eq(&self, other: &Self) -> bool {
89        partial_eq!(self, other);
90
91        // If the store is different, the string may be the same, even though the
92        // `unsafe_data` is different
93        self.as_wtf8() == other.as_wtf8()
94    }
95}
96
97impl Eq for Wtf8Atom {}
98
99impl Hash for Wtf8Atom {
100    #[inline(always)]
101    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
102        state.write_u64(self.get_hash());
103    }
104}
105
106impl Drop for Wtf8Atom {
107    #[inline(always)]
108    fn drop(&mut self) {
109        if self.is_dynamic() {
110            unsafe { drop(crate::dynamic::restore_arc(self.unsafe_data)) }
111        }
112    }
113}
114
115impl Clone for Wtf8Atom {
116    #[inline(always)]
117    fn clone(&self) -> Self {
118        Self::from_alias(self.unsafe_data)
119    }
120}
121
122impl Deref for Wtf8Atom {
123    type Target = Wtf8;
124
125    #[inline(always)]
126    fn deref(&self) -> &Self::Target {
127        self.as_wtf8()
128    }
129}
130
131impl AsRef<Wtf8> for Wtf8Atom {
132    #[inline(always)]
133    fn as_ref(&self) -> &Wtf8 {
134        self.as_wtf8()
135    }
136}
137
138impl PartialEq<Wtf8> for Wtf8Atom {
139    #[inline]
140    fn eq(&self, other: &Wtf8) -> bool {
141        self.as_wtf8() == other
142    }
143}
144
145impl PartialEq<crate::Atom> for Wtf8Atom {
146    #[inline]
147    fn eq(&self, other: &crate::Atom) -> bool {
148        self.as_str() == Some(other.as_str())
149    }
150}
151
152impl PartialEq<&'_ Wtf8> for Wtf8Atom {
153    #[inline]
154    fn eq(&self, other: &&Wtf8) -> bool {
155        self.as_wtf8() == *other
156    }
157}
158
159impl PartialEq<Wtf8Atom> for Wtf8 {
160    #[inline]
161    fn eq(&self, other: &Wtf8Atom) -> bool {
162        self == other.as_wtf8()
163    }
164}
165
166impl Wtf8Atom {
167    pub(super) fn get_hash(&self) -> u64 {
168        get_hash!(self)
169    }
170
171    fn as_wtf8(&self) -> &Wtf8 {
172        match self.tag() {
173            DYNAMIC_TAG => unsafe {
174                let item = crate::dynamic::deref_from(self.unsafe_data);
175                Wtf8::from_bytes_unchecked(transmute::<&[u8], &'static [u8]>(&item.slice))
176            },
177            INLINE_TAG => {
178                let len = (self.unsafe_data.tag() & LEN_MASK) >> LEN_OFFSET;
179                let src = self.unsafe_data.data();
180                unsafe { Wtf8::from_bytes_unchecked(&src[..(len as usize)]) }
181            }
182            _ => unsafe { debug_unreachable!() },
183        }
184    }
185}
186
187impl_from_alias!(Wtf8Atom);
188
189#[cfg(test)]
190impl Wtf8Atom {
191    pub(crate) fn ref_count(&self) -> usize {
192        match self.tag() {
193            DYNAMIC_TAG => {
194                let ptr = unsafe { crate::dynamic::deref_from(self.unsafe_data) };
195
196                triomphe::ThinArc::strong_count(&ptr.0)
197            }
198            _ => 1,
199        }
200    }
201}