jlrs/data/managed/symbol/
mod.rs1use std::{
4 ffi::CStr,
5 hash::{Hash, Hasher},
6 marker::PhantomData,
7 ptr::NonNull,
8};
9
10use jl_sys::{jl_gensym, jl_sym_t, jl_symbol_n, jl_symbol_type, jl_tagged_gensym};
11use jlrs_sys::{jlrs_symbol_hash, jlrs_symbol_name};
12use rustc_hash::{FxBuildHasher, FxHashMap};
13
14use self::static_symbol::{StaticSymbol, Sym};
15use super::Weak;
16use crate::{
17 catch::{catch_exceptions, unwrap_exc},
18 data::{
19 cache::Cache,
20 managed::{erase_scope_lifetime, private::ManagedPriv},
21 },
22 error::{JlrsError, JlrsResult},
23 impl_julia_typecheck,
24 memory::target::{Target, TargetException, TargetResult, unrooted::Unrooted},
25 private::Private,
26};
27
28pub mod static_symbol;
29
30static CACHE: SymbolCache = SymbolCache::new();
31
32#[repr(transparent)]
40#[derive(Copy, Clone, PartialEq, Eq)]
41pub struct Symbol<'scope>(NonNull<jl_sym_t>, PhantomData<&'scope ()>);
42
43impl<'scope> Symbol<'scope> {
44 #[inline]
46 pub fn new<S, Tgt>(_: &Tgt, symbol: S) -> Self
47 where
48 S: AsRef<str>,
49 Tgt: Target<'scope>,
50 {
51 let bytes = symbol.as_ref().as_bytes();
52
53 if let Some(sym) = CACHE.find(bytes) {
54 return sym;
55 }
56
57 unsafe {
59 let sym = jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
60 let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
61 CACHE.insert(bytes.into(), sym);
62 sym
63 }
64 }
65
66 pub fn new_bytes<N, Tgt>(target: Tgt, symbol: N) -> TargetException<'scope, 'static, Self, Tgt>
68 where
69 N: AsRef<[u8]>,
70 Tgt: Target<'scope>,
71 {
72 let bytes = symbol.as_ref();
73
74 if let Some(sym) = CACHE.find(bytes) {
75 unsafe {
76 return target.exception_from_ptr(Ok(sym), Private);
77 }
78 }
79
80 unsafe {
81 let callback = || jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
82
83 match catch_exceptions(callback, unwrap_exc) {
84 Ok(sym) => {
85 let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
86 CACHE.insert(bytes.into(), sym);
87 Ok(sym)
88 }
89 Err(e) => target.exception_from_ptr(Err(e), Private),
90 }
91 }
92 }
93
94 #[inline]
98 pub unsafe fn new_bytes_unchecked<S, Tgt>(_: &Tgt, symbol: S) -> Self
99 where
100 S: AsRef<[u8]>,
101 Tgt: Target<'scope>,
102 {
103 let bytes = symbol.as_ref();
104
105 if let Some(sym) = CACHE.find(bytes) {
106 return sym;
107 }
108
109 unsafe {
111 let sym = jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
112 let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
113 CACHE.insert(bytes.into(), sym);
114 sym
115 }
116 }
117
118 #[inline]
120 pub fn generate<Tgt>(_: &Tgt) -> Self
121 where
122 Tgt: Target<'scope>,
123 {
124 unsafe {
125 let sym = jl_gensym();
126 Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
127 }
128 }
129
130 #[inline]
132 pub fn generate_tagged<S, Tgt>(_: &Tgt, tag: S) -> Self
133 where
134 S: AsRef<str>,
135 Tgt: Target<'scope>,
136 {
137 unsafe {
138 let tag = tag.as_ref().as_bytes();
139 let sym = jl_tagged_gensym(tag.as_ptr() as _, tag.len());
140 Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
141 }
142 }
143
144 #[inline]
149 pub fn extend<'target, Tgt>(self, _: &Tgt) -> Symbol<'target>
150 where
151 Tgt: Target<'target>,
152 {
153 unsafe { Symbol::wrap_non_null(self.unwrap_non_null(Private), Private) }
155 }
156
157 #[inline]
159 pub fn as_string(self) -> JlrsResult<String> {
160 self.as_str().map(Into::into)
161 }
162
163 #[inline]
165 pub fn as_str(self) -> JlrsResult<&'scope str> {
166 unsafe {
168 let ptr = jlrs_symbol_name(self.unwrap(Private)).cast();
169 let symbol = CStr::from_ptr(ptr);
170 Ok(symbol.to_str().map_err(JlrsError::other)?)
171 }
172 }
173
174 #[inline]
176 pub fn as_cstr(self) -> &'scope CStr {
177 unsafe {
179 let ptr = jlrs_symbol_name(self.unwrap(Private));
180 &CStr::from_ptr(ptr.cast())
181 }
182 }
183
184 #[inline]
186 pub fn as_bytes(self) -> &'scope [u8] {
187 unsafe {
189 let ptr = jlrs_symbol_name(self.unwrap(Private)).cast();
190 let symbol = CStr::from_ptr(ptr);
191 symbol.to_bytes()
192 }
193 }
194
195 fn hash(self) -> usize {
196 unsafe { jlrs_symbol_hash(self.unwrap(Private)) }
197 }
198}
199
200impl Hash for Symbol<'_> {
201 #[inline]
202 fn hash<H: Hasher>(&self, state: &mut H) {
203 state.write_usize((*self).hash())
204 }
205}
206
207impl<S: StaticSymbol> PartialEq<S> for Symbol<'_> {
208 fn eq(&self, _: &S) -> bool {
209 unsafe {
210 let unrooted = Unrooted::new();
211 let other = S::get_symbol(&unrooted);
212 self.0 == other.0 && self.1 == other.1
213 }
214 }
215}
216
217impl<S: StaticSymbol> PartialEq<Sym<'_, S>> for Symbol<'_> {
218 fn eq(&self, _: &Sym<S>) -> bool {
219 unsafe {
220 let unrooted = Unrooted::new();
221 let other = S::get_symbol(&unrooted);
222 self.0 == other.0 && self.1 == other.1
223 }
224 }
225}
226
227impl<S: StaticSymbol> PartialEq<Sym<'_, PhantomData<S>>> for Symbol<'_> {
228 fn eq(&self, _: &Sym<PhantomData<S>>) -> bool {
229 unsafe {
230 let unrooted = Unrooted::new();
231 let other = S::get_symbol(&unrooted);
232 self.0 == other.0 && self.1 == other.1
233 }
234 }
235}
236
237impl_julia_typecheck!(Symbol<'scope>, jl_symbol_type, 'scope);
238impl_debug!(Symbol<'_>);
239
240impl<'scope> ManagedPriv<'scope, '_> for Symbol<'scope> {
241 type Wraps = jl_sym_t;
242 type WithLifetimes<'target, 'da> = Symbol<'target>;
243 const NAME: &'static str = "Symbol";
244
245 #[inline]
248 unsafe fn wrap_non_null(inner: NonNull<Self::Wraps>, _: Private) -> Self {
249 Self(inner, PhantomData)
250 }
251
252 #[inline]
253 fn unwrap_non_null(self, _: Private) -> NonNull<Self::Wraps> {
254 self.0
255 }
256}
257
258impl_construct_type_managed!(Symbol, 1, jl_symbol_type);
259
260pub type WeakSymbol<'scope> = Weak<'scope, 'static, Symbol<'scope>>;
262
263pub type SymbolRet = WeakSymbol<'static>;
266
267impl_valid_layout!(WeakSymbol, Symbol, jl_symbol_type);
268
269use crate::memory::target::TargetType;
270
271pub type SymbolData<'target, Tgt> = <Tgt as TargetType<'target>>::Data<'static, Symbol<'target>>;
273
274pub type SymbolResult<'target, Tgt> = TargetResult<'target, 'static, Symbol<'target>, Tgt>;
276
277pub type SymbolUnbound = Symbol<'static>;
278
279impl_ccall_arg_managed!(Symbol, 1);
280impl_into_typed!(Symbol);
281
282struct SymbolCache {
283 inner: Cache<FxHashMap<Vec<u8>, Symbol<'static>>>,
284}
285
286impl SymbolCache {
287 const fn new() -> Self {
288 let map = std::collections::HashMap::with_hasher(FxBuildHasher);
289 let inner = Cache::new(map);
290
291 SymbolCache { inner }
292 }
293
294 #[inline]
295 fn find(&self, key: &[u8]) -> Option<Symbol<'static>> {
296 unsafe {
297 self.inner.read(
298 #[inline]
299 |cache| cache.cache().get(key).copied(),
300 )
301 }
302 }
303
304 #[inline]
305 fn insert(&self, key: Vec<u8>, sym: Symbol) {
306 unsafe {
307 self.inner.write(
308 #[inline]
309 |cache| cache.cache_mut().insert(key, erase_scope_lifetime(sym)),
310 )
311 };
312 }
313}