use crate::array::Array;
use crate::error::{AllocError, JlrsResult};
use crate::frame::{DynamicFrame, Output, StaticFrame};
use crate::symbol::Symbol;
use std::borrow::Cow;
macro_rules! p {
($trait:ident, $type:ty, $($bounds:tt)+) => {
impl<$($bounds)+> $trait for $type {}
};
($trait:ident, $type:ty) => {
impl $trait for $type {}
};
}
pub trait TemporarySymbol: private::TemporarySymbol {}
pub trait IntoJulia: private::IntoJulia {}
pub trait JuliaType: private::JuliaType {}
pub trait ArrayDatatype: private::ArrayDatatype + JuliaType {}
pub trait TryUnbox: private::TryUnbox {}
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);
p!(IntoJulia, bool);
p!(IntoJulia, char);
p!(IntoJulia, u8);
p!(IntoJulia, u16);
p!(IntoJulia, u32);
p!(IntoJulia, u64);
p!(IntoJulia, i8);
p!(IntoJulia, i16);
p!(IntoJulia, i32);
p!(IntoJulia, i64);
p!(IntoJulia, f32);
p!(IntoJulia, f64);
p!(IntoJulia, usize);
p!(IntoJulia, isize);
p!(IntoJulia, String);
p!(IntoJulia, &dyn AsRef<str>);
p!(IntoJulia, &'a str, 'a);
p!(IntoJulia, Cow<'a, str>, 'a);
p!(JuliaType, bool);
p!(JuliaType, char);
p!(JuliaType, u8);
p!(JuliaType, u16);
p!(JuliaType, u32);
p!(JuliaType, u64);
p!(JuliaType, i8);
p!(JuliaType, i16);
p!(JuliaType, i32);
p!(JuliaType, i64);
p!(JuliaType, f32);
p!(JuliaType, f64);
p!(JuliaType, usize);
p!(JuliaType, isize);
p!(ArrayDatatype, u8);
p!(ArrayDatatype, u16);
p!(ArrayDatatype, u32);
p!(ArrayDatatype, u64);
p!(ArrayDatatype, i8);
p!(ArrayDatatype, i16);
p!(ArrayDatatype, i32);
p!(ArrayDatatype, i64);
p!(ArrayDatatype, f32);
p!(ArrayDatatype, f64);
p!(ArrayDatatype, usize);
p!(ArrayDatatype, isize);
p!(TryUnbox, bool);
p!(TryUnbox, char);
p!(TryUnbox, u8);
p!(TryUnbox, u16);
p!(TryUnbox, u32);
p!(TryUnbox, u64);
p!(TryUnbox, i8);
p!(TryUnbox, i16);
p!(TryUnbox, i32);
p!(TryUnbox, i64);
p!(TryUnbox, f32);
p!(TryUnbox, f64);
p!(TryUnbox, usize);
p!(TryUnbox, isize);
p!(TryUnbox, String);
p!(TryUnbox, Array<A>, A: ArrayDatatype);
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
}
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
}
fn print_memory(&self) {
self.memory.print_memory()
}
}
pub(crate) mod private {
use crate::array::Array;
use crate::array::Dimensions;
use crate::error::{AllocError, JlrsError, JlrsResult};
use crate::frame::{DynamicFrame, Output, StaticFrame};
use crate::stack::FrameIdx;
use crate::symbol::Symbol;
use crate::value::{Value, Values};
use jl_sys::{
jl_array_data, jl_array_dim, jl_array_dims, jl_array_eltype, jl_array_ndims,
jl_array_nrows, 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_float32_type, jl_float64_type, jl_int16_type,
jl_int32_type, jl_int64_type, jl_int8_type, jl_is_array, jl_is_string, jl_pchar_to_string,
jl_string_data, jl_string_len, jl_typeis, 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, jl_symbol_n
};
use std::borrow::Cow;
use std::mem::size_of;
pub struct Internal;
pub trait TemporarySymbol {
unsafe fn temporary_symbol<'symbol>(&self, _: Internal) -> Symbol<'symbol>;
}
pub trait IntoJulia {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t;
}
pub trait JuliaType {
unsafe fn julia_type(_: Internal) -> *mut jl_value_t;
}
pub trait ArrayDatatype: JuliaType {}
pub trait TryUnbox
where
Self: Sized,
{
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self>;
}
pub trait Frame<'frame> {
unsafe fn protect(
&mut self,
value: *mut jl_value_t,
_: Internal,
) -> Result<Value<'frame, 'static>, AllocError>;
fn create_many<P: 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())
}
}
macro_rules! impl_into_julia {
($type:ty, $boxer:ident) => {
impl IntoJulia for $type {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
$boxer(*self)
}
}
};
($type:ty, $as:ty, $boxer:ident) => {
impl IntoJulia for $type {
unsafe fn into_julia(&self, _: Internal) -> *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);
impl IntoJulia for usize {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
if size_of::<usize>() == size_of::<u32>() {
jl_box_uint32(*self as u32)
} else {
jl_box_uint64(*self as u64)
}
}
}
impl IntoJulia for isize {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
if size_of::<isize>() == size_of::<i32>() {
jl_box_int32(*self as i32)
} else {
jl_box_int64(*self as i64)
}
}
}
impl<'a> IntoJulia for &'a str {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
impl<'a> IntoJulia for Cow<'a, str> {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
impl IntoJulia for String {
unsafe fn into_julia(&self, _: Internal) -> *mut jl_value_t {
let ptr = self.as_ptr().cast();
let len = self.len();
jl_pchar_to_string(ptr, len)
}
}
impl IntoJulia for &dyn AsRef<str> {
unsafe fn into_julia(&self, _: Internal) -> *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) => {
impl JuliaType for $type {
unsafe fn julia_type(_: Internal) -> *mut jl_value_t {
$jl_type.cast()
}
}
};
}
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);
impl JuliaType for usize {
unsafe fn julia_type(_: Internal) -> *mut jl_value_t {
if size_of::<usize>() == size_of::<u32>() {
jl_uint32_type.cast()
} else {
jl_uint64_type.cast()
}
}
}
impl JuliaType for isize {
unsafe fn julia_type(_: Internal) -> *mut jl_value_t {
if size_of::<isize>() == size_of::<i32>() {
jl_int32_type.cast()
} else {
jl_int64_type.cast()
}
}
}
macro_rules! impl_array_datatype {
($type:ty) => {
impl ArrayDatatype for $type {}
};
}
impl_array_datatype!(u8);
impl_array_datatype!(u16);
impl_array_datatype!(u32);
impl_array_datatype!(u64);
impl_array_datatype!(i8);
impl_array_datatype!(i16);
impl_array_datatype!(i32);
impl_array_datatype!(i64);
impl_array_datatype!(f32);
impl_array_datatype!(f64);
impl_array_datatype!(usize);
impl_array_datatype!(isize);
macro_rules! impl_try_unbox {
($type:ty, $jl_type:expr, $unboxer:path) => {
impl TryUnbox for $type {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if jl_typeis(value, $jl_type) {
return Ok($unboxer(value));
}
Err(JlrsError::WrongType.into())
}
}
};
}
impl_try_unbox!(u8, jl_uint8_type, jl_unbox_uint8);
impl_try_unbox!(u16, jl_uint16_type, jl_unbox_uint16);
impl_try_unbox!(u32, jl_uint32_type, jl_unbox_uint32);
impl_try_unbox!(u64, jl_uint64_type, jl_unbox_uint64);
impl_try_unbox!(i8, jl_int8_type, jl_unbox_int8);
impl_try_unbox!(i16, jl_int16_type, jl_unbox_int16);
impl_try_unbox!(i32, jl_int32_type, jl_unbox_int32);
impl_try_unbox!(i64, jl_int64_type, jl_unbox_int64);
impl_try_unbox!(f32, jl_float32_type, jl_unbox_float32);
impl_try_unbox!(f64, jl_float64_type, jl_unbox_float64);
impl TryUnbox for bool {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if jl_typeis(value, jl_bool_type) {
return Ok(jl_unbox_int8(value) != 0);
}
Err(JlrsError::WrongType.into())
}
}
impl TryUnbox for char {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if jl_typeis(value, jl_char_type) {
return std::char::from_u32(jl_unbox_uint32(value))
.ok_or(JlrsError::InvalidCharacter.into());
}
Err(JlrsError::WrongType.into())
}
}
impl TryUnbox for usize {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if size_of::<usize>() == size_of::<u32>() {
if jl_typeis(value, jl_uint32_type) {
return Ok(jl_unbox_uint32(value) as usize);
}
} else {
if jl_typeis(value, jl_uint64_type) {
return Ok(jl_unbox_uint64(value) as usize);
}
}
Err(JlrsError::WrongType.into())
}
}
impl TryUnbox for isize {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if size_of::<isize>() == size_of::<i32>() {
if jl_typeis(value, jl_int32_type) {
return Ok(jl_unbox_int32(value) as isize);
}
} else {
if jl_typeis(value, jl_int64_type) {
return Ok(jl_unbox_int64(value) as isize);
}
}
Err(JlrsError::WrongType.into())
}
}
impl TryUnbox for String {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<String> {
if !jl_is_string(value) {
return Err(JlrsError::NotAString.into());
}
let len = jl_string_len(value);
if len == 0 {
return Ok(String::new());
}
let raw = jl_string_data(value);
let raw_slice = std::slice::from_raw_parts(raw, len);
let owned_slice = Vec::from(raw_slice);
Ok(
String::from_utf8(owned_slice).map_err(|e| -> Box<JlrsError> {
let b: Box<dyn std::error::Error + Send + Sync> = Box::new(e);
b.into()
})?,
)
}
}
impl<T: ArrayDatatype> TryUnbox for Array<T> {
unsafe fn try_unbox(value: *mut jl_value_t, _: Internal) -> JlrsResult<Self> {
if !jl_is_array(value) {
return Err(JlrsError::NotAnArray.into());
}
if jl_array_eltype(value) as *mut jl_value_t != T::julia_type(Internal) {
return Err(JlrsError::WrongType.into());
}
let jl_data = jl_array_data(value) as *const T;
let n_dims = jl_array_ndims(value.cast());
let dimensions: Dimensions = match n_dims {
0 => return Err(JlrsError::ZeroDimension.into()),
1 => Into::into(jl_array_nrows(value.cast()) as usize),
2 => Into::into((
jl_array_dim(value.cast(), 0),
jl_array_dim(value.cast(), 1),
)),
3 => Into::into((
jl_array_dim(value.cast(), 0),
jl_array_dim(value.cast(), 1),
jl_array_dim(value.cast(), 2),
)),
ndims => Into::into(jl_array_dims(value.cast(), ndims as _)),
};
let sz = dimensions.size();
let mut data = Vec::with_capacity(sz);
let ptr = data.as_mut_ptr();
std::ptr::copy_nonoverlapping(jl_data, ptr, sz);
data.set_len(sz);
Ok(Array::new(data, dimensions))
}
}
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: 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(Internal).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(Internal).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: 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(Internal).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(Internal).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()) }
}
}
}