rustmex 0.6.4

Rustmex: providing convenient Rust bindings to Matlab MEX API's
Documentation
/*!
 * Numeric data classes
 *
 */
#![allow(unused_unsafe)]
use std::os::raw::c_void;
use core::fmt::Debug;
use core::ops::DerefMut;
use std::slice::{
	from_raw_parts,
	from_raw_parts_mut
};

use ndarray::{
	Array,
	ArrayViewD,
	ArrayViewMutD,
	Dimension,
};

pub use rustmex_core::numeric::{
	MatlabNumber,
	NumericArray,
	MutNumericArray,
};


use rustmex_core::{
	mxArray,
	pointers::{
		MxArray,
		MatlabPtr,
		MutMatlabPtr,
	},

	num_struct,


	data_or_dangling,
	data_shape_ok,

	mappable::*,

	convert::{
		ToMatlabResult,
		FromVec,
		VecLayout,
		FromMatlabError,
	},
	shim::{
		rustmex_create_uninit_numeric_array,
	},

	MatlabClass,
	MutMatlabClass,
	OwnedMatlabClass,
	NewEmpty,
	TakeData,
	Complexity,
	raw::{
		mwSize,
		mxClassID,
	},
};

// Complex array with interleaved data. Available on the `matlab800` backend with the
// `complex` feature enabled.
#[cfg(any(doc, all(feature = "complex", feature="matlab800")))]
pub use rustmex_interleaved_complex::InterleavedComplexArray;

// Complex array with separated data. Available on the `matlab700` and `octave` backends
// with the `complex` feature enabled.
#[cfg(any(doc, all(feature = "complex", any(feature="matlab700", feature="octave"))))]
pub use rustmex_separated_complex::SeparatedComplexArray;

/// Complex array re-export. Depending
#[cfg(any(doc, all(feature="complex", feature="matlab800")))]
pub use InterleavedComplexArray as ComplexArray;

#[cfg(all(feature="complex", any(feature="matlab700", feature="octave")))]
pub use SeparatedComplexArray as ComplexArray;

num_struct!(
/**
 Real numeric array
 */
Numeric, Complexity::Real);


pub type OwnedNumeric<T> = Numeric<T, MxArray>;

extern "Rust" {
	fn rustmex_set_real_data(mx: *mut mxArray, newdata: Real<*mut c_void>);
	fn rustmex_get_real_data(mx: *const mxArray) -> Real<*mut c_void>;
}

macro_rules! data_access {
	($s:ident, $t1:ty, $accessor:ident, $builder:ident) => {{
		let ptr = data_or_dangling!(unsafe { $accessor($s.array.deref()) }.0, $t1 );
		let numel = $s.array.numel();
		unsafe { $builder(ptr, numel) }
	}}
}

impl<'p, T, P> NumericArray<'p> for Numeric<T, P> where
	T: MatlabNumber + 'p,
	P: MatlabPtr + 'p,
{
	type Data = &'p [T];
	fn data(&self) -> Self::Data {
		data_access!(self, *const T, rustmex_get_real_data, from_raw_parts)
	}
}

impl<'p, T, P> MutNumericArray<'p> for Numeric<T, P> where
	T: MatlabNumber + 'p,
	P: MutMatlabPtr + 'p,
{
	type MutData = &'p mut [T];
	fn mut_data(&mut self) -> Self::MutData {
		data_access!(self, *mut T, rustmex_get_real_data, from_raw_parts_mut)
	}
}

impl<T, P> TakeData<P> for Numeric<T, P> where
	T: MatlabNumber,
	P: MutMatlabPtr,
{
	type OwnedData = Box<[T]>;
	fn take_data(&mut self) -> Self::OwnedData {

		// use mut_data to get the data slice, since that method implements that
		// correctly
		let data = self.mut_data();

		// SAFETY: Omg this is such a mess
		// Technically, Vec wants to know the _original_ size of the allocation,
		// which is something we can't get out of matlab. So there are two
		// options: either do a complicated and expensive realloc, or (possibly)
		// lie to Vec and Box about the size of the allocation.
		//
		// For now, we're doing the latter. The Matlab allocator does not care
		// about the allocated size, after all, it only cares about the pointer
		// it gets (it probably tracks the size internally).
		let data = unsafe { Vec::from_raw_parts(data.as_mut_ptr(), data.len(), data.len()) }
			.into_boxed_slice();

		// SAFETY: Set the data pointer of the array to NULL, so we don't have an
		// aliased pointer lying around.
		unsafe { rustmex_set_real_data(self.deref_mut(), Real(core::ptr::null_mut())) };

		data
	}
}

