rustmex/
numeric.rs

1/*!
2 * Numeric data classes
3 *
4 */
5#![allow(unused_unsafe)]
6use std::os::raw::c_void;
7use core::fmt::Debug;
8use core::ops::DerefMut;
9use std::slice::{
10	from_raw_parts,
11	from_raw_parts_mut
12};
13
14use ndarray::{
15	Array,
16	ArrayViewD,
17	ArrayViewMutD,
18	Dimension,
19};
20
21pub use rustmex_core::numeric::{
22	MatlabNumber,
23	NumericArray,
24	MutNumericArray,
25};
26
27
28use rustmex_core::{
29	mxArray,
30	pointers::{
31		MxArray,
32		MatlabPtr,
33		MutMatlabPtr,
34	},
35
36	num_struct,
37
38
39	data_or_dangling,
40	data_shape_ok,
41
42	mappable::*,
43
44	convert::{
45		ToMatlabResult,
46		FromVec,
47		VecLayout,
48		FromMatlabError,
49	},
50	shim::{
51		rustmex_create_uninit_numeric_array,
52	},
53
54	MatlabClass,
55	MutMatlabClass,
56	OwnedMatlabClass,
57	NewEmpty,
58	TakeData,
59	Complexity,
60	raw::{
61		mwSize,
62		mxClassID,
63	},
64};
65
66// Complex array with interleaved data. Available on the `matlab800` backend with the
67// `complex` feature enabled.
68#[cfg(any(doc, all(feature = "complex", feature="matlab800")))]
69pub use rustmex_interleaved_complex::InterleavedComplexArray;
70
71// Complex array with separated data. Available on the `matlab700` and `octave` backends
72// with the `complex` feature enabled.
73#[cfg(any(doc, all(feature = "complex", any(feature="matlab700", feature="octave"))))]
74pub use rustmex_separated_complex::SeparatedComplexArray;
75
76/// Complex array re-export. Depending
77#[cfg(any(doc, all(feature="complex", feature="matlab800")))]
78pub use InterleavedComplexArray as ComplexArray;
79
80#[cfg(all(feature="complex", any(feature="matlab700", feature="octave")))]
81pub use SeparatedComplexArray as ComplexArray;
82
83num_struct!(
84/**
85 Real numeric array
86 */
87Numeric, Complexity::Real);
88
89
90pub type OwnedNumeric<T> = Numeric<T, MxArray>;
91
92extern "Rust" {
93	fn rustmex_set_real_data(mx: *mut mxArray, newdata: Real<*mut c_void>);
94	fn rustmex_get_real_data(mx: *const mxArray) -> Real<*mut c_void>;
95}
96
97macro_rules! data_access {
98	($s:ident, $t1:ty, $accessor:ident, $builder:ident) => {{
99		let ptr = data_or_dangling!(unsafe { $accessor($s.array.deref()) }.0, $t1 );
100		let numel = $s.array.numel();
101		unsafe { $builder(ptr, numel) }
102	}}
103}
104
105impl<'p, T, P> NumericArray<'p> for Numeric<T, P> where
106	T: MatlabNumber + 'p,
107	P: MatlabPtr + 'p,
108{
109	type Data = &'p [T];
110	fn data(&self) -> Self::Data {
111		data_access!(self, *const T, rustmex_get_real_data, from_raw_parts)
112	}
113}
114
115impl<'p, T, P> MutNumericArray<'p> for Numeric<T, P> where
116	T: MatlabNumber + 'p,
117	P: MutMatlabPtr + 'p,
118{
119	type MutData = &'p mut [T];
120	fn mut_data(&mut self) -> Self::MutData {
121		data_access!(self, *mut T, rustmex_get_real_data, from_raw_parts_mut)
122	}
123}
124
125impl<T, P> TakeData<P> for Numeric<T, P> where
126	T: MatlabNumber,
127	P: MutMatlabPtr,
128{
129	type OwnedData = Box<[T]>;
130	fn take_data(&mut self) -> Self::OwnedData {
131
132		// use mut_data to get the data slice, since that method implements that
133		// correctly
134		let data = self.mut_data();
135
136		// SAFETY: Omg this is such a mess
137		// Technically, Vec wants to know the _original_ size of the allocation,
138		// which is something we can't get out of matlab. So there are two
139		// options: either do a complicated and expensive realloc, or (possibly)
140		// lie to Vec and Box about the size of the allocation.
141		//
142		// For now, we're doing the latter. The Matlab allocator does not care
143		// about the allocated size, after all, it only cares about the pointer
144		// it gets (it probably tracks the size internally).
145		let data = unsafe { Vec::from_raw_parts(data.as_mut_ptr(), data.len(), data.len()) }
146			.into_boxed_slice();
147
148		// SAFETY: Set the data pointer of the array to NULL, so we don't have an
149		// aliased pointer lying around.
150		unsafe { rustmex_set_real_data(self.deref_mut(), Real(core::ptr::null_mut())) };
151
152		data
153	}
154}
155
156macro_rules! construct_set {
157	($data:ident, $shape:ident, $complex:literal) => {{
158		data_shape_ok!($data, $shape);
159		let mx = rustmex_core::create_uninit_numeric_array!($shape, T, Complexity::Real);
160		unsafe { rustmex_set_real_data(mx, Real(Box::into_raw($data) as *mut core::ffi::c_void)); }
161		mx
162	}}
163}
164
165impl<T: MatlabNumber> Numeric<T, MxArray> {
166	pub fn new(data: Box<[T]>, shape: &[usize]) -> ToMatlabResult<Self, Box<[T]>> {
167		Ok(Self::construct(unsafe {MxArray::assume_responsibility_ptr(construct_set!(data, shape, false))}))
168	}
169}
170
171/**
172 * Convert a numeric array into a view into that array
173 */
174impl<'a, T> From<Numeric<T, &'a mxArray>> for ArrayViewD<'a, T> where
175	T: MatlabNumber
176{
177	fn from(num: Numeric<T, &'a mxArray>) -> Self {
178		(&num).into()
179	}
180}
181
182impl<'a, T> From<&Numeric<T, &'a mxArray>> for ArrayViewD<'a, T> where
183	T: MatlabNumber
184{
185	fn from(num: &Numeric<T, &'a mxArray>) -> Self {
186		let data = num.data();
187		let dims = num.dimensions();
188		rustmex_core::from_num_to_ndarray!(data, dims)
189	}
190}
191
192impl<'a, T> From<&Numeric<T, &'a mxArray>> for &'a [T] where
193	T: MatlabNumber
194{
195	fn from(num: &Numeric<T, &'a mxArray>) -> Self {
196		num.data()
197	}
198}
199
200impl<'a, T> From<Numeric<T, &'a mxArray>> for &'a [T] where
201	T: MatlabNumber
202{
203	fn from(num: Numeric<T, &'a mxArray>) -> Self {
204		num.into()
205	}
206}
207
208/**
209 * Convert a numeric array, with a mutable borrow to the underlying [`mxArray`], to a
210 * mutable view into that array
211 */
212impl<'a, T> From<Numeric<T, &'a mut mxArray>> for ArrayViewMutD<'a, T> where
213	T: MatlabNumber
214{
215	fn from(mut num: Numeric<T, &'a mut mxArray>) -> Self {
216		(&mut num).into()
217	}
218}
219
220impl<'a, T> From<&mut Numeric<T, &'a mut mxArray>> for ArrayViewMutD<'a, T> where
221	T: MatlabNumber,
222{
223	fn from(num: &mut Numeric<T, &'a mut mxArray>) -> Self {
224		let data = num.mut_data();
225		let dims = num.dimensions();
226		rustmex_core::from_num_to_ndarray!(data, dims)
227	}
228}
229
230impl<'a, T, D> From<Array<T, D>> for Numeric<T, MxArray> where
231	T: MatlabNumber,
232	D: Dimension,
233{
234	fn from(mut arr: Array<T, D>) -> Self {
235		rustmex_core::from_ndarray_to_num!(arr)
236	}
237}
238
239/// Store a scalar into a Matlab class, to be returned to Matlab.
240impl<T> From<T> for Numeric<T, MxArray> where T: MatlabNumber {
241	fn from(t: T) -> Self {
242		Numeric::new(Box::new([t]), &[1, 1]).unwrap()
243	}
244}
245
246/// Create an empty numeric array for type T
247impl<T> From<[T;0]> for Numeric<T, MxArray> where T: MatlabNumber {
248	fn from(_empty: [T;0]) -> Self {
249		Self::new_empty()
250	}
251}
252
253/// Create an empty numeric array of type [`f64`]; the default Matlab empty array type.
254impl From<()> for Numeric<f64, MxArray> {
255	fn from(_nothing: ()) -> Self {
256		Self::new_empty()
257	}
258}
259
260impl<T> FromVec<T> for Numeric<T, MxArray> where T: MatlabNumber {
261	fn from_boxed_slice<L: VecLayout>(b: Box<[T]>, l: L) -> Self {
262		let len = b.len();
263		Self::new(b, l.layout(len).as_ref()).expect("Conversion from Vec-ish type should be infallible")
264	}
265}