use std::{
ffi::CStr,
hash::{Hash, Hasher},
marker::PhantomData,
ptr::NonNull,
};
use jl_sys::{jl_gensym, jl_sym_t, jl_symbol_n, jl_symbol_type, jl_tagged_gensym};
use jlrs_sys::{jlrs_symbol_hash, jlrs_symbol_name};
use self::static_symbol::{StaticSymbol, Sym};
use super::Weak;
use crate::{
catch::{catch_exceptions, unwrap_exc},
data::{
cache::{CacheMap, FxCache, new_fx_cache},
managed::private::ManagedPriv,
},
error::{JlrsError, JlrsResult},
impl_julia_typecheck,
memory::target::{Target, TargetException, TargetResult, unrooted::Unrooted},
private::Private,
};
pub mod static_symbol;
pub(crate) static CACHE: FxCache<Box<[u8]>, Symbol<'static>> = new_fx_cache();
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Symbol<'scope>(NonNull<jl_sym_t>, PhantomData<&'scope ()>);
impl<'scope> Symbol<'scope> {
#[inline]
pub fn new<S, Tgt>(_: &Tgt, symbol: S) -> Self
where
S: AsRef<str>,
Tgt: Target<'scope>,
{
let bytes = symbol.as_ref().as_bytes();
if let Some(sym) = CACHE.get(bytes) {
return sym;
}
unsafe {
let sym = jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
CACHE.insert(bytes.into(), sym);
sym
}
}
pub fn new_bytes<N, Tgt>(target: Tgt, symbol: N) -> TargetException<'scope, 'static, Self, Tgt>
where
N: AsRef<[u8]>,
Tgt: Target<'scope>,
{
let bytes = symbol.as_ref();
if let Some(sym) = CACHE.get(bytes) {
unsafe {
return target.exception_from_ptr(Ok(sym), Private);
}
}
unsafe {
let callback = || jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
match catch_exceptions(callback, unwrap_exc) {
Ok(sym) => {
let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
CACHE.insert(bytes.into(), sym);
Ok(sym)
}
Err(e) => target.exception_from_ptr(Err(e), Private),
}
}
}
#[inline]
pub unsafe fn new_bytes_unchecked<S, Tgt>(_: &Tgt, symbol: S) -> Self
where
S: AsRef<[u8]>,
Tgt: Target<'scope>,
{
let bytes = symbol.as_ref();
if let Some(sym) = CACHE.get(bytes) {
return sym;
}
unsafe {
let sym = jl_symbol_n(bytes.as_ptr().cast(), bytes.len());
let sym = Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private);
CACHE.insert(bytes.into(), sym);
sym
}
}
#[inline]
pub fn generate<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'scope>,
{
unsafe {
let sym = jl_gensym();
Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
}
}
#[inline]
pub fn generate_tagged<S, Tgt>(_: &Tgt, tag: S) -> Self
where
S: AsRef<str>,
Tgt: Target<'scope>,
{
unsafe {
let tag = tag.as_ref().as_bytes();
let sym = jl_tagged_gensym(tag.as_ptr() as _, tag.len());
Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
}
}
#[inline]
pub fn extend<'target, Tgt>(self, _: &Tgt) -> Symbol<'target>
where
Tgt: Target<'target>,
{
unsafe { Symbol::wrap_non_null(self.unwrap_non_null(Private), Private) }
}
#[inline]
pub fn as_string(self) -> JlrsResult<String> {
self.as_str().map(Into::into)
}
#[inline]
pub fn as_str(self) -> JlrsResult<&'scope str> {
unsafe {
let ptr = jlrs_symbol_name(self.unwrap(Private)).cast();
let symbol = CStr::from_ptr(ptr);
Ok(symbol.to_str().map_err(JlrsError::other)?)
}
}
#[inline]
pub fn as_cstr(self) -> &'scope CStr {
unsafe {
let ptr = jlrs_symbol_name(self.unwrap(Private));
&CStr::from_ptr(ptr.cast())
}
}
#[inline]
pub fn as_bytes(self) -> &'scope [u8] {
unsafe {
let ptr = jlrs_symbol_name(self.unwrap(Private)).cast();
let symbol = CStr::from_ptr(ptr);
symbol.to_bytes()
}
}
fn hash(self) -> usize {
unsafe { jlrs_symbol_hash(self.unwrap(Private)) }
}
}
impl Hash for Symbol<'_> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_usize((*self).hash())
}
}
impl<S: StaticSymbol> PartialEq<S> for Symbol<'_> {
fn eq(&self, _: &S) -> bool {
unsafe {
let unrooted = Unrooted::new();
let other = S::get_symbol(&unrooted);
self.0 == other.0 && self.1 == other.1
}
}
}
impl<S: StaticSymbol> PartialEq<Sym<'_, S>> for Symbol<'_> {
fn eq(&self, _: &Sym<S>) -> bool {
unsafe {
let unrooted = Unrooted::new();
let other = S::get_symbol(&unrooted);
self.0 == other.0 && self.1 == other.1
}
}
}
impl<S: StaticSymbol> PartialEq<Sym<'_, PhantomData<S>>> for Symbol<'_> {
fn eq(&self, _: &Sym<PhantomData<S>>) -> bool {
unsafe {
let unrooted = Unrooted::new();
let other = S::get_symbol(&unrooted);
self.0 == other.0 && self.1 == other.1
}
}
}
impl_julia_typecheck!(Symbol<'scope>, jl_symbol_type, 'scope);
impl_debug!(Symbol<'_>);
impl<'scope> ManagedPriv<'scope, '_> for Symbol<'scope> {
type Wraps = jl_sym_t;
type WithLifetimes<'target, 'da> = Symbol<'target>;
const NAME: &'static str = "Symbol";
#[inline]
unsafe fn wrap_non_null(inner: NonNull<Self::Wraps>, _: Private) -> Self {
Self(inner, PhantomData)
}
#[inline]
fn unwrap_non_null(self, _: Private) -> NonNull<Self::Wraps> {
self.0
}
}
impl_construct_type_managed!(Symbol, 1, jl_symbol_type);
pub type WeakSymbol<'scope> = Weak<'scope, 'static, Symbol<'scope>>;
pub type SymbolRet = WeakSymbol<'static>;
impl_valid_layout!(WeakSymbol, Symbol, jl_symbol_type);
use crate::memory::target::TargetType;
pub type SymbolData<'target, Tgt> = <Tgt as TargetType<'target>>::Data<'static, Symbol<'target>>;
pub type SymbolResult<'target, Tgt> = TargetResult<'target, 'static, Symbol<'target>, Tgt>;
pub type SymbolUnbound = Symbol<'static>;
impl_ccall_arg_managed!(Symbol, 1);
impl_into_typed!(Symbol);