1use std::os::raw::c_void;
2
3use once_cell::sync::Lazy;
4
5use dlpk::sys::{DLDevice, DLManagedTensorVersioned, DLPackVersion, DLDataType};
6use dlpk::DLPackTensor;
7
8use crate::errors::Error;
9use crate::c_api::{mts_array_t, mts_data_origin_t, mts_data_movement_t, mts_status_t};
10
11use super::MtsArray;
12
13pub trait Array: std::any::Any + Send + Sync {
19 fn as_any(&self) -> &dyn std::any::Any;
21
22 fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
24
25 fn create(&self, shape: &[usize], fill_value: MtsArray) -> Box<dyn Array>;
32
33 fn copy(&self, device: DLDevice) -> Box<dyn Array>;
38
39 fn shape(&self) -> Vec<usize>;
42
43 fn reshape(&mut self, shape: &[usize]);
45
46 fn swap_axes(&mut self, axis_1: usize, axis_2: usize);
48
49 fn move_data(
64 &mut self,
65 input: &dyn Array,
66 movements: &[mts_data_movement_t],
67 );
68
69 fn device(&self) -> DLDevice;
73
74 fn dtype(&self) -> DLDataType;
80
81 fn as_dlpack(
84 &self,
85 device: DLDevice,
86 stream: Option<i64>,
87 max_version: DLPackVersion
88 ) -> Result<DLPackTensor, Error>;
89
90 #[allow(clippy::wrong_self_convention)]
93 fn from_dlpack(&self, dl_tensor: DLPackTensor) -> Result<Box<dyn Array>, Error>;
94}
95
96pub (super) struct RustArray {
97 impl_: Box<dyn Array>,
98 shape: Vec<usize>,
99}
100
101impl std::ops::Deref for RustArray {
102 type Target = dyn Array;
103
104 fn deref(&self) -> &Self::Target {
105 &*self.impl_
106 }
107}
108
109impl std::ops::DerefMut for RustArray {
110 fn deref_mut(&mut self) -> &mut Self::Target {
111 &mut *self.impl_
112 }
113}
114
115impl From<Box<dyn Array>> for MtsArray {
116 fn from(value: Box<dyn Array>) -> Self {
117 let shape = value.shape();
118 let array = RustArray {
119 impl_: value,
120 shape,
121 };
122
123 let raw = mts_array_t {
124 ptr: Box::into_raw(Box::new(array)).cast(),
125 origin: Some(rust_array_origin),
126 device: Some(rust_array_device),
127 dtype: Some(rust_array_dtype),
128 as_dlpack: Some(rust_array_as_dlpack),
129 from_dlpack: Some(rust_array_from_dlpack),
130 shape: Some(rust_array_shape),
131 reshape: Some(rust_array_reshape),
132 swap_axes: Some(rust_array_swap_axes),
133 create: Some(rust_array_create),
134 copy: Some(rust_array_copy),
135 destroy: Some(rust_array_destroy),
136 move_data: Some(rust_array_move_data),
137 };
138
139 return MtsArray::from_raw(raw);
140 }
141}
142
143impl<T> From<T> for MtsArray where T: Array + 'static {
144 fn from(value: T) -> Self {
145 let boxed = Box::new(value) as Box<dyn Array>;
146 return MtsArray::from(boxed);
147 }
148}
149
150macro_rules! check_pointers {
151 ($pointer: ident) => {
152 if $pointer.is_null() {
153 panic!(
154 "got invalid NULL pointer for {} at {}:{}",
155 stringify!($pointer), file!(), line!()
156 );
157 }
158 };
159 ($($pointer: ident),* $(,)?) => {
160 $(check_pointers!($pointer);)*
161 }
162}
163
164pub(super) static RUST_DATA_ORIGIN: Lazy<mts_data_origin_t> = Lazy::new(|| {
165 super::origin::register_data_origin("RustArray".into()).expect("failed to register a new origin")
166});
167
168unsafe extern "C" fn rust_array_origin(
171 array: *const c_void,
172 origin: *mut mts_data_origin_t
173) -> mts_status_t {
174 crate::errors::catch_unwind(|| {
175 check_pointers!(array, origin);
176 unsafe {
177 *origin = *RUST_DATA_ORIGIN;
178 }
179
180 Ok(())
181 })
182}
183
184unsafe extern "C" fn rust_array_device(
186 array: *const c_void,
187 device: *mut DLDevice,
188) -> mts_status_t {
189 crate::errors::catch_unwind(|| {
190 check_pointers!(array, device);
191 let array = array.cast::<RustArray>();
192 unsafe {
193 *device = (*array).impl_.device();
194 }
195
196 Ok(())
197 })
198}
199
200unsafe extern "C" fn rust_array_dtype(
202 array: *const c_void,
203 dtype: *mut DLDataType,
204) -> mts_status_t {
205 crate::errors::catch_unwind(|| {
206 check_pointers!(array, dtype);
207 let array = array.cast::<RustArray>();
208 unsafe {
209 *dtype = (*array).impl_.dtype();
210 }
211
212 Ok(())
213 })
214}
215
216unsafe extern "C" fn rust_array_shape(
218 array: *const c_void,
219 shape: *mut *const usize,
220 shape_count: *mut usize,
221) -> mts_status_t {
222 crate::errors::catch_unwind(|| {
223 check_pointers!(array, shape, shape_count);
224 let array = array.cast::<RustArray>();
225 unsafe {
226 let rust_shape = &(*array).shape;
227
228 *shape = rust_shape.as_ptr();
229 *shape_count = rust_shape.len();
230 }
231
232 Ok(())
233 })
234}
235
236#[allow(clippy::cast_possible_truncation)]
238unsafe extern "C" fn rust_array_reshape(
239 array: *mut c_void,
240 shape: *const usize,
241 shape_count: usize,
242) -> mts_status_t {
243 crate::errors::catch_unwind(|| {
244 check_pointers!(array);
245 let array = array.cast::<RustArray>();
246
247 let shape = if shape_count == 0 {
248 &[]
249 } else {
250 check_pointers!(shape);
251 unsafe { std::slice::from_raw_parts(shape, shape_count) }
252 };
253
254 unsafe {
255 (*array).impl_.reshape(shape);
256 (*array).shape = shape.to_vec();
257 }
258
259 Ok(())
260 })
261}
262
263#[allow(clippy::cast_possible_truncation)]
265unsafe extern "C" fn rust_array_swap_axes(
266 array: *mut c_void,
267 axis_1: usize,
268 axis_2: usize,
269) -> mts_status_t {
270 crate::errors::catch_unwind(|| {
271 check_pointers!(array);
272 let array = array.cast::<RustArray>();
273 unsafe {
274 (*array).impl_.swap_axes(axis_1, axis_2);
275 (*array).shape.swap(axis_1, axis_2);
276 }
277
278 Ok(())
279 })
280}
281
282#[allow(clippy::cast_possible_truncation)]
284unsafe extern "C" fn rust_array_create(
285 array: *const c_void,
286 shape: *const usize,
287 shape_count: usize,
288 fill_value: mts_array_t,
289 array_storage: *mut mts_array_t,
290) -> mts_status_t {
291 crate::errors::catch_unwind(|| {
292 check_pointers!(array, array_storage);
293 let array = array.cast::<RustArray>();
294
295 let shape = if shape_count == 0 {
296 &[]
297 } else {
298 check_pointers!(shape);
299 unsafe { std::slice::from_raw_parts(shape, shape_count) }
300 };
301
302 unsafe {
303 let new_array = (*array).impl_.create(shape, MtsArray::from_raw(fill_value));
304 let new_array = MtsArray::from(new_array);
305
306 *array_storage = new_array.into_raw();
307 }
308
309 Ok(())
310 })
311}
312
313unsafe extern "C" fn rust_array_copy(
315 array: *const c_void,
316 device: DLDevice,
317 new_array: *mut mts_array_t
318) -> mts_status_t {
319 crate::errors::catch_unwind(|| {
320 check_pointers!(array, new_array);
321 let array = array.cast::<RustArray>();
322
323 unsafe {
324 let copy = (*array).impl_.copy(device);
325 let copy = MtsArray::from(copy);
326 *new_array = copy.into_raw();
327 }
328
329 Ok(())
330 })
331}
332
333unsafe extern "C" fn rust_array_destroy(
335 array: *mut c_void,
336) {
337 if !array.is_null() {
338 let array = array.cast::<RustArray>();
339 unsafe {
340 std::mem::drop(Box::from_raw(array));
341 }
342 }
343}
344
345#[allow(clippy::cast_possible_truncation)]
347unsafe extern "C" fn rust_array_move_data(
348 output: *mut c_void,
349 input: *const c_void,
350 movements: *const mts_data_movement_t,
351 movements_count: usize,
352) -> mts_status_t {
353 crate::errors::catch_unwind(|| {
354 check_pointers!(output, input);
355 let output = output.cast::<RustArray>();
356 let input = input.cast::<RustArray>();
357
358 let movements = if movements_count == 0 {
359 &[]
360 } else {
361 check_pointers!(movements);
362 unsafe { std::slice::from_raw_parts(movements, movements_count) }
363 };
364
365 unsafe {
366 (*output).impl_.move_data(&*(*input).impl_, movements);
367 }
368
369 Ok(())
370 })
371}
372
373unsafe extern "C" fn rust_array_as_dlpack(
375 array: *mut c_void,
376 dl_tensor: *mut *mut DLManagedTensorVersioned,
377 device: DLDevice,
378 stream: *const i64,
379 max_version: DLPackVersion,
380) -> mts_status_t {
381 crate::errors::catch_unwind(|| {
382 check_pointers!(array, dl_tensor);
383 let array = array.cast::<RustArray>();
384 unsafe {
385 let stream_opt = stream.as_ref().copied();
386 let tensor = (*array).impl_.as_dlpack(device, stream_opt, max_version)?;
387
388 *dl_tensor = tensor.into_raw().as_ptr();
389 }
390 Ok(())
391 })
392}
393
394unsafe extern "C" fn rust_array_from_dlpack(
396 array: *const c_void,
397 dl_tensor: *mut DLManagedTensorVersioned,
398 new_array: *mut mts_array_t,
399) -> mts_status_t {
400 crate::errors::catch_unwind(|| {
401 check_pointers!(array, dl_tensor, new_array);
402 let array = array.cast::<RustArray>();
403 unsafe {
404 let dl_tensor = DLPackTensor::from_ptr(dl_tensor);
405 let new_rust_array = (*array).impl_.from_dlpack(dl_tensor)?;
406 *new_array = MtsArray::from(new_rust_array).into_raw();
407 }
408
409 Ok(())
410 })
411}