1use crate::prelude::*;
2use core::marker::PhantomData;
3pub mod list;
4mod tensor_any;
5mod tensor_ref;
6pub use tensor_any::AnyTensorRef;
7pub use tensor_ref::{TensorRef, from_raw_parts, from_raw_parts_mut};
8
9use mnn_sys::HalideType;
10
11pub type TensorOwned<H, M> = Tensor<Owned<H>, M>;
13pub type TensorView<'t, H, M> = Tensor<View<&'t H>, M>;
15pub type TensorViewMut<'t, H, M> = Tensor<View<&'t mut H>, M>;
17
18mod seal {
19 pub trait Sealed {}
20}
21impl seal::Sealed for Host {}
22impl seal::Sealed for Device {}
23impl<T> seal::Sealed for View<T> {}
24impl<T> seal::Sealed for Owned<T> {}
25
26pub trait TensorType: seal::Sealed {
28 type H;
30 fn owned() -> bool;
32 fn borrowed() -> bool {
34 !Self::owned()
35 }
36}
37
38pub trait MutableTensorType: TensorType + seal::Sealed {}
40
41impl<H> MutableTensorType for Owned<H> {}
42impl<H> MutableTensorType for View<&mut H> {}
43
44impl<H> TensorType for Owned<H> {
45 type H = H;
46 fn owned() -> bool {
47 true
48 }
49}
50
51impl<H> TensorType for View<&H> {
52 type H = H;
53 fn owned() -> bool {
54 false
55 }
56}
57
58impl<H> TensorType for View<&mut H> {
59 type H = H;
60 fn owned() -> bool {
61 false
62 }
63}
64
65pub trait TensorMachine: seal::Sealed {
67 fn device() -> bool;
69 fn host() -> bool {
71 !Self::device()
72 }
73}
74
75impl TensorMachine for Host {
76 fn device() -> bool {
77 false
78 }
79}
80
81impl TensorMachine for Device {
82 fn device() -> bool {
83 true
84 }
85}
86
87#[non_exhaustive]
89#[derive(Debug)]
90pub struct Host {}
91#[non_exhaustive]
93#[derive(Debug)]
94pub struct Device {}
95
96#[repr(transparent)]
98#[derive(Debug)]
99pub struct View<T> {
100 pub(crate) __marker: PhantomData<[T]>,
101}
102#[repr(transparent)]
104#[derive(Debug)]
105pub struct Owned<T> {
106 pub(crate) __marker: PhantomData<T>,
107}
108
109#[derive(Debug)]
117#[repr(transparent)]
118pub struct Tensor<S, M, A = <S as TensorType>::H>
119where
120 A: HalideType,
121 M: TensorMachine,
122 S: TensorType<H = A>,
123{
124 pub(crate) tensor: *mut mnn_sys::Tensor,
125 __marker: PhantomData<(S, M, A)>,
126}
127
128impl<S, M, A> Drop for Tensor<S, M, A>
129where
130 A: HalideType,
131 S: TensorType<H = A>,
132 M: TensorMachine,
133{
134 fn drop(&mut self) {
135 unsafe {
136 mnn_sys::Tensor_destroy(self.tensor);
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
148pub enum DimensionType {
149 #[default]
151 Caffe,
152 CaffeC4,
154 TensorFlow,
156}
157
158impl DimensionType {
159 pub const NHWC: Self = Self::TensorFlow;
161 pub const NCHW: Self = Self::Caffe;
163 pub const NC4HW4: Self = Self::CaffeC4;
165 pub(crate) fn to_mnn_sys(self) -> mnn_sys::DimensionType {
166 match self {
167 DimensionType::Caffe => mnn_sys::DimensionType::CAFFE,
168 DimensionType::CaffeC4 => mnn_sys::DimensionType::CAFFE_C4,
169 DimensionType::TensorFlow => mnn_sys::DimensionType::TENSORFLOW,
170 }
171 }
172}
173
174impl From<mnn_sys::DimensionType> for DimensionType {
175 fn from(dm: mnn_sys::DimensionType) -> Self {
176 match dm {
177 mnn_sys::DimensionType::CAFFE => DimensionType::Caffe,
178 mnn_sys::DimensionType::CAFFE_C4 => DimensionType::CaffeC4,
179 mnn_sys::DimensionType::TENSORFLOW => DimensionType::TensorFlow,
180 }
181 }
182}
183
184impl<H, T, M> Tensor<T, M>
185where
186 T: TensorType<H = H>,
187 H: HalideType,
188 M: TensorMachine,
189{
190 pub unsafe fn from_ptr(tensor: *mut mnn_sys::Tensor) -> Self {
198 assert!(!tensor.is_null());
199 Self {
200 tensor,
201 __marker: PhantomData,
202 }
203 }
204
205 pub fn print(&self) {
207 unsafe {
208 mnn_sys::Tensor_print(self.tensor);
209 }
210 }
211
212 pub unsafe fn halide_buffer(&self) -> *const mnn_sys::halide_buffer_t {
216 unsafe { mnn_sys::Tensor_buffer(self.tensor) }
217 }
218
219 pub unsafe fn halide_buffer_mut(&self) -> *mut mnn_sys::halide_buffer_t {
223 unsafe { mnn_sys::Tensor_buffer_mut(self.tensor) }
224 }
225
226 pub fn get_type(&self) -> mnn_sys::halide_type_t {
228 unsafe { mnn_sys::Tensor_getType(self.tensor) }
229 }
230
231 pub unsafe fn as_any_tensor(&self) -> &AnyTensorRef {
235 unsafe { AnyTensorRef::from_ptr(self.tensor) }
236 }
237
238 pub unsafe fn as_any_tensor_mut(&mut self) -> &mut AnyTensorRef {
242 unsafe { AnyTensorRef::from_mut_ptr(self.tensor) }
243 }
244}
245
246impl<H> Tensor<Owned<H>, Host>
247where
248 H: HalideType + Copy,
249{
250 pub fn from_shape_data(shape: impl AsTensorShape, dm_type: DimensionType, data: &[H]) -> Self {
256 let shape = shape.as_tensor_shape();
257 let size = shape
258 .tensor_size()
259 .unwrap_or_else(|| panic!("Tensor shape {shape:?} is dynamic or too large"));
260 assert_eq!(
261 size,
262 data.len(),
263 "Data length ({}) does not match tensor shape ({:?})",
264 data.len(),
265 shape
266 );
267 let mut tensor = Self::new(shape, dm_type);
268 let host = tensor.host_mut();
269 assert_eq!(
270 host.len(),
271 data.len(),
272 "Tensor layout {dm_type:?} needs {} elements for shape {shape:?} (channel padding) but {} were provided",
273 host.len(),
274 data.len()
275 );
276 host.copy_from_slice(data);
277 tensor
278 }
279}
280
281impl<H, M> Tensor<Owned<H>, M>
282where
283 H: HalideType,
284 M: TensorMachine,
285{
286 pub fn new(shape: impl AsTensorShape, dm_type: DimensionType) -> Self {
293 let shape = shape.as_tensor_shape();
294 if M::host() {
295 assert!(
296 shape.tensor_size().is_some(),
297 "Cannot allocate a host tensor with a dynamic or oversized shape {shape:?}"
298 );
299 }
300 let tensor = unsafe {
301 if M::device() {
302 mnn_sys::Tensor_createDevice(
303 shape.shape.as_ptr(),
304 shape.size,
305 mnn_sys::halide_type_of::<H>(),
306 dm_type.to_mnn_sys(),
307 )
308 } else {
309 mnn_sys::Tensor_createWith(
310 shape.shape.as_ptr(),
311 shape.size,
312 mnn_sys::halide_type_of::<H>(),
313 core::ptr::null_mut(),
314 dm_type.to_mnn_sys(),
315 )
316 }
317 };
318 debug_assert!(!tensor.is_null());
319 Self {
320 tensor,
321 __marker: PhantomData,
322 }
323 }
324}
325
326impl<H> Clone for Tensor<Owned<H>, Host>
327where
328 H: HalideType + Copy,
329{
330 fn clone(&self) -> Tensor<Owned<H>, Host> {
331 let data = self.host();
332 let shape = self.shape();
333 let mut out = Tensor::new(shape, self.get_dimension_type());
334 out.host_mut().copy_from_slice(data);
335 out
338 }
339}
340
341impl<T, H> PartialEq for Tensor<T, Host>
342where
343 T: TensorType<H = H>,
344 H: HalideType + PartialEq,
345{
346 fn eq(&self, other: &Self) -> bool {
347 if self.size() != other.size() {
348 return false;
349 }
350 let self_data = self.host();
351 let other_data = other.host();
352 self_data == other_data
353 }
354}
355
356impl<T, H> Eq for Tensor<T, Host>
357where
358 T: TensorType<H = H>,
359 H: HalideType + Eq,
360{
361}
362
363#[derive(Clone, Copy)]
365#[repr(C)]
366pub struct TensorShape {
367 pub(crate) shape: [i32; 4],
368 pub(crate) size: usize,
369}
370
371impl TensorShape {
372 pub fn tensor_size(&self) -> Option<usize> {
377 self.shape[..self.size].iter().try_fold(1usize, |acc, &x| {
378 let x = usize::try_from(x).ok()?;
379 acc.checked_mul(x)
380 })
381 }
382}
383
384impl From<mnn_sys::TensorShape> for TensorShape {
385 fn from(value: mnn_sys::TensorShape) -> Self {
386 Self {
387 shape: value.shape,
388 size: value.size,
389 }
390 }
391}
392
393impl From<TensorShape> for mnn_sys::TensorShape {
394 fn from(value: TensorShape) -> Self {
395 Self {
396 shape: value.shape,
397 size: value.size,
398 }
399 }
400}
401
402impl core::ops::Deref for TensorShape {
403 type Target = [i32];
404
405 fn deref(&self) -> &Self::Target {
406 &self.shape[..self.size]
407 }
408}
409
410impl core::ops::Index<usize> for TensorShape {
411 type Output = i32;
412
413 fn index(&self, index: usize) -> &Self::Output {
414 &self.shape[..self.size][index]
415 }
416}
417
418impl core::ops::IndexMut<usize> for TensorShape {
419 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
420 &mut self.shape[..self.size][index]
421 }
422}
423
424impl core::ops::DerefMut for TensorShape {
425 fn deref_mut(&mut self) -> &mut Self::Target {
426 &mut self.shape[..self.size]
427 }
428}
429
430impl core::fmt::Debug for TensorShape {
431 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
432 f.debug_tuple("TensorShape")
433 .field(&&self.shape[..self.size])
434 .finish()
435 }
436}
437
438pub trait AsTensorShape {
440 fn as_tensor_shape(&self) -> TensorShape;
442}
443
444impl<T: AsRef<[i32]>> AsTensorShape for T {
445 fn as_tensor_shape(&self) -> TensorShape {
446 let this = self.as_ref();
447 let size = std::cmp::min(this.len(), 4);
448 let mut shape = [1; 4];
449 shape[..size].copy_from_slice(&this[..size]);
450 TensorShape { shape, size }
451 }
452}
453
454impl AsTensorShape for TensorShape {
455 fn as_tensor_shape(&self) -> TensorShape {
456 *self
457 }
458}
459
460#[cfg(test)]
461mod as_tensor_shape_tests {
462 use super::AsTensorShape;
463 macro_rules! shape_test {
464 ($t:ty, $kind: expr, $value: expr) => {
465 eprintln!("Testing {} with {} shape", stringify!($t), $kind);
466 $value.as_tensor_shape();
467 };
468 }
469 #[test]
470 fn as_tensor_shape_test_vec() {
471 shape_test!(Vec<i32>, "small", vec![1, 2, 3]);
472 shape_test!(Vec<i32>, "large", vec![12, 23, 34, 45, 67]);
473 }
474 #[test]
475 fn as_tensor_shape_test_array() {
476 shape_test!([i32; 3], "small", [1, 2, 3]);
477 shape_test!([i32; 5], "large", [12, 23, 34, 45, 67]);
478 }
479 #[test]
480 fn as_tensor_shape_test_ref() {
481 shape_test!(&[i32], "small", &[1, 2, 3]);
482 shape_test!(&[i32], "large", &[12, 23, 34, 45, 67]);
483 }
484
485 #[test]
486 fn tensor_size_handles_dynamic_and_overflow() {
487 use super::TensorShape;
488 assert_eq!([1, 2, 3].as_tensor_shape().tensor_size(), Some(6));
489 assert_eq!([1, -1, 3].as_tensor_shape().tensor_size(), None);
490 assert_eq!([i32::MAX; 4].as_tensor_shape().tensor_size(), None);
491 let dynamic = TensorShape {
493 shape: [1, 3, -1, 0],
494 size: 3,
495 };
496 assert_eq!(format!("{dynamic:?}"), "TensorShape([1, 3, -1])");
497 }
498}
499
500#[cfg(test)]
501mod tensor_tests {
502 #[test]
503 #[should_panic]
504 fn unsafe_nullptr_tensor() {
505 use super::*;
506 unsafe {
507 super::Tensor::<Owned<i32>, Host>::from_ptr(core::ptr::null_mut());
508 }
509 }
510
511 #[test]
516 fn try_host_not_available_on_device_tensor() {
517 use super::*;
518 let device_tensor = Tensor::<Owned<f32>, Device>::new([1, 2, 3], DimensionType::Caffe);
519 drop(device_tensor);
523 }
524}
525
526impl<'a, A> Tensor<View<&'a A>, Host, A>
527where
528 A: HalideType,
529{
530 pub fn borrowed(shape: impl AsTensorShape, dm_type: DimensionType, input: &'a [A]) -> Self {
538 let shape = shape.as_tensor_shape();
539 let size = shape
540 .tensor_size()
541 .unwrap_or_else(|| panic!("Tensor shape {shape:?} is dynamic or too large"));
542 assert_eq!(
543 size,
544 input.len(),
545 "Input data length ({}) does not match the tensor shape ({:?})",
546 input.len(),
547 shape
548 );
549 let tensor = unsafe {
550 mnn_sys::Tensor_createWith(
551 shape.shape.as_ptr(),
552 shape.size,
553 mnn_sys::halide_type_of::<A>(),
554 input.as_ptr().cast_mut().cast(),
555 dm_type.to_mnn_sys(),
556 )
557 };
558 assert!(!tensor.is_null());
559 let output = Self {
562 tensor,
563 __marker: PhantomData,
564 };
565 assert_eq!(
567 output.element_size(),
568 input.len(),
569 "Tensor layout {dm_type:?} needs {} elements for shape {shape:?} (channel padding) but the slice has {}",
570 output.element_size(),
571 input.len()
572 );
573 output
574 }
575}
576
577impl<'a, A> Tensor<View<&'a mut A>, Host, A>
578where
579 A: HalideType,
580{
581 pub fn borrowed_mut(
589 shape: impl AsTensorShape,
590 dm_type: DimensionType,
591 input: &'a mut [A],
592 ) -> Self {
593 let shape = shape.as_tensor_shape();
594 let size = shape
595 .tensor_size()
596 .unwrap_or_else(|| panic!("Tensor shape {shape:?} is dynamic or too large"));
597 assert_eq!(
598 size,
599 input.len(),
600 "Input data length ({}) does not match the tensor shape ({:?})",
601 input.len(),
602 shape
603 );
604 let len = input.len();
605 let tensor = unsafe {
606 mnn_sys::Tensor_createWith(
607 shape.shape.as_ptr(),
608 shape.size,
609 mnn_sys::halide_type_of::<A>(),
610 input.as_mut_ptr().cast(),
611 dm_type.to_mnn_sys(),
612 )
613 };
614 assert!(!tensor.is_null());
615 let output = Self {
618 tensor,
619 __marker: PhantomData,
620 };
621 assert_eq!(
623 output.element_size(),
624 len,
625 "Tensor layout {dm_type:?} needs {} elements for shape {shape:?} (channel padding) but the slice has {}",
626 output.element_size(),
627 len
628 );
629 output
630 }
631}
632
633#[test]
634fn test_tensor_borrowed() {
635 let shape = [1, 2, 3];
636 let data = vec![1, 2, 3, 4, 5, 6];
637 let tensor = Tensor::<View<&i32>, Host>::borrowed(shape, DimensionType::Caffe, &data);
638 assert_eq!(tensor.shape().as_ref(), shape);
639 assert_eq!(tensor.host(), data.as_slice());
640}
641#[test]
642fn test_tensor_borrow_mut() {
643 let shape = [1, 2, 3];
644 let mut data = vec![1, 2, 3, 4, 5, 6];
645 let mut tensor =
646 Tensor::<View<&mut i32>, Host>::borrowed_mut(shape, DimensionType::Caffe, &mut data);
647 tensor.host_mut().fill(1);
648 drop(tensor);
649 assert_eq!(data, &[1, 1, 1, 1, 1, 1]);
650}
651
652#[test]
653#[should_panic]
654fn test_tensor_invalid_size() {
655 let shape = [300, 400, 500];
656 let date = vec![0; 100];
657 Tensor::<View<&i32>, Host>::borrowed(shape, DimensionType::default(), &date);
658}
659
660#[test]
661#[should_panic(expected = "channel padding")]
662fn test_tensor_borrowed_rejects_unpadded_c4() {
663 let data = vec![1f32; 12];
666 Tensor::<View<&f32>, Host>::borrowed([1, 3, 2, 2], DimensionType::CaffeC4, &data);
667}
668
669#[test]
670#[should_panic(expected = "channel padding")]
671fn test_tensor_borrowed_mut_rejects_unpadded_c4() {
672 let mut data = vec![1f32; 12];
673 Tensor::<View<&mut f32>, Host>::borrowed_mut([1, 3, 2, 2], DimensionType::CaffeC4, &mut data);
674}
675
676#[test]
677fn test_tensor_borrowed_padded_c4() {
678 let data = vec![1f32; 16];
680 let tensor = Tensor::<View<&f32>, Host>::borrowed([1, 4, 2, 2], DimensionType::CaffeC4, &data);
681 assert_eq!(tensor.host().len(), 16);
682}
683
684#[test]
685#[should_panic(expected = "channel padding")]
686fn test_from_shape_data_rejects_unpadded_c4() {
687 TensorOwned::<f32, Host>::from_shape_data([1, 3, 2, 2], DimensionType::CaffeC4, &[0f32; 12]);
688}
689
690#[test]
691#[should_panic(expected = "dynamic")]
692fn test_tensor_borrowed_rejects_dynamic_shape() {
693 let data = vec![0i32; 6];
694 Tensor::<View<&i32>, Host>::borrowed([1, -1, 3], DimensionType::Caffe, &data);
695}
696
697#[test]
698fn test_tensor_ref_borrows() {
699 let data = vec![1, 2, 3, 4, 5, 6];
700 let mut owned =
701 TensorOwned::<i32, Host>::from_shape_data([1, 2, 3], DimensionType::Caffe, &data);
702 {
703 let shared: &TensorRef<i32, Host> = &owned;
704 let again: &TensorRef<i32, Host> = shared;
705 assert_eq!(shared.host(), data.as_slice());
706 assert_eq!(again.host(), data.as_slice());
707 }
708 {
709 let exclusive: &mut TensorRef<i32, Host> = &mut owned;
710 exclusive.host_mut().fill(7);
711 let reborrowed: &mut TensorRef<i32, Host> = &mut *exclusive;
712 reborrowed.host_mut()[0] = 9;
713 }
714 assert_eq!(owned.host(), &[9, 7, 7, 7, 7, 7]);
716}
717
718#[test]
719fn test_borrowed_view_drop_keeps_user_data() {
720 let data = vec![1, 2, 3, 4, 5, 6];
721 for _ in 0..3 {
722 let view = Tensor::<View<&i32>, Host>::borrowed([1, 2, 3], DimensionType::Caffe, &data);
723 assert_eq!(view.host(), data.as_slice());
724 }
726 assert_eq!(data, &[1, 2, 3, 4, 5, 6]);
727}
728
729#[test]
730fn test_tensor_clone() {
731 let shape = [1, 2, 3];
732 let data = vec![1, 3, 5, 8, 1, 23];
733 let mut tensor = Tensor::new(shape, DimensionType::Caffe);
734 tensor.host_mut().copy_from_slice(&data);
735
736 let tensor_cloned = tensor.clone();
737 assert_eq!(tensor_cloned.host(), data);
738}
739
740#[test]
741fn test_tensor_creation() {
742 let shape = [4, 2, 8];
743 let data: &[u8; 64] = b"4756ff762c7a7909a1ec0015836296807602fb2d44e1cac15e057e22b2258040";
744
745 let tensor = {
746 let data = data.iter().copied().map(|v| v as f32).collect::<Vec<_>>();
747 TensorOwned::<f32, Host>::from_shape_data(shape, DimensionType::Caffe, &data)
748 };
749 let data = data.iter().copied().map(|v| v as f32).collect::<Vec<_>>();
750 assert_eq!(tensor.host(), data);
751}