rustmex_core/
lib.rs

1pub mod numeric;
2pub mod pointers;
3pub mod raw;
4pub mod classid;
5
6#[doc(hidden)]
7pub mod mappable;
8
9pub mod shim;
10pub mod convert;
11
12pub use raw::{mxArray, mxComplexity};
13
14// I wanted to use cfg(doc) for this, but it didn't really work.
15#[allow(unused)]
16use crate::{
17	pointers::{
18		MatlabPtr,
19		MutMatlabPtr,
20	},
21};
22
23/**
24 * Macro to construct a valid pointer for slice construction. Internal Rustmex tool.
25 *
26 * Matlab returns, for empty arrays, a null pointer. For this case, we want to construct
27 * an empty slice, but cannot pass in the null pointer for that. Instead, per
28 * [std::slice::from_raw_parts]' documentation, we can obtain a valid pointer through NonNull.
29 *
30 * ```rust
31 * # use rustmex_core::data_or_dangling;
32 * # use core::ffi::c_void;
33 * fn get_some_maybe_null_pointer() -> *const c_void { 0 as *const c_void }
34 * const DATA: *const i8 = 0xbeef as *const i8;
35 * let nonnull_i8 = data_or_dangling!{ get_some_maybe_null_pointer(), *const i8 };
36 * let data = data_or_dangling!{ DATA, *const i8 };
37 * assert_ne!(nonnull_i8, core::ptr::null());
38 * assert_eq!(data, DATA);
39 * ```
40 */
41#[doc(hidden)]
42#[macro_export]
43macro_rules! data_or_dangling {
44	($p:expr, $t:ty) => {{
45		let ptr = { $p };
46		(if ptr.is_null() {
47			::core::ptr::NonNull::dangling().as_ptr()
48		} else {
49			ptr
50		}) as $t
51	}}
52}
53
54/**
55 * Given some data and a shape, check whether the size is okay. Internal Rustmex tool.
56 *
57 * _Cf._ [`DataShapeMismatch`](convert::DataShapeMismatch).
58 */
59#[doc(hidden)]
60#[macro_export]
61macro_rules! data_shape_ok {
62	($data:ident, $shape:ident) => {
63		::rustmex_core::shape_ok!($shape);
64
65		// In this case, the provided shape does not match the total data size.
66		if $shape.iter().product::<usize>() != $data.len() {
67			return Err(::rustmex_core::convert
68				::DataShapeMismatch::because_numel_shape($data))
69		}
70	}
71}
72
73/**
74 * Flag for whether a real or complex numeric array should be created.
75 *
76 * Enumerates over the allowed values of the [`mxComplexity`](raw::mxComplexity) type.
77 * Besides that, it has the same size and ABI as that type, so can be passed as is.
78 */
79#[repr(u32)]
80#[derive(Clone, Copy, Hash, PartialEq, Eq)]
81pub enum Complexity {
82	Real = raw::mxComplexity_mxREAL,
83	Complex = raw::mxComplexity_mxCOMPLEX
84}
85
86impl mxArray {
87	pub fn complexity(&self) -> Complexity {
88		self.is_complex().into()
89	}
90}
91
92impl From<Complexity> for bool {
93	fn from(c: Complexity) -> Self {
94		match c {
95			Complexity::Real => false,
96			Complexity::Complex => true,
97		}
98	}
99}
100
101impl From<bool> for Complexity {
102	fn from(b: bool) -> Self {
103		if b { Complexity::Complex } else { Complexity::Real }
104	}
105}
106
107impl From<Complexity> for mxComplexity {
108	fn from(c: Complexity) -> Self {
109		bool::from(c) as Self
110	}
111}
112
113impl PartialEq<bool> for Complexity {
114	fn eq(&self, other: &bool) -> bool {
115		bool::from(*self) == *other
116	}
117}
118
119impl PartialEq<Complexity> for bool {
120	fn eq(&self, other: &Complexity) -> bool {
121		other.eq(self)
122	}
123}
124
125/**
126 * Create an [`mxArray`] of a specified shape, type, and complexity, **without** a
127 * backing array. Internal Rustmex tool.
128 *
129 * Creating an empty [`mxArray`] avoids the allocation matlab would otherwise make. The
130 * expectation is that that data will be provided immediately after the creation of the
131 * [`mxArray`]. Until the data is provided, the data get methods will return `NULL`,
132 * which is an invalid state for Rustmex mxArrays to be in, which expect that they will
133 * always have a backing array and thus a valid reference to return to that.
134 */
135#[doc(hidden)]
136#[macro_export]
137macro_rules! create_uninit_numeric_array {
138	($shape:ident, $t:ty, $complexity:expr) => {{
139		use rustmex_core::{
140			Complexity,
141			raw::{mxClassID, mwSize},
142			shim::{
143				rustmex_create_uninit_numeric_array,
144				rustmex_set_dimensions,
145			}
146		};
147
148		const EMPTY_ARRAY_SIZE: [mwSize; 2] = [0, 0];
149		const RESIZE_FAILURE: i32 = 1;
150
151		let ptr = unsafe {
152			rustmex_create_uninit_numeric_array(
153				EMPTY_ARRAY_SIZE.len() as mwSize,
154				EMPTY_ARRAY_SIZE.as_ptr(),
155				<$t>::class_id() as mxClassID,
156				$complexity as u32)
157		};
158
159		if ptr.is_null() {
160			panic!("OOM")
161		}
162
163		if unsafe { rustmex_set_dimensions(
164			ptr,
165			$shape.as_ptr(),
166			$shape.len()
167		) } == RESIZE_FAILURE {
168			panic!("could not resize array!")
169		}
170
171		ptr
172	}}
173}
174
175/**
176 * Check whether a shape is okay. Internal Rustmex tool
177 *
178 * Since some backends use a signed type for the dimension lenghts, values greater that
179 * [`isize::MAX`] are not supported. Currently implemented as a [`debug_assert`], but
180 * that may change in the future.
181 */
182#[doc(hidden)]
183#[macro_export]
184macro_rules! shape_ok {
185	($shape:ident) => {
186		debug_assert!($shape.iter().all(|&v| v <= isize::MAX as usize),
187			"Octave uses a signed type for the length of dimensions. One or more values was over this signed type's maximum value."
188		)
189	}
190}
191
192use core::ops::{Deref, DerefMut};
193use crate::pointers::MxArray;
194use crate::convert::FromMatlabError;
195
196/**
197 * Base Matlab class trait. Matlab classes, in Rustmex, are wrapper structures around a
198 * bare [`MatlabPtr`], giving them some type information.
199 */
200pub trait MatlabClass<P>: Deref<Target = mxArray> + Sized {
201	/**
202	 * Try to build a matlab class from a [`MatlabPtr`]. If the type requirements of
203	 * the class match what the pointer is, then the class is constructed. Otherwise,
204	 * an error is returned; containing the reason of the failure and the original
205	 * pointer. The latter is especially useful with [`MxArray`]s; the owned
206	 * [`MatlabPtr`] type --- otherwise it would be dropped.
207	 */
208	fn from_mx_array(mx: P) -> Result<Self, FromMatlabError<P>>;
209
210	/// Deconstruct the `MatlabClass` back to a bare [`MatlabPtr`].
211	fn into_inner(self) -> P;
212	/**
213	 * Get a reference to the inner [`MatlabPtr`]. However, note that for `&mxArray`
214	 * and `&mut mxArray`, the returned references will be of type `&&mxArray` and
215	 * `&&mut mxArray`.
216	 */
217	fn inner(&self) -> &P;
218
219	/// The owned variant of this matlab class.
220	type Owned;
221	/**
222	 * Make a deep clone of the inner [`MatlabPtr`], and construct an
223	 * [`Owned`](Self::Owned) [`MatlabClass`] from it.
224	 */
225	fn duplicate(&self) -> Self::Owned;
226}
227
228/// Denotes whether the Matlab class is mutable.
229pub trait MutMatlabClass<P>: MatlabClass<P> + DerefMut {
230	type AsBorrowed<'a> where Self: 'a, P: 'a;
231	fn as_borrowed<'a>(&'a self) -> Self::AsBorrowed<'a>;
232	fn inner_mut(&mut self) -> &mut P;
233}
234
235/// Denotes whether a Matlab class owns its data.
236pub trait OwnedMatlabClass: MutMatlabClass<MxArray> {
237	/// The way this class would be represented if it were a mutable borrow.
238	type AsMutable<'a> where Self: 'a; // e.g. Numeric<&'s mut mxArray>
239	/// Create a [`MutMatlabClass`] from this owned instance.
240	fn as_mutable<'a>(&'a mut self) -> Self::AsMutable<'a>;
241
242}
243
244/**
245 * Only some Matlab classes, _i.e_ the numeric classes, have data that can be
246 * meaningfully taken out of the backing [`MutMatlabPtr`].
247 */
248pub trait TakeData<P>: MutMatlabClass<P> {
249	/// The type of data that this array wraps.
250	type OwnedData;
251
252	/// Take the data out of the array, leaving it in an empty state.
253	fn take_data(&mut self) -> Self::OwnedData;
254}
255
256/**
257 * Some, but not all types, can be created in an empty state; whether they can is denoted
258 * by this trait.
259 */
260pub trait NewEmpty: OwnedMatlabClass {
261	/// Construct the empty type
262	fn new_empty() -> Self;
263}