macro_rules! construct_set {
	($data:ident, $shape:ident, $complex:literal) => {{
		data_shape_ok!($data, $shape);
		let mx = rustmex_core::create_uninit_numeric_array!($shape, T, Complexity::Real);
		unsafe { rustmex_set_real_data(mx, Real(Box::into_raw($data) as *mut core::ffi::c_void)); }
		mx
	}}
}

impl<T: MatlabNumber> Numeric<T, MxArray> {
	pub fn new(data: Box<[T]>, shape: &[usize]) -> ToMatlabResult<Self, Box<[T]>> {
		Ok(Self::construct(unsafe {MxArray::assume_responsibility_ptr(construct_set!(data, shape, false))}))
	}
}

/**
 * Convert a numeric array into a view into that array
 */
impl<'a, T> From<Numeric<T, &'a mxArray>> for ArrayViewD<'a, T> where
	T: MatlabNumber
{
	fn from(num: Numeric<T, &'a mxArray>) -> Self {
		(&num).into()
	}
}

impl<'a, T> From<&Numeric<T, &'a mxArray>> for ArrayViewD<'a, T> where
	T: MatlabNumber
{
	fn from(num: &Numeric<T, &'a mxArray>) -> Self {
		let data = num.data();
		let dims = num.dimensions();
		rustmex_core::from_num_to_ndarray!(data, dims)
	}
}

impl<'a, T> From<&Numeric<T, &'a mxArray>> for &'a [T] where
	T: MatlabNumber
{
	fn from(num: &Numeric<T, &'a mxArray>) -> Self {
		num.data()
	}
}

impl<'a, T> From<Numeric<T, &'a mxArray>> for &'a [T] where
	T: MatlabNumber
{
	fn from(num: Numeric<T, &'a mxArray>) -> Self {
		num.into()
	}
}

/**
 * Convert a numeric array, with a mutable borrow to the underlying [`mxArray`], to a
 * mutable view into that array
 */
impl<'a, T> From<Numeric<T, &'a mut mxArray>> for ArrayViewMutD<'a, T> where
	T: MatlabNumber
{
	fn from(mut num: Numeric<T, &'a mut mxArray>) -> Self {
		(&mut num).into()
	}
}

impl<'a, T> From<&mut Numeric<T, &'a mut mxArray>> for ArrayViewMutD<'a, T> where
	T: MatlabNumber,
{
	fn from(num: &mut Numeric<T, &'a mut mxArray>) -> Self {
		let data = num.mut_data();
		let dims = num.dimensions();
		rustmex_core::from_num_to_ndarray!(data, dims)
	}
}

impl<'a, T, D> From<Array<T, D>> for Numeric<T, MxArray> where
	T: MatlabNumber,
	D: Dimension,
{
	fn from(mut arr: Array<T, D>) -> Self {
		rustmex_core::from_ndarray_to_num!(arr)
	}
}

/// Store a scalar into a Matlab class, to be returned to Matlab.
impl<T> From<T> for Numeric<T, MxArray> where T: MatlabNumber {
	fn from(t: T) -> Self {
		Numeric::new(Box::new([t]), &[1, 1]).unwrap()
	}
}

/// Create an empty numeric array for type T
impl<T> From<[T;0]> for Numeric<T, MxArray> where T: MatlabNumber {
	fn from(_empty: [T;0]) -> Self {
		Self::new_empty()
	}
}

/// Create an empty numeric array of type [`f64`]; the default Matlab empty array type.
impl From<()> for Numeric<f64, MxArray> {
	fn from(_nothing: ()) -> Self {
		Self::new_empty()
	}
}

impl<T> FromVec<T> for Numeric<T, MxArray> where T: MatlabNumber {
	fn from_boxed_slice<L: VecLayout>(b: Box<[T]>, l: L) -> Self {
		let len = b.len();
		Self::new(b, l.layout(len).as_ref()).expect("Conversion from Vec-ish type should be infallible")
	}
}