rustmex_separated_complex/
lib.rs

1#![allow(unused_unsafe)]
2use std::slice::{from_raw_parts_mut, from_raw_parts};
3use core::ops::DerefMut;
4use rustmex_core::{
5	mxArray,
6
7	num_struct,
8	data_or_dangling,
9	create_uninit_numeric_array,
10	Complexity,
11
12	numeric::{NumericArray, ComplexNumericArray, MutNumericArray, MatlabNumber},
13	pointers::{MatlabPtr, MutMatlabPtr, MxArray},
14	mappable::{*},
15	convert::{ToMatlabResult, DataShapeMismatch, FromMatlabError},
16	shim::rustmex_create_uninit_numeric_array,
17	raw::{
18		mxClassID,
19		mwSize,
20	},
21
22	MatlabClass,
23	MutMatlabClass,
24	OwnedMatlabClass,
25	NewEmpty,
26	TakeData,
27};
28
29use num_complex::Complex;
30
31use ndarray::{
32	Array,
33	Dimension,
34	ArrayD,
35	ArrayViewD,
36	ArrayViewMutD,
37	Zip,
38};
39
40use core::ffi::c_void;
41
42// If these symbols don't exist in the backend, this crate does not compile. This was the
43// best way I could of to ensure the interleaved and separated implementations don't get
44// mixed up.
45extern "Rust" {
46	fn rustmex_get_separated_complex(mx: *const mxArray) -> Complex<*mut c_void>;
47	fn rustmex_set_separated_complex(mx: *mut mxArray, newdata: Complex<*mut c_void>);
48}
49
50num_struct!(SeparatedComplexArray, Complexity::Complex);
51
52macro_rules! data_access {
53	($s:ident, $t1:ty, $builder:ident) => {{
54		let numel = $s.array.numel();
55		unsafe { rustmex_get_separated_complex($s.array.deref()) }
56			.map(|elem| data_or_dangling!(elem, $t1))
57			.map(|elem| unsafe { $builder(elem, numel) } )
58	}}
59}
60
61impl<'p, T, P> NumericArray<'p> for SeparatedComplexArray<T, P> where
62	T: MatlabNumber + 'p,
63	P: MatlabPtr + 'p,
64{
65	type Data = Complex<&'p [T]>;
66	fn data(&self) -> Self::Data {
67		data_access!(self, *const T, from_raw_parts)
68	}
69}
70
71impl<'p, T, P> MutNumericArray<'p> for SeparatedComplexArray<T, P> where
72	T: MatlabNumber + 'p,
73	P: MutMatlabPtr + 'p,
74{
75	type MutData = Complex<&'p mut [T]>;
76	fn mut_data(&mut self) -> Self::MutData {
77		data_access!(self, *mut T, from_raw_parts_mut)
78	}
79}
80
81impl<T, P> TakeData<P> for SeparatedComplexArray<T, P> where
82	T: MatlabNumber,
83	P: MutMatlabPtr,
84{
85	type OwnedData = Complex<Box<[T]>>;
86	fn take_data(&mut self) -> Self::OwnedData {
87
88		// use mut_data to get the data slice, since that method implements that
89		// correctly
90		let data = self.mut_data();
91
92		// SAFETY: Omg this is such a mess
93		// Technically, Vec wants to know the _original_ size of the allocation,
94		// which is something we can't get out of matlab. So there are two
95		// options: either do a complicated and expensive realloc, or (possibly)
96		// lie to Vec and Box about the size of the allocation.
97		//
98		// For now, we're doing the latter. The Matlab allocator does not care
99		// about the allocated size, after all, it only cares about the pointer
100		// it gets (it probably tracks the size internally).
101		let data = data.map(|part| {
102			unsafe { Vec::from_raw_parts(part.as_mut_ptr(), part.len(), part.len()) }
103				.into_boxed_slice()
104		});
105
106		// SAFETY: Set the data pointer of the array to NULL, so we don't have an
107		// aliased pointer lying around.
108		unsafe { rustmex_set_separated_complex(self.deref_mut(),
109			Complex::init_value(core::ptr::null_mut())) };
110
111		data
112	}
113}
114
115impl<'p, T, P> ComplexNumericArray<'p> for SeparatedComplexArray<T, P> where
116	T: MatlabNumber + 'p,
117	P: MatlabPtr + 'p,
118{
119	type CDT = T;
120}
121
122macro_rules! copy_complex {
123	($data:ident, $f:expr) => {{
124		let mut v = Vec::with_capacity($data.len());
125		v.extend($data.iter().map($f));
126		v.into_boxed_slice()
127	}}
128}
129
130impl<T: MatlabNumber + Copy> SeparatedComplexArray<T, MxArray> {
131	pub fn new(data: Box<[Complex<T>]>, shape: &[usize]) -> ToMatlabResult<Self, Box<[Complex<T>]>> {
132		let dimprod = shape.iter().product::<usize>();
133		if dimprod != data.len() {
134			return Err(DataShapeMismatch::because_numel_shape(data));
135		}
136		let split = Complex {
137			re: copy_complex!(data, |x|x.re),
138			im: copy_complex!(data, |x|x.im)
139		};
140		Ok(Self::new_separated_unchecked(split, shape))
141	}
142}
143
144impl<T: MatlabNumber> SeparatedComplexArray<T, MxArray> {
145	pub fn new_separated(data: Complex<Box<[T]>>, shape: &[usize]) -> ToMatlabResult<Self, Complex<Box<[T]>>> {
146		if data.re.len() != data.im.len() {
147			return Err(DataShapeMismatch::because_im_re(data));
148		}
149
150		if shape.iter().product::<usize>() != data.re.len() {
151			return Err(DataShapeMismatch::because_numel_shape(data));
152		}
153
154		Ok(Self::new_separated_unchecked(data, shape))
155	}
156
157	fn new_separated_unchecked(data: Complex<Box<[T]>>, shape: &[usize]) -> Self {
158		let mx = create_uninit_numeric_array!(shape, T, Complexity::Real);
159
160		unsafe { rustmex_set_separated_complex(mx, data.map(|x| Box::into_raw(x) as *mut core::ffi::c_void)); }
161
162		Self::construct(unsafe { MxArray::assume_responsibility_ptr(mx)})
163	}
164}
165
166impl<'a, 'b, T, P> From<&'b SeparatedComplexArray<T, P>> for Complex<ArrayViewD<'a, T>> where
167	T: MatlabNumber,
168	P: MatlabPtr + 'a,
169	'b: 'a,
170{
171	fn from(num: &SeparatedComplexArray<T, P>) -> Self {
172		let data = num.data();
173		let dimensions = num.dimensions();
174
175		data.map(|part| rustmex_core::from_num_to_ndarray!(part, dimensions, ArrayViewD<T>))
176	}
177}
178
179impl<'a, T> From<SeparatedComplexArray<T, &'a mxArray>> for Complex<ArrayViewD<'a, T>> where
180	T: MatlabNumber,
181{
182	fn from(arr: SeparatedComplexArray<T, &'a mxArray>) -> Self {
183		arr.into()
184	}
185}
186
187impl<'a, 'b, T, P> From<&'b mut SeparatedComplexArray<T, P>> for Complex<ArrayViewMutD<'a, T>> where
188	T: MatlabNumber,
189	P: MutMatlabPtr + 'a,
190	'b: 'a,
191{
192	fn from(num: &mut SeparatedComplexArray<T, P>) -> Self {
193		let data: Complex<&mut[T]> = num.mut_data();
194		let dimensions = num.dimensions();
195
196		data.map(|part| rustmex_core::from_num_to_ndarray!(part, dimensions, ArrayViewMutD<T>))
197	}
198}
199
200impl<'a, T> From<SeparatedComplexArray<T, &'a mut mxArray>> for Complex<ArrayViewMutD<'a, T>> where
201	T: MatlabNumber,
202{
203	fn from(arr: SeparatedComplexArray<T, &'a mut mxArray>) -> Self {
204		arr.into()
205	}
206}
207
208
209impl<T, P> From<SeparatedComplexArray<T, P>> for ArrayD<Complex<T>> where
210	T: MatlabNumber,
211	P: MatlabPtr,
212{
213	fn from(arr: SeparatedComplexArray<T, P>) -> Self {
214		// Technically one could construct the Array just from the two slices,
215		// but that is massively combersome, so just create two views into the
216		// real and imaginary part, and then zip them together. The cost of
217		// creating two views will in general be overshadowed by the copy anyway,
218		// but now we don't duplicate the array construction code.
219		let arr: Complex<ArrayViewD<T>> = (&arr).into();
220		Zip::from(&arr.re).and(&arr.im).map_collect(|&re, &im| Complex { re, im })
221	}
222}
223
224impl<T> From<Complex<T>> for SeparatedComplexArray<T, MxArray> where T: MatlabNumber {
225	fn from(c: Complex<T>) -> Self {
226		let alloced = c.map(|val| Box::from([val]));
227		Self::new_separated_unchecked(alloced, &[1,1])
228	}
229}
230
231impl<T> From<[Complex<T>;0]> for SeparatedComplexArray<T, MxArray> where T: MatlabNumber {
232	fn from(_empty: [Complex<T>;0]) -> Self {
233		Self::new_empty()
234	}
235}
236
237impl<'a, T, D> From<Array<Complex<T>, D>> for SeparatedComplexArray<T, MxArray> where
238	T: MatlabNumber + Copy,
239	D: Dimension,
240	SeparatedComplexArray<T, MxArray>: TryFrom<Complex<Array<T, D>>>
241{
242	// This method is implemented separately from the Complex<Array> case. Even
243	// though this method does split the Array<Complex> into a Complex<Array>, we
244	// know that the sizes of the imaginary and real parts match, so we can skip the
245	// extra check.
246	fn from(arr: Array<Complex<T>, D>) -> Self {
247		let arr = arr.view().split_complex();
248		let arr = <Complex<_> as Mappable<_, _>>::map(arr, |component| {
249			let mut component = component.into_owned();
250			rustmex_core::from_ndarray_to_num!(@shuffle component)
251		});
252		let dim = arr.re.raw_dim();
253		let dimview = dim.as_array_view();
254		let shape = dimview.to_slice().unwrap();
255		let arr = arr.map(|x|x.into_raw_vec().into_boxed_slice());
256		Self::new_separated_unchecked(arr, &shape)
257	}
258}
259
260impl<'a, T, D> TryFrom<Complex<Array<T, D>>> for SeparatedComplexArray<T, MxArray> where
261	T: MatlabNumber,
262	D: Dimension,
263{
264	type Error = DataShapeMismatch<Complex<Array<T,D>>>;
265	fn try_from(arr: Complex<Array<T, D>>) -> Result<Self, Self::Error> {
266		// Check whether the shapes of both arrays match; hereby also inferring
267		// that the number of elements is correct (we're assuming that that is
268		// handled correctly by NDarray).
269		if arr.re.shape() != arr.im.shape() {
270			return Err(DataShapeMismatch::because_im_re(arr));
271		}
272
273		let arr = arr.map(|mut arr| {
274			rustmex_core::from_ndarray_to_num!(@shuffle arr)
275		});
276
277		// At this point we know that the shapes match, so it does not matter
278		// which one we pick
279		//
280		// We do this dance with the raw_dim(), because NDarray knows more about
281		// the layout of the dimension than we do. It is probably more efficient
282		// than always copying the .shape() into a Vec.
283		let dim = arr.re.raw_dim();
284		let dimview = dim.as_array_view();
285		let shape = dimview.to_slice().unwrap();
286
287		Ok(Self::new_separated_unchecked(arr.map(|x| {
288			x.into_raw_vec()
289			 .into_boxed_slice()
290		}), &shape))
291	}
292}