1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
//! Unobox the contents of a Julia value.
//!
//! A [`Value`] contains a pointer to some data owned by Julia. The layout of this data depends on
//! its [`DataType`]. It's often possible to provide a type defined in Rust that matches the
//! layout of the data in Julia. For example, if the `DataType` is `Int8`, the pointer points to
//! an `i8`. Extracting the contents of a `Value` is called unboxing.
//!
//! The [`Unbox`] trait defined in this module usually dereferences the pointer. There are a few
//! exceptions to this rule. In particular, unboxing a `char` or a `bool` results in a [`Char`] or
//! a [`Bool`] respectively. The reason is that while using invalid `Char`s and `Bool`s is an
//! error in Julia, it's undefined behavior to create them in Rust. Similarly, strings in Julia
//! should be UTF-8 encoded, but to account for the possibility that the contents are invalid the
//! implementation of `Unbox` returns a `String` if the contents are valid and a `Vec<u8>`
//! otherwise.
//!
//! Unlike [`IntoJulia`], the `Unbox` trait is not limited to bits-types. The only requirement is
//! that the layout of the types in both languages match. Types that can be unboxed include those
//! with pointer fields, type parameters, and bits unions. When wrappers are generated with
//! JlrsReflect.jl [`Unbox`] is always derived.
//!
//! [`Cast`]: crate::convert::cast::Cast
//! [`Bool`]: crate::wrappers::inline::bool::Bool
//! [`Char`]: crate::wrappers::inline::char::Char
//! [`DataType`]: crate::wrappers::ptr::datatype::DataType
//! [`IntoJulia`]: crate::convert::into_julia::IntoJulia
use crate::wrappers::ptr::value::Value;
use jl_sys::{
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_unbox_voidpointer,
};
use std::ffi::c_void;
use super::into_julia::IntoJulia;
/// A trait implemented by types that can be extracted from a Julia value with [`Value::unbox`].
///
/// This trait can be derived, it's recommended
/// to use JlrsReflect.jl to ensure it's implemented correctly. All wrappers generated by
/// JlrsReflect.jl will implement this trait and [`ValidLayout`], which checks if the conversion
/// is valid at runtime.
///
/// If you do choose to implement it manually, you only need to provide the associated `Output`
/// type if the type matches the layout of the data in Julia. The default implementation of
/// `unbox` dereferences the value as `&Self::Output` and clones it. If this implementation is
/// incorrect it must be overridden.
///
/// [`Value::unbox`]: crate::wrappers::ptr::value::Value::unbox
/// [`Value::unbox_unchecked`]: crate::wrappers::ptr::value::Value::unbox_unchecked
/// [`ValidLayout`]: crate::layout::valid_layout::ValidLayout
pub unsafe trait Unbox {
/// The type of the unboxed data. Must be `#[repr(C)]`.
type Output: Sized + Clone;
/// Unbox the value as `Self::Output`.
///
/// Safety: The default implementation assumes that `Self::Output` is the correct layout for
/// the data that `value` points to.
#[inline(always)]
unsafe fn unbox(value: Value) -> Self::Output {
value.data_ptr().cast::<Self::Output>().as_ref().clone()
}
}
macro_rules! impl_unboxer {
($type:ty, $unboxer:expr) => {
unsafe impl Unbox for $type {
type Output = Self;
#[inline(always)]
unsafe fn unbox(value: Value) -> $type {
$unboxer(
<Value as crate::wrappers::ptr::private::WrapperPriv>::unwrap(
value,
$crate::private::Private,
),
) as _
}
}
};
}
impl_unboxer!(u8, jl_unbox_uint8);
impl_unboxer!(u16, jl_unbox_uint16);
impl_unboxer!(u32, jl_unbox_uint32);
impl_unboxer!(u64, jl_unbox_uint64);
impl_unboxer!(i8, jl_unbox_int8);
impl_unboxer!(i16, jl_unbox_int16);
impl_unboxer!(i32, jl_unbox_int32);
impl_unboxer!(i64, jl_unbox_int64);
impl_unboxer!(f32, jl_unbox_float32);
impl_unboxer!(f64, jl_unbox_float64);
impl_unboxer!(*mut c_void, jl_unbox_voidpointer);
#[cfg(not(target_pointer_width = "64"))]
impl_unboxer!(usize, jl_unbox_uint32);
#[cfg(not(target_pointer_width = "64"))]
impl_unboxer!(isize, jl_unbox_int32);
#[cfg(target_pointer_width = "64")]
impl_unboxer!(usize, jl_unbox_uint64);
#[cfg(target_pointer_width = "64")]
impl_unboxer!(isize, jl_unbox_int64);
// Safety: *mut T and Ptr{T} have the same layout
unsafe impl<T: IntoJulia> Unbox for *mut T {
type Output = Self;
}