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}