use crate::error::{AllocError, JlrsError, JlrsResult};
use crate::frame::{DynamicFrame, Output, StaticFrame};
use crate::value::array::Array;
use crate::value::datatype::DataType;
use crate::value::module::Module;
use crate::value::symbol::Symbol;
use crate::value::Value;
use jl_sys::{
jl_bool_type, jl_box_bool, jl_box_char, jl_box_float32, jl_box_float64, jl_box_int16,
jl_box_int32, jl_box_int64, jl_box_int8, jl_box_uint16, jl_box_uint32, jl_box_uint64,
jl_box_uint8, jl_char_type, jl_datatype_t, jl_float32_type, jl_float64_type, jl_int16_type,
jl_int32_type, jl_int64_type, jl_int8_type, jl_pchar_to_string, jl_string_data, jl_string_len,
jl_uint16_type, jl_uint32_type, jl_uint64_type, jl_uint8_type, jl_unbox_float32,
jl_unbox_float64, jl_unbox_int16, jl_unbox_int32, jl_unbox_int64, jl_unbox_int8,
jl_unbox_uint16, jl_unbox_uint32, jl_unbox_uint64, jl_unbox_uint8, jl_value_t,
};
use std::borrow::Cow;
macro_rules! p {
($trait:ident, $type:ty, $($bounds:tt)+) => {
unsafe impl<$($bounds)+> $trait for $type {}
};
($trait:ident, $type:ty) => {
unsafe impl $trait for $type {}
};
}
pub unsafe trait TemporarySymbol: private::TemporarySymbol {}
pub unsafe trait IntoJulia {
#[doc(hidden)]
unsafe fn into_julia(&self) -> *mut jl_value_t;
}
pub unsafe trait JuliaType {
#[doc(hidden)]
unsafe fn julia_type() -> *mut jl_datatype_t;
}
pub unsafe trait JuliaTuple: JuliaType + IntoJulia + Copy + Clone {}
pub unsafe trait JuliaStruct: JuliaType + IntoJulia + Copy + Clone {}
pub unsafe trait JuliaTypecheck {
#[doc(hidden)]
unsafe fn julia_typecheck(t: DataType) -> bool;
}
pub unsafe trait Cast<'frame, 'data> {
type Output;
#[doc(hidden)]
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output>;
#[doc(hidden)]
unsafe fn cast_unchecked(value: Value<'frame, 'data>) -> Self::Output;
}
pub trait Frame<'frame>: private::Frame<'frame> {
fn frame<'nested, T, F: FnOnce(&mut StaticFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
capacity: usize,
func: F,
) -> JlrsResult<T>;
fn dynamic_frame<'nested, T, F: FnOnce(&mut DynamicFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
func: F,
) -> JlrsResult<T>;
fn output(&mut self) -> JlrsResult<Output<'frame>>;
fn size(&self) -> usize;
#[doc(hidden)]
fn print_memory(&self);
}
p!(TemporarySymbol, String);
p!(TemporarySymbol, &dyn AsRef<str>);
p!(TemporarySymbol, &'a str, 'a);
p!(TemporarySymbol, Cow<'a, str>, 'a);
p!(TemporarySymbol, Symbol<'s>, 's);
macro_rules! impl_into_julia {
($type:ty, $boxer:ident) => {
unsafe impl IntoJulia for $type {
unsafe fn into_julia(&self) -> *mut jl_value_t {
$boxer(*self)
}
}
};
($type:ty, $as:ty, $boxer:ident) => {
unsafe impl IntoJulia for $type {
unsafe fn into_julia(&self) -> *mut jl_value_t {
$boxer(*self as $as)
}
}
};
}
impl_into_julia!(bool, i8, jl_box_bool);
impl_into_julia!(char, u32, jl_box_char);
impl_into_julia!(u8, jl_box_uint8);
impl_into_julia!(u16, jl_box_uint16);
impl_into_julia!(u32, jl_box_uint32);
impl_into_julia!(u64, jl_box_uint64);
impl_into_julia!(i8, jl_box_int8);
impl_into_julia!(i16, jl_box_int16);
impl_into_julia!(i32, jl_box_int32);
impl_into_julia!(i64, jl_box_int64);
impl_into_julia!(f32, jl_box_float32);
impl_into_julia!(f64, jl_box_float64);
#[cfg(not(target_pointer_width = "64"))]
unsafe impl IntoJulia for usize {
#[cfg_attr(tarpaulin, skip)]
unsafe fn into_julia(&self) -> *mut jl_value_t {
jl_box_uint32(*self as u32)
}
}
#[cfg(target_pointer_width = "64")]
unsafe impl IntoJulia for usize {
unsafe fn into_julia(&self) -> *mut jl_value_t {
jl_box_uint64(*self as u64)
}
}
#[cfg(not(target_pointer_width = "64"))]
unsafe impl IntoJulia for isize {
#[cfg_attr(tarpaulin, skip)]
unsafe fn into_julia(&self) -> *mut jl_value_t {
jl_box_int32(*self as i32)
}
}
#[cfg(target_pointer_width = "64")]
unsafe impl IntoJulia for isize {
unsafe fn into_julia(&self) -> *mut jl_value_t {
jl_box_int64(*self as i64)
}
}
unsafe impl<'a> IntoJulia for &'a str {
unsafe fn into_julia(&self) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
unsafe impl<'a> IntoJulia for Cow<'a, str> {
unsafe fn into_julia(&self) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
unsafe impl IntoJulia for String {
unsafe fn into_julia(&self) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
unsafe impl IntoJulia for &dyn AsRef<str> {
unsafe fn into_julia(&self) -> *mut jl_value_t {
let ptr = self.as_ref().as_ptr().cast();
let len = self.as_ref().len();
jl_pchar_to_string(ptr, len)
}
}
macro_rules! impl_julia_type {
($type:ty, $jl_type:expr) => {
unsafe impl JuliaType for $type {
unsafe fn julia_type() -> *mut jl_datatype_t {
$jl_type
}
}
};
}
impl_julia_type!(u8, jl_uint8_type);
impl_julia_type!(u16, jl_uint16_type);
impl_julia_type!(u32, jl_uint32_type);
impl_julia_type!(u64, jl_uint64_type);
impl_julia_type!(i8, jl_int8_type);
impl_julia_type!(i16, jl_int16_type);
impl_julia_type!(i32, jl_int32_type);
impl_julia_type!(i64, jl_int64_type);
impl_julia_type!(f32, jl_float32_type);
impl_julia_type!(f64, jl_float64_type);
impl_julia_type!(bool, jl_bool_type);
impl_julia_type!(char, jl_char_type);
#[cfg(not(target_pointer_width = "64"))]
unsafe impl JuliaType for usize {
#[cfg_attr(tarpaulin, skip)]
unsafe fn julia_type() -> *mut jl_datatype_t {
jl_uint32_type
}
}
#[cfg(target_pointer_width = "64")]
unsafe impl JuliaType for usize {
unsafe fn julia_type() -> *mut jl_datatype_t {
jl_uint64_type
}
}
#[cfg(not(target_pointer_width = "64"))]
unsafe impl JuliaType for isize {
#[cfg_attr(tarpaulin, skip)]
unsafe fn julia_type() -> *mut jl_datatype_t {
jl_int32_type
}
}
#[cfg(target_pointer_width = "64")]
unsafe impl JuliaType for isize {
unsafe fn julia_type() -> *mut jl_datatype_t {
jl_int64_type
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for Array<'frame, 'data> {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<Array>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotAnArray)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
Array::wrap(value.ptr().cast())
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for DataType<'frame> {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<DataType>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotADataType)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
DataType::wrap(value.ptr().cast())
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for Symbol<'frame> {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<Symbol>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotASymbol)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
Symbol::wrap(value.ptr().cast())
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for Module<'frame> {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<Module>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotAModule("This".to_string()))?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
Module::wrap(value.ptr().cast())
}
}
macro_rules! impl_primitive_cast {
($type:ty, $unboxer:ident) => {
unsafe impl<'frame, 'data> Cast<'frame, 'data> for $type {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<$type>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::WrongType)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
$unboxer(value.ptr().cast()) as _
}
}
};
}
impl_primitive_cast!(u8, jl_unbox_uint8);
impl_primitive_cast!(u16, jl_unbox_uint16);
impl_primitive_cast!(u32, jl_unbox_uint32);
impl_primitive_cast!(u64, jl_unbox_uint64);
impl_primitive_cast!(i8, jl_unbox_int8);
impl_primitive_cast!(i16, jl_unbox_int16);
impl_primitive_cast!(i32, jl_unbox_int32);
impl_primitive_cast!(i64, jl_unbox_int64);
impl_primitive_cast!(f32, jl_unbox_float32);
impl_primitive_cast!(f64, jl_unbox_float64);
#[cfg(not(target_pointer_width = "64"))]
impl_primitive_cast!(usize, jl_unbox_uint32);
#[cfg(not(target_pointer_width = "64"))]
impl_primitive_cast!(isize, jl_unbox_int32);
#[cfg(target_pointer_width = "64")]
impl_primitive_cast!(usize, jl_unbox_uint64);
#[cfg(target_pointer_width = "64")]
impl_primitive_cast!(isize, jl_unbox_int64);
unsafe impl<'frame, 'data> Cast<'frame, 'data> for bool {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<bool>() {
unsafe { return Ok(Self::cast_unchecked(value)) }
}
Err(JlrsError::WrongType)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
jl_unbox_int8(value.ptr()) != 0
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for char {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<char>() {
unsafe {
return std::char::from_u32(jl_unbox_uint32(value.ptr()))
.ok_or(JlrsError::InvalidCharacter.into());
}
}
Err(JlrsError::WrongType)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
std::char::from_u32_unchecked(jl_unbox_uint32(value.ptr()))
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for String {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<String>() {
unsafe { return Ok(Self::cast_unchecked(value)) }
}
Err(JlrsError::WrongType)?
}
unsafe fn cast_unchecked<'fr, 'da>(value: Value<'frame, 'data>) -> Self::Output {
let len = jl_string_len(value.ptr());
if len == 0 {
return String::new();
}
let raw = jl_string_data(value.ptr());
let raw_slice = std::slice::from_raw_parts(raw, len);
let owned_slice = Vec::from(raw_slice);
String::from_utf8_unchecked(owned_slice)
}
}
impl<'frame> Frame<'frame> for StaticFrame<'frame> {
fn frame<'nested, T, F: FnOnce(&mut StaticFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
capacity: usize,
func: F,
) -> JlrsResult<T> {
let mut frame = unsafe { self.nested_frame(capacity).unwrap() };
func(&mut frame)
}
fn dynamic_frame<'nested, T, F: FnOnce(&mut DynamicFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
func: F,
) -> JlrsResult<T> {
unsafe {
let mut view = self.memory.nest_dynamic();
let idx = view.new_frame()?;
let mut frame = DynamicFrame {
idx,
len: 0,
memory: view,
};
func(&mut frame)
}
}
fn output(&mut self) -> JlrsResult<Output<'frame>> {
if self.capacity == self.len {
return Err(AllocError::FrameOverflow(1, self.len).into());
}
let out = unsafe {
let out = self.memory.new_output(self.idx, self.len);
self.len += 1;
out
};
Ok(out)
}
fn size(&self) -> usize {
self.len
}
#[cfg_attr(tarpaulin, skip)]
fn print_memory(&self) {
self.memory.print_memory()
}
}
impl<'frame> Frame<'frame> for DynamicFrame<'frame> {
fn dynamic_frame<'nested, T, F: FnOnce(&mut DynamicFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
func: F,
) -> JlrsResult<T> {
let mut frame = unsafe { self.nested_frame().unwrap() };
func(&mut frame)
}
fn frame<'nested, T, F: FnOnce(&mut StaticFrame<'nested>) -> JlrsResult<T>>(
&'nested mut self,
capacity: usize,
func: F,
) -> JlrsResult<T> {
unsafe {
let mut view = self.memory.nest_static();
let idx = view.new_frame(capacity)?;
let mut frame = StaticFrame {
idx,
capacity,
len: 0,
memory: view,
};
func(&mut frame)
}
}
fn output(&mut self) -> JlrsResult<Output<'frame>> {
unsafe {
let out = self.memory.new_output(self.idx)?;
self.len += 1;
Ok(out)
}
}
fn size(&self) -> usize {
self.len
}
#[cfg_attr(tarpaulin, skip)]
fn print_memory(&self) {
self.memory.print_memory()
}
}
pub(crate) mod private {
use crate::error::AllocError;
use crate::frame::{DynamicFrame, Output, StaticFrame};
use crate::stack::FrameIdx;
use crate::value::symbol::Symbol;
use crate::value::{Value, Values};
use jl_sys::jl_symbol_n;
use jl_sys::jl_value_t;
use std::borrow::Cow;
pub struct Internal;
pub trait TemporarySymbol {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol>;
}
pub trait Frame<'frame> {
unsafe fn protect(
&mut self,
value: *mut jl_value_t,
_: Internal,
) -> Result<Value<'frame, 'static>, AllocError>;
fn create_many<P: super::IntoJulia>(
&mut self,
values: &[P],
_: Internal,
) -> Result<Values<'frame>, AllocError>;
fn create_many_dyn(
&mut self,
values: &[&dyn super::IntoJulia],
_: Internal,
) -> Result<Values<'frame>, AllocError>;
fn assign_output<'output>(
&mut self,
output: Output<'output>,
value: *mut jl_value_t,
_: Internal,
) -> Value<'output, 'static>;
}
impl<'a> TemporarySymbol for &'a str {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol> {
let symbol_ptr = self.as_ptr();
let symbol = jl_symbol_n(symbol_ptr.cast(), self.len());
Symbol::wrap(symbol)
}
}
impl<'a> TemporarySymbol for Cow<'a, str> {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol> {
let symbol_ptr = self.as_ptr().cast();
let symbol = jl_symbol_n(symbol_ptr, self.len());
Symbol::wrap(symbol)
}
}
impl TemporarySymbol for String {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol> {
let symbol_ptr = self.as_ptr().cast();
let symbol = jl_symbol_n(symbol_ptr, self.len());
Symbol::wrap(symbol)
}
}
impl TemporarySymbol for &dyn AsRef<str> {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol> {
let symbol_ptr = self.as_ref().as_ptr().cast();
let symbol = jl_symbol_n(symbol_ptr, self.as_ref().len());
Symbol::wrap(symbol)
}
}
impl<'s> TemporarySymbol for Symbol<'s> {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol> {
Symbol::wrap(self.ptr())
}
}
impl<'frame> Frame<'frame> for StaticFrame<'frame> {
unsafe fn protect(
&mut self,
value: *mut jl_value_t,
_: Internal,
) -> Result<Value<'frame, 'static>, AllocError> {
if self.capacity == self.len {
return Err(AllocError::FrameOverflow(1, self.len));
}
let out = {
let out = self.memory.protect(self.idx, self.len, value.cast());
self.len += 1;
out
};
Ok(out)
}
fn create_many<P: super::IntoJulia>(
&mut self,
values: &[P],
_: Internal,
) -> Result<Values<'frame>, AllocError> {
unsafe {
if self.capacity < self.len + values.len() {
return Err(AllocError::FrameOverflow(values.len(), self.capacity()));
}
let offset = self.len;
for value in values {
self.memory
.protect(self.idx, self.len, value.into_julia().cast());
self.len += 1;
}
Ok(self.memory.as_values(self.idx, offset, values.len()))
}
}
fn create_many_dyn(
&mut self,
values: &[&dyn super::IntoJulia],
_: Internal,
) -> Result<Values<'frame>, AllocError> {
unsafe {
if self.capacity < self.len + values.len() {
return Err(AllocError::FrameOverflow(values.len(), self.capacity()));
}
let offset = self.len;
for value in values {
self.memory
.protect(self.idx, self.len, value.into_julia().cast());
self.len += 1;
}
Ok(self.memory.as_values(self.idx, offset, values.len()))
}
}
fn assign_output<'output>(
&mut self,
output: Output<'output>,
value: *mut jl_value_t,
_: Internal,
) -> Value<'output, 'static> {
unsafe {
self.memory
.protect(FrameIdx::default(), output.offset, value.cast())
}
}
}
impl<'frame> Frame<'frame> for DynamicFrame<'frame> {
unsafe fn protect(
&mut self,
value: *mut jl_value_t,
_: Internal,
) -> Result<Value<'frame, 'static>, AllocError> {
let out = self.memory.protect(self.idx, value.cast())?;
self.len += 1;
Ok(out)
}
fn create_many<P: super::IntoJulia>(
&mut self,
values: &[P],
_: Internal,
) -> Result<Values<'frame>, AllocError> {
unsafe {
let offset = self.len;
for value in values {
match self.memory.protect(self.idx, value.into_julia().cast()) {
Ok(_) => (),
Err(AllocError::StackOverflow(_, n)) => {
return Err(AllocError::StackOverflow(values.len(), n))
}
_ => unreachable!(),
}
self.len += 1;
}
Ok(self.memory.as_values(self.idx, offset, values.len()))
}
}
fn create_many_dyn(
&mut self,
values: &[&dyn super::IntoJulia],
_: Internal,
) -> Result<Values<'frame>, AllocError> {
unsafe {
let offset = self.len;
for value in values {
self.memory.protect(self.idx, value.into_julia().cast())?;
self.len += 1;
}
Ok(self.memory.as_values(self.idx, offset, values.len()))
}
}
fn assign_output<'output>(
&mut self,
output: Output<'output>,
value: *mut jl_value_t,
_: Internal,
) -> Value<'output, 'static> {
unsafe { self.memory.protect_output(output, value.cast()) }
}
}
}