1use crate::{DType, PixelFormat, Tensor, TensorMemory, TensorTrait};
5use half::f16;
6use std::fmt;
7
8#[non_exhaustive]
10pub enum TensorDyn {
11 U8(Tensor<u8>),
13 I8(Tensor<i8>),
15 U16(Tensor<u16>),
17 I16(Tensor<i16>),
19 U32(Tensor<u32>),
21 I32(Tensor<i32>),
23 U64(Tensor<u64>),
25 I64(Tensor<i64>),
27 F16(Tensor<f16>),
29 F32(Tensor<f32>),
31 F64(Tensor<f64>),
33}
34
35macro_rules! dispatch {
37 ($self:expr, $method:ident $(, $arg:expr)*) => {
38 match $self {
39 TensorDyn::U8(t) => t.$method($($arg),*),
40 TensorDyn::I8(t) => t.$method($($arg),*),
41 TensorDyn::U16(t) => t.$method($($arg),*),
42 TensorDyn::I16(t) => t.$method($($arg),*),
43 TensorDyn::U32(t) => t.$method($($arg),*),
44 TensorDyn::I32(t) => t.$method($($arg),*),
45 TensorDyn::U64(t) => t.$method($($arg),*),
46 TensorDyn::I64(t) => t.$method($($arg),*),
47 TensorDyn::F16(t) => t.$method($($arg),*),
48 TensorDyn::F32(t) => t.$method($($arg),*),
49 TensorDyn::F64(t) => t.$method($($arg),*),
50 }
51 };
52}
53
54macro_rules! dyn_fanout {
58 ($self:expr, $method:ident $(, $arg:expr)*) => {
59 match $self {
60 TensorDyn::U8(t) => t.$method($($arg),*).map(TensorDyn::U8),
61 TensorDyn::I8(t) => t.$method($($arg),*).map(TensorDyn::I8),
62 TensorDyn::U16(t) => t.$method($($arg),*).map(TensorDyn::U16),
63 TensorDyn::I16(t) => t.$method($($arg),*).map(TensorDyn::I16),
64 TensorDyn::U32(t) => t.$method($($arg),*).map(TensorDyn::U32),
65 TensorDyn::I32(t) => t.$method($($arg),*).map(TensorDyn::I32),
66 TensorDyn::U64(t) => t.$method($($arg),*).map(TensorDyn::U64),
67 TensorDyn::I64(t) => t.$method($($arg),*).map(TensorDyn::I64),
68 TensorDyn::F16(t) => t.$method($($arg),*).map(TensorDyn::F16),
69 TensorDyn::F32(t) => t.$method($($arg),*).map(TensorDyn::F32),
70 TensorDyn::F64(t) => t.$method($($arg),*).map(TensorDyn::F64),
71 }
72 };
73}
74
75macro_rules! downcast_methods {
77 ($variant:ident, $ty:ty, $as_name:ident, $as_mut_name:ident, $into_name:ident) => {
78 pub fn $as_name(&self) -> Option<&Tensor<$ty>> {
80 match self {
81 Self::$variant(t) => Some(t),
82 _ => None,
83 }
84 }
85
86 pub fn $as_mut_name(&mut self) -> Option<&mut Tensor<$ty>> {
88 match self {
89 Self::$variant(t) => Some(t),
90 _ => None,
91 }
92 }
93
94 #[allow(clippy::result_large_err)]
97 pub fn $into_name(self) -> Result<Tensor<$ty>, Self> {
98 match self {
99 Self::$variant(t) => Ok(t),
100 other => Err(other),
101 }
102 }
103 };
104}
105
106impl TensorDyn {
107 pub fn dtype(&self) -> DType {
109 match self {
110 Self::U8(_) => DType::U8,
111 Self::I8(_) => DType::I8,
112 Self::U16(_) => DType::U16,
113 Self::I16(_) => DType::I16,
114 Self::U32(_) => DType::U32,
115 Self::I32(_) => DType::I32,
116 Self::U64(_) => DType::U64,
117 Self::I64(_) => DType::I64,
118 Self::F16(_) => DType::F16,
119 Self::F32(_) => DType::F32,
120 Self::F64(_) => DType::F64,
121 }
122 }
123
124 pub fn shape(&self) -> &[usize] {
126 dispatch!(self, shape)
127 }
128
129 pub fn name(&self) -> String {
131 dispatch!(self, name)
132 }
133
134 pub fn format(&self) -> Option<PixelFormat> {
136 dispatch!(self, format)
137 }
138
139 pub fn width(&self) -> Option<usize> {
141 dispatch!(self, width)
142 }
143
144 pub fn height(&self) -> Option<usize> {
146 dispatch!(self, height)
147 }
148
149 pub fn size(&self) -> usize {
151 dispatch!(self, size)
152 }
153
154 pub fn memory(&self) -> TensorMemory {
156 dispatch!(self, memory)
157 }
158
159 pub fn reshape(&mut self, shape: &[usize]) -> crate::Result<()> {
161 dispatch!(self, reshape, shape)
162 }
163
164 pub fn set_format(&mut self, format: PixelFormat) -> crate::Result<()> {
182 dispatch!(self, set_format, format)
183 }
184
185 pub fn with_format(mut self, format: PixelFormat) -> crate::Result<Self> {
202 self.set_format(format)?;
203 Ok(self)
204 }
205
206 pub fn colorimetry(&self) -> Option<crate::Colorimetry> {
208 dispatch!(self, colorimetry)
209 }
210
211 pub fn set_colorimetry(&mut self, c: Option<crate::Colorimetry>) {
213 dispatch!(self, set_colorimetry, c)
214 }
215
216 pub fn with_colorimetry(mut self, c: crate::Colorimetry) -> Self {
218 self.set_colorimetry(Some(c));
219 self
220 }
221
222 pub fn row_stride(&self) -> Option<usize> {
224 dispatch!(self, row_stride)
225 }
226
227 pub fn effective_row_stride(&self) -> Option<usize> {
229 dispatch!(self, effective_row_stride)
230 }
231
232 pub fn configure_image(
235 &mut self,
236 width: usize,
237 height: usize,
238 format: PixelFormat,
239 ) -> crate::Result<()> {
240 dispatch!(self, configure_image, width, height, format)
241 }
242
243 pub fn set_row_stride(&mut self, stride: usize) -> crate::Result<()> {
249 dispatch!(self, set_row_stride, stride)
250 }
251
252 pub fn with_row_stride(mut self, stride: usize) -> crate::Result<Self> {
254 self.set_row_stride(stride)?;
255 Ok(self)
256 }
257
258 pub fn plane_offset(&self) -> Option<usize> {
260 dispatch!(self, plane_offset)
261 }
262
263 pub fn view_origin(&self) -> Option<crate::ViewOrigin> {
267 dispatch!(self, view_origin)
268 }
269
270 pub fn set_plane_offset(&mut self, offset: usize) {
272 dispatch!(self, set_plane_offset, offset)
273 }
274
275 pub fn batch(&self, n: usize) -> crate::Result<TensorDyn> {
278 dyn_fanout!(self, batch, n)
279 }
280
281 pub fn view(&self, region: crate::Region) -> crate::Result<TensorDyn> {
284 dyn_fanout!(self, view, region)
285 }
286
287 pub fn cuda(&self) -> Option<&crate::cuda::CudaHandle> {
292 dispatch!(self, cuda)
293 }
294
295 pub fn cuda_map(&self) -> Option<crate::cuda::CudaMap<'_>> {
318 dispatch!(self, cuda_map)
319 }
320
321 pub fn quantization(&self) -> Option<&crate::Quantization> {
325 match self {
326 Self::U8(t) => t.quantization(),
327 Self::I8(t) => t.quantization(),
328 Self::U16(t) => t.quantization(),
329 Self::I16(t) => t.quantization(),
330 Self::U32(t) => t.quantization(),
331 Self::I32(t) => t.quantization(),
332 Self::U64(t) => t.quantization(),
333 Self::I64(t) => t.quantization(),
334 Self::F16(_) | Self::F32(_) | Self::F64(_) => None,
335 }
336 }
337
338 pub fn set_quantization(&mut self, q: crate::Quantization) -> crate::Result<()> {
342 match self {
343 Self::U8(t) => t.set_quantization(q),
344 Self::I8(t) => t.set_quantization(q),
345 Self::U16(t) => t.set_quantization(q),
346 Self::I16(t) => t.set_quantization(q),
347 Self::U32(t) => t.set_quantization(q),
348 Self::I32(t) => t.set_quantization(q),
349 Self::U64(t) => t.set_quantization(q),
350 Self::I64(t) => t.set_quantization(q),
351 Self::F16(_) | Self::F32(_) | Self::F64(_) => Err(crate::Error::QuantizationInvalid {
352 field: "dtype_is_integer",
353 expected: "integer tensor dtype (u8/i8/u16/i16/u32/i32/u64/i64)".to_string(),
354 got: format!("{:?}", self.dtype()),
355 }),
356 }
357 }
358
359 pub fn with_quantization(mut self, q: crate::Quantization) -> crate::Result<Self> {
362 self.set_quantization(q)?;
363 Ok(self)
364 }
365
366 pub fn clear_quantization(&mut self) {
368 match self {
369 Self::U8(t) => t.clear_quantization(),
370 Self::I8(t) => t.clear_quantization(),
371 Self::U16(t) => t.clear_quantization(),
372 Self::I16(t) => t.clear_quantization(),
373 Self::U32(t) => t.clear_quantization(),
374 Self::I32(t) => t.clear_quantization(),
375 Self::U64(t) => t.clear_quantization(),
376 Self::I64(t) => t.clear_quantization(),
377 Self::F16(_) | Self::F32(_) | Self::F64(_) => {}
378 }
379 }
380
381 #[cfg(unix)]
383 pub fn clone_fd(&self) -> crate::Result<std::os::fd::OwnedFd> {
384 dispatch!(self, clone_fd)
385 }
386
387 #[cfg(target_os = "linux")]
398 pub fn dmabuf_clone(&self) -> crate::Result<std::os::fd::OwnedFd> {
399 if self.memory() != TensorMemory::Dma {
400 return Err(crate::Error::NotImplemented(format!(
401 "dmabuf_clone requires DMA-backed tensor, got {:?}",
402 self.memory()
403 )));
404 }
405 self.clone_fd()
406 }
407
408 #[cfg(target_os = "linux")]
419 pub fn dmabuf(&self) -> crate::Result<std::os::fd::BorrowedFd<'_>> {
420 dispatch!(self, dmabuf)
421 }
422
423 pub fn is_multiplane(&self) -> bool {
425 dispatch!(self, is_multiplane)
426 }
427
428 pub fn buffer_identity(&self) -> &crate::BufferIdentity {
438 dispatch!(self, buffer_identity)
439 }
440
441 pub fn aliases(&self, other: &Self) -> bool {
459 if self.buffer_identity().id() == other.buffer_identity().id() {
460 return true;
461 }
462 if self.memory() != other.memory() {
463 return false;
464 }
465 #[cfg(target_os = "linux")]
466 if self.memory() == TensorMemory::Dma {
467 use std::os::fd::AsRawFd;
468 if let (Ok(a), Ok(b)) = (self.dmabuf(), other.dmabuf()) {
469 return a.as_raw_fd() == b.as_raw_fd();
470 }
471 }
472 false
473 }
474
475 downcast_methods!(U8, u8, as_u8, as_u8_mut, into_u8);
478 downcast_methods!(I8, i8, as_i8, as_i8_mut, into_i8);
479 downcast_methods!(U16, u16, as_u16, as_u16_mut, into_u16);
480 downcast_methods!(I16, i16, as_i16, as_i16_mut, into_i16);
481 downcast_methods!(U32, u32, as_u32, as_u32_mut, into_u32);
482 downcast_methods!(I32, i32, as_i32, as_i32_mut, into_i32);
483 downcast_methods!(U64, u64, as_u64, as_u64_mut, into_u64);
484 downcast_methods!(I64, i64, as_i64, as_i64_mut, into_i64);
485 downcast_methods!(F16, f16, as_f16, as_f16_mut, into_f16);
486 downcast_methods!(F32, f32, as_f32, as_f32_mut, into_f32);
487 downcast_methods!(F64, f64, as_f64, as_f64_mut, into_f64);
488
489 pub fn new(
491 shape: &[usize],
492 dtype: DType,
493 memory: Option<TensorMemory>,
494 name: Option<&str>,
495 ) -> crate::Result<Self> {
496 match dtype {
497 DType::U8 => Tensor::<u8>::new(shape, memory, name).map(Self::U8),
498 DType::I8 => Tensor::<i8>::new(shape, memory, name).map(Self::I8),
499 DType::U16 => Tensor::<u16>::new(shape, memory, name).map(Self::U16),
500 DType::I16 => Tensor::<i16>::new(shape, memory, name).map(Self::I16),
501 DType::U32 => Tensor::<u32>::new(shape, memory, name).map(Self::U32),
502 DType::I32 => Tensor::<i32>::new(shape, memory, name).map(Self::I32),
503 DType::U64 => Tensor::<u64>::new(shape, memory, name).map(Self::U64),
504 DType::I64 => Tensor::<i64>::new(shape, memory, name).map(Self::I64),
505 DType::F16 => Tensor::<f16>::new(shape, memory, name).map(Self::F16),
506 DType::F32 => Tensor::<f32>::new(shape, memory, name).map(Self::F32),
507 DType::F64 => Tensor::<f64>::new(shape, memory, name).map(Self::F64),
508 }
509 }
510
511 #[cfg(unix)]
538 pub fn from_fd(
539 fd: std::os::fd::OwnedFd,
540 shape: &[usize],
541 dtype: DType,
542 name: Option<&str>,
543 ) -> crate::Result<Self> {
544 match dtype {
545 DType::U8 => Tensor::<u8>::from_fd(fd, shape, name).map(Self::U8),
546 DType::I8 => Tensor::<i8>::from_fd(fd, shape, name).map(Self::I8),
547 DType::U16 => Tensor::<u16>::from_fd(fd, shape, name).map(Self::U16),
548 DType::I16 => Tensor::<i16>::from_fd(fd, shape, name).map(Self::I16),
549 DType::U32 => Tensor::<u32>::from_fd(fd, shape, name).map(Self::U32),
550 DType::I32 => Tensor::<i32>::from_fd(fd, shape, name).map(Self::I32),
551 DType::U64 => Tensor::<u64>::from_fd(fd, shape, name).map(Self::U64),
552 DType::I64 => Tensor::<i64>::from_fd(fd, shape, name).map(Self::I64),
553 DType::F16 => Tensor::<f16>::from_fd(fd, shape, name).map(Self::F16),
554 DType::F32 => Tensor::<f32>::from_fd(fd, shape, name).map(Self::F32),
555 DType::F64 => Tensor::<f64>::from_fd(fd, shape, name).map(Self::F64),
556 }
557 }
558
559 pub unsafe fn from_foreign_ptr(
572 ptr: *mut u8,
573 shape: &[usize],
574 dtype: DType,
575 owner: Option<crate::ForeignOwner>,
576 name: Option<&str>,
577 ) -> crate::Result<Self> {
578 match dtype {
579 DType::U8 => Tensor::<u8>::from_foreign(ptr.cast(), shape, owner, name).map(Self::U8),
580 DType::I8 => Tensor::<i8>::from_foreign(ptr.cast(), shape, owner, name).map(Self::I8),
581 DType::U16 => {
582 Tensor::<u16>::from_foreign(ptr.cast(), shape, owner, name).map(Self::U16)
583 }
584 DType::I16 => {
585 Tensor::<i16>::from_foreign(ptr.cast(), shape, owner, name).map(Self::I16)
586 }
587 DType::U32 => {
588 Tensor::<u32>::from_foreign(ptr.cast(), shape, owner, name).map(Self::U32)
589 }
590 DType::I32 => {
591 Tensor::<i32>::from_foreign(ptr.cast(), shape, owner, name).map(Self::I32)
592 }
593 DType::U64 => {
594 Tensor::<u64>::from_foreign(ptr.cast(), shape, owner, name).map(Self::U64)
595 }
596 DType::I64 => {
597 Tensor::<i64>::from_foreign(ptr.cast(), shape, owner, name).map(Self::I64)
598 }
599 DType::F16 => {
600 Tensor::<f16>::from_foreign(ptr.cast(), shape, owner, name).map(Self::F16)
601 }
602 DType::F32 => {
603 Tensor::<f32>::from_foreign(ptr.cast(), shape, owner, name).map(Self::F32)
604 }
605 DType::F64 => {
606 Tensor::<f64>::from_foreign(ptr.cast(), shape, owner, name).map(Self::F64)
607 }
608 }
609 }
610
611 #[cfg(any(target_os = "macos", target_os = "ios"))]
619 pub unsafe fn from_iosurface(
620 surface_ref: *mut std::ffi::c_void,
621 shape: &[usize],
622 dtype: DType,
623 name: Option<&str>,
624 ) -> crate::Result<Self> {
625 unsafe {
626 match dtype {
627 DType::U8 => Tensor::<u8>::from_iosurface(surface_ref, shape, name).map(Self::U8),
628 DType::I8 => Tensor::<i8>::from_iosurface(surface_ref, shape, name).map(Self::I8),
629 DType::U16 => {
630 Tensor::<u16>::from_iosurface(surface_ref, shape, name).map(Self::U16)
631 }
632 DType::I16 => {
633 Tensor::<i16>::from_iosurface(surface_ref, shape, name).map(Self::I16)
634 }
635 DType::U32 => {
636 Tensor::<u32>::from_iosurface(surface_ref, shape, name).map(Self::U32)
637 }
638 DType::I32 => {
639 Tensor::<i32>::from_iosurface(surface_ref, shape, name).map(Self::I32)
640 }
641 DType::U64 => {
642 Tensor::<u64>::from_iosurface(surface_ref, shape, name).map(Self::U64)
643 }
644 DType::I64 => {
645 Tensor::<i64>::from_iosurface(surface_ref, shape, name).map(Self::I64)
646 }
647 DType::F16 => {
648 Tensor::<f16>::from_iosurface(surface_ref, shape, name).map(Self::F16)
649 }
650 DType::F32 => {
651 Tensor::<f32>::from_iosurface(surface_ref, shape, name).map(Self::F32)
652 }
653 DType::F64 => {
654 Tensor::<f64>::from_iosurface(surface_ref, shape, name).map(Self::F64)
655 }
656 }
657 }
658 }
659
660 #[cfg(any(target_os = "macos", target_os = "ios"))]
663 pub fn iosurface_id(&self) -> Option<u32> {
664 dispatch!(self, iosurface_id)
665 }
666
667 #[cfg(any(target_os = "macos", target_os = "ios"))]
671 pub fn iosurface_ref(&self) -> Option<*mut std::ffi::c_void> {
672 dispatch!(self, iosurface_ref)
673 }
674
675 #[cfg(any(target_os = "macos", target_os = "ios"))]
680 pub fn iosurface_physical_dims(&self) -> Option<(usize, usize)> {
681 dispatch!(self, iosurface_physical_dims)
682 }
683
684 #[cfg(target_os = "android")]
694 pub unsafe fn from_hardware_buffer(
695 buffer_ptr: *mut std::ffi::c_void,
696 shape: &[usize],
697 dtype: DType,
698 name: Option<&str>,
699 ) -> crate::Result<Self> {
700 unsafe {
701 match dtype {
702 DType::U8 => {
703 Tensor::<u8>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::U8)
704 }
705 DType::I8 => {
706 Tensor::<i8>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::I8)
707 }
708 DType::U16 => {
709 Tensor::<u16>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::U16)
710 }
711 DType::I16 => {
712 Tensor::<i16>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::I16)
713 }
714 DType::U32 => {
715 Tensor::<u32>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::U32)
716 }
717 DType::I32 => {
718 Tensor::<i32>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::I32)
719 }
720 DType::U64 => {
721 Tensor::<u64>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::U64)
722 }
723 DType::I64 => {
724 Tensor::<i64>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::I64)
725 }
726 DType::F16 => {
727 Tensor::<f16>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::F16)
728 }
729 DType::F32 => {
730 Tensor::<f32>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::F32)
731 }
732 DType::F64 => {
733 Tensor::<f64>::from_hardware_buffer(buffer_ptr, shape, name).map(Self::F64)
734 }
735 }
736 }
737 }
738
739 #[cfg(target_os = "android")]
743 pub fn hardware_buffer_ptr(&self) -> Option<*mut std::ffi::c_void> {
744 dispatch!(self, hardware_buffer_ptr)
745 }
746
747 #[cfg(target_os = "android")]
751 pub fn hardware_buffer_physical_dims(&self) -> Option<(usize, usize)> {
752 dispatch!(self, hardware_buffer_physical_dims)
753 }
754
755 pub fn copy_to_flat(&self, dst: &mut [u8]) -> crate::Result<()> {
760 dispatch!(self, copy_to_flat, dst)
761 }
762
763 pub fn image(
781 width: usize,
782 height: usize,
783 format: PixelFormat,
784 dtype: DType,
785 memory: Option<TensorMemory>,
786 access: crate::CpuAccess,
787 ) -> crate::Result<Self> {
788 match dtype {
789 DType::U8 => Tensor::<u8>::image(width, height, format, memory, access).map(Self::U8),
790 DType::I8 => Tensor::<i8>::image(width, height, format, memory, access).map(Self::I8),
791 DType::U16 => {
792 Tensor::<u16>::image(width, height, format, memory, access).map(Self::U16)
793 }
794 DType::I16 => {
795 Tensor::<i16>::image(width, height, format, memory, access).map(Self::I16)
796 }
797 DType::U32 => {
798 Tensor::<u32>::image(width, height, format, memory, access).map(Self::U32)
799 }
800 DType::I32 => {
801 Tensor::<i32>::image(width, height, format, memory, access).map(Self::I32)
802 }
803 DType::U64 => {
804 Tensor::<u64>::image(width, height, format, memory, access).map(Self::U64)
805 }
806 DType::I64 => {
807 Tensor::<i64>::image(width, height, format, memory, access).map(Self::I64)
808 }
809 DType::F16 => {
810 Tensor::<f16>::image(width, height, format, memory, access).map(Self::F16)
811 }
812 DType::F32 => {
813 Tensor::<f32>::image(width, height, format, memory, access).map(Self::F32)
814 }
815 DType::F64 => {
816 Tensor::<f64>::image(width, height, format, memory, access).map(Self::F64)
817 }
818 }
819 }
820
821 pub fn image_desc(desc: &crate::ImageDesc) -> crate::Result<Self> {
825 match desc.dtype() {
826 DType::U8 => Tensor::<u8>::image_desc(desc).map(Self::U8),
827 DType::I8 => Tensor::<i8>::image_desc(desc).map(Self::I8),
828 DType::U16 => Tensor::<u16>::image_desc(desc).map(Self::U16),
829 DType::I16 => Tensor::<i16>::image_desc(desc).map(Self::I16),
830 DType::U32 => Tensor::<u32>::image_desc(desc).map(Self::U32),
831 DType::I32 => Tensor::<i32>::image_desc(desc).map(Self::I32),
832 DType::U64 => Tensor::<u64>::image_desc(desc).map(Self::U64),
833 DType::I64 => Tensor::<i64>::image_desc(desc).map(Self::I64),
834 DType::F16 => Tensor::<f16>::image_desc(desc).map(Self::F16),
835 DType::F32 => Tensor::<f32>::image_desc(desc).map(Self::F32),
836 DType::F64 => Tensor::<f64>::image_desc(desc).map(Self::F64),
837 }
838 }
839
840 pub fn compression(&self) -> Option<crate::CompressionScheme> {
843 dispatch!(self, compression)
844 }
845
846 pub fn image_with_stride(
873 width: usize,
874 height: usize,
875 format: PixelFormat,
876 dtype: DType,
877 row_stride_bytes: usize,
878 memory: Option<TensorMemory>,
879 access: crate::CpuAccess,
880 ) -> crate::Result<Self> {
881 match dtype {
882 DType::U8 => Tensor::<u8>::image_with_stride(
883 width,
884 height,
885 format,
886 row_stride_bytes,
887 memory,
888 access,
889 )
890 .map(Self::U8),
891 DType::I8 => Tensor::<i8>::image_with_stride(
892 width,
893 height,
894 format,
895 row_stride_bytes,
896 memory,
897 access,
898 )
899 .map(Self::I8),
900 DType::U16 => Tensor::<u16>::image_with_stride(
901 width,
902 height,
903 format,
904 row_stride_bytes,
905 memory,
906 access,
907 )
908 .map(Self::U16),
909 DType::I16 => Tensor::<i16>::image_with_stride(
910 width,
911 height,
912 format,
913 row_stride_bytes,
914 memory,
915 access,
916 )
917 .map(Self::I16),
918 DType::U32 => Tensor::<u32>::image_with_stride(
919 width,
920 height,
921 format,
922 row_stride_bytes,
923 memory,
924 access,
925 )
926 .map(Self::U32),
927 DType::I32 => Tensor::<i32>::image_with_stride(
928 width,
929 height,
930 format,
931 row_stride_bytes,
932 memory,
933 access,
934 )
935 .map(Self::I32),
936 DType::U64 => Tensor::<u64>::image_with_stride(
937 width,
938 height,
939 format,
940 row_stride_bytes,
941 memory,
942 access,
943 )
944 .map(Self::U64),
945 DType::I64 => Tensor::<i64>::image_with_stride(
946 width,
947 height,
948 format,
949 row_stride_bytes,
950 memory,
951 access,
952 )
953 .map(Self::I64),
954 DType::F16 => Tensor::<f16>::image_with_stride(
955 width,
956 height,
957 format,
958 row_stride_bytes,
959 memory,
960 access,
961 )
962 .map(Self::F16),
963 DType::F32 => Tensor::<f32>::image_with_stride(
964 width,
965 height,
966 format,
967 row_stride_bytes,
968 memory,
969 access,
970 )
971 .map(Self::F32),
972 DType::F64 => Tensor::<f64>::image_with_stride(
973 width,
974 height,
975 format,
976 row_stride_bytes,
977 memory,
978 access,
979 )
980 .map(Self::F64),
981 }
982 }
983}
984
985impl From<Tensor<u8>> for TensorDyn {
988 fn from(t: Tensor<u8>) -> Self {
989 Self::U8(t)
990 }
991}
992
993impl From<Tensor<i8>> for TensorDyn {
994 fn from(t: Tensor<i8>) -> Self {
995 Self::I8(t)
996 }
997}
998
999impl From<Tensor<u16>> for TensorDyn {
1000 fn from(t: Tensor<u16>) -> Self {
1001 Self::U16(t)
1002 }
1003}
1004
1005impl From<Tensor<i16>> for TensorDyn {
1006 fn from(t: Tensor<i16>) -> Self {
1007 Self::I16(t)
1008 }
1009}
1010
1011impl From<Tensor<u32>> for TensorDyn {
1012 fn from(t: Tensor<u32>) -> Self {
1013 Self::U32(t)
1014 }
1015}
1016
1017impl From<Tensor<i32>> for TensorDyn {
1018 fn from(t: Tensor<i32>) -> Self {
1019 Self::I32(t)
1020 }
1021}
1022
1023impl From<Tensor<u64>> for TensorDyn {
1024 fn from(t: Tensor<u64>) -> Self {
1025 Self::U64(t)
1026 }
1027}
1028
1029impl From<Tensor<i64>> for TensorDyn {
1030 fn from(t: Tensor<i64>) -> Self {
1031 Self::I64(t)
1032 }
1033}
1034
1035impl From<Tensor<f16>> for TensorDyn {
1036 fn from(t: Tensor<f16>) -> Self {
1037 Self::F16(t)
1038 }
1039}
1040
1041impl From<Tensor<f32>> for TensorDyn {
1042 fn from(t: Tensor<f32>) -> Self {
1043 Self::F32(t)
1044 }
1045}
1046
1047impl From<Tensor<f64>> for TensorDyn {
1048 fn from(t: Tensor<f64>) -> Self {
1049 Self::F64(t)
1050 }
1051}
1052
1053impl fmt::Debug for TensorDyn {
1054 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1055 dispatch!(self, fmt, f)
1056 }
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061 use super::*;
1062
1063 #[test]
1064 fn from_typed_tensor() {
1065 let t = Tensor::<u8>::new(&[10], None, None).unwrap();
1066 let dyn_t: TensorDyn = t.into();
1067 assert_eq!(dyn_t.dtype(), DType::U8);
1068 assert_eq!(dyn_t.shape(), &[10]);
1069 }
1070
1071 #[test]
1072 fn from_foreign_ptr_wraps_borrowed_memory() {
1073 use crate::TensorMapTrait;
1074 let mut vec: Vec<f32> = vec![0.0; 4];
1077 let ptr = vec.as_mut_ptr() as *mut u8;
1078 let owner: crate::ForeignOwner = Box::new(vec);
1079 let t = unsafe {
1080 TensorDyn::from_foreign_ptr(ptr, &[2, 2], DType::F32, Some(owner), Some("trt_output"))
1081 }
1082 .unwrap();
1083 assert_eq!(t.dtype(), DType::F32);
1084 assert_eq!(t.memory(), TensorMemory::Mem);
1085 assert_eq!(t.shape(), &[2, 2]);
1086 {
1087 let mut m = t.as_f32().unwrap().map().unwrap();
1088 m.as_mut_slice().copy_from_slice(&[1.0, 2.0, 3.0, 4.0]);
1089 }
1090 let m = t.as_f32().unwrap().map().unwrap();
1091 assert_eq!(m.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
1092 }
1093
1094 #[test]
1103 fn from_foreign_ptr_rejects_null_ptr() {
1104 let err = unsafe {
1105 TensorDyn::from_foreign_ptr(std::ptr::null_mut(), &[4], DType::U8, None, None)
1106 }
1107 .unwrap_err();
1108 assert!(
1110 matches!(err, crate::error::Error::InvalidArgument(ref m) if m.contains("non-null")),
1111 "expected InvalidArgument(non-null), got {err:?}"
1112 );
1113 }
1114
1115 #[test]
1116 fn from_foreign_ptr_rejects_empty_shape() {
1117 let mut dummy: u8 = 0;
1118 let err = unsafe {
1119 TensorDyn::from_foreign_ptr(&mut dummy as *mut u8, &[], DType::U8, None, None)
1120 }
1121 .unwrap_err();
1122 assert!(
1123 matches!(err, crate::error::Error::InvalidSize(0)),
1124 "expected InvalidSize(0) for empty shape, got {err:?}"
1125 );
1126 }
1127
1128 #[test]
1129 fn from_foreign_ptr_rejects_overflow_shape() {
1130 let mut dummy: u8 = 0;
1131 let huge = [usize::MAX / 2 + 1, 2];
1132 let err = unsafe { TensorDyn::from_foreign_ptr(&mut dummy, &huge, DType::U8, None, None) }
1133 .unwrap_err();
1134 assert!(
1135 matches!(err, crate::error::Error::InvalidArgument(ref m) if m.contains("overflow")),
1136 "expected InvalidArgument(overflow), got {err:?}"
1137 );
1138 }
1139
1140 #[test]
1141 fn from_foreign_ptr_u8_dtype_dispatch() {
1142 let mut buf: Vec<u8> = vec![1, 2, 3, 4];
1145 let ptr = buf.as_mut_ptr();
1146 let owner: crate::ForeignOwner = Box::new(buf);
1147 let t = unsafe {
1148 TensorDyn::from_foreign_ptr(ptr, &[4], DType::U8, Some(owner), Some("u8_foreign"))
1149 }
1150 .unwrap();
1151 assert_eq!(t.dtype(), DType::U8);
1152 assert_eq!(t.shape(), &[4]);
1153 let m = t.as_u8().unwrap().map().unwrap();
1154 use crate::TensorMapTrait;
1155 assert_eq!(m.as_slice(), &[1u8, 2, 3, 4]);
1156 }
1157
1158 #[test]
1159 fn downcast_ref() {
1160 let t = Tensor::<u8>::new(&[10], None, None).unwrap();
1161 let dyn_t: TensorDyn = t.into();
1162 assert!(dyn_t.as_u8().is_some());
1163 assert!(dyn_t.as_i8().is_none());
1164 }
1165
1166 #[test]
1167 fn downcast_into() {
1168 let t = Tensor::<u8>::new(&[10], None, None).unwrap();
1169 let dyn_t: TensorDyn = t.into();
1170 let back = dyn_t.into_u8().unwrap();
1171 assert_eq!(back.shape(), &[10]);
1172 }
1173
1174 #[test]
1175 fn image_accessors() {
1176 let t = Tensor::<u8>::image(
1177 640,
1178 480,
1179 PixelFormat::Rgba,
1180 None,
1181 crate::CpuAccess::ReadWrite,
1182 )
1183 .unwrap();
1184 let dyn_t: TensorDyn = t.into();
1185 assert_eq!(dyn_t.format(), Some(PixelFormat::Rgba));
1186 assert_eq!(dyn_t.width(), Some(640));
1187 assert_eq!(dyn_t.height(), Some(480));
1188 assert!(!dyn_t.is_multiplane());
1189 }
1190
1191 #[test]
1192 fn image_constructor() {
1193 let dyn_t = TensorDyn::image(
1194 640,
1195 480,
1196 PixelFormat::Rgb,
1197 DType::U8,
1198 None,
1199 crate::CpuAccess::ReadWrite,
1200 )
1201 .unwrap();
1202 assert_eq!(dyn_t.dtype(), DType::U8);
1203 assert_eq!(dyn_t.format(), Some(PixelFormat::Rgb));
1204 assert_eq!(dyn_t.width(), Some(640));
1205 }
1206
1207 #[test]
1208 fn image_constructor_i8() {
1209 let dyn_t = TensorDyn::image(
1210 640,
1211 480,
1212 PixelFormat::Rgb,
1213 DType::I8,
1214 None,
1215 crate::CpuAccess::ReadWrite,
1216 )
1217 .unwrap();
1218 assert_eq!(dyn_t.dtype(), DType::I8);
1219 assert_eq!(dyn_t.format(), Some(PixelFormat::Rgb));
1220 }
1221
1222 #[test]
1223 fn set_format_packed() {
1224 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1225 assert_eq!(t.format(), None);
1226 t.set_format(PixelFormat::Rgb).unwrap();
1227 assert_eq!(t.format(), Some(PixelFormat::Rgb));
1228 assert_eq!(t.width(), Some(640));
1229 assert_eq!(t.height(), Some(480));
1230 }
1231
1232 #[test]
1233 fn set_format_planar() {
1234 let mut t = TensorDyn::new(&[3, 480, 640], DType::U8, None, None).unwrap();
1235 t.set_format(PixelFormat::PlanarRgb).unwrap();
1236 assert_eq!(t.format(), Some(PixelFormat::PlanarRgb));
1237 assert_eq!(t.width(), Some(640));
1238 assert_eq!(t.height(), Some(480));
1239 }
1240
1241 #[test]
1242 fn set_format_rejects_wrong_shape() {
1243 let mut t = TensorDyn::new(&[480, 640, 4], DType::U8, None, None).unwrap();
1244 assert!(t.set_format(PixelFormat::Rgb).is_err());
1245 }
1246
1247 #[test]
1248 fn with_format_builder() {
1249 let t = TensorDyn::new(&[480, 640, 4], DType::U8, None, None)
1250 .unwrap()
1251 .with_format(PixelFormat::Rgba)
1252 .unwrap();
1253 assert_eq!(t.format(), Some(PixelFormat::Rgba));
1254 assert_eq!(t.width(), Some(640));
1255 assert_eq!(t.height(), Some(480));
1256 }
1257
1258 #[cfg(target_os = "linux")]
1259 #[test]
1260 fn dmabuf_clone_mem_tensor_fails() {
1261 let t = TensorDyn::new(&[480, 640, 3], DType::U8, Some(TensorMemory::Mem), None).unwrap();
1262 assert_eq!(t.memory(), TensorMemory::Mem);
1263 assert!(t.dmabuf_clone().is_err());
1264 }
1265
1266 #[cfg(target_os = "linux")]
1267 #[test]
1268 fn dmabuf_mem_tensor_fails() {
1269 let t = TensorDyn::new(&[480, 640, 3], DType::U8, Some(TensorMemory::Mem), None).unwrap();
1270 assert!(t.dmabuf().is_err());
1271 }
1272
1273 #[test]
1274 fn set_format_semi_planar_nv12() {
1275 let mut t = TensorDyn::new(&[720, 640], DType::U8, Some(TensorMemory::Mem), None).unwrap();
1277 t.set_format(PixelFormat::Nv12).unwrap();
1278 assert_eq!(t.format(), Some(PixelFormat::Nv12));
1279 assert_eq!(t.width(), Some(640));
1280 assert_eq!(t.height(), Some(480));
1281 }
1282
1283 #[test]
1284 fn set_format_semi_planar_nv16() {
1285 let mut t = TensorDyn::new(&[960, 640], DType::U8, Some(TensorMemory::Mem), None).unwrap();
1287 t.set_format(PixelFormat::Nv16).unwrap();
1288 assert_eq!(t.format(), Some(PixelFormat::Nv16));
1289 assert_eq!(t.width(), Some(640));
1290 assert_eq!(t.height(), Some(480));
1291 }
1292
1293 #[test]
1294 fn with_format_rejects_wrong_shape() {
1295 let result = TensorDyn::new(&[480, 640, 4], DType::U8, None, None)
1296 .unwrap()
1297 .with_format(PixelFormat::Rgb);
1298 assert!(result.is_err());
1299 }
1300
1301 #[test]
1302 fn set_format_preserved_after_rejection() {
1303 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1304 t.set_format(PixelFormat::Rgb).unwrap();
1305 assert_eq!(t.format(), Some(PixelFormat::Rgb));
1306
1307 assert!(t.set_format(PixelFormat::Rgba).is_err());
1309
1310 assert_eq!(t.format(), Some(PixelFormat::Rgb));
1312 }
1313
1314 #[test]
1315 fn set_format_idempotent() {
1316 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1317 t.set_format(PixelFormat::Rgb).unwrap();
1318 t.set_format(PixelFormat::Rgb).unwrap();
1319 assert_eq!(t.format(), Some(PixelFormat::Rgb));
1320 assert_eq!(t.width(), Some(640));
1321 assert_eq!(t.height(), Some(480));
1322 }
1323
1324 #[test]
1327 fn set_row_stride_valid() {
1328 let mut t = TensorDyn::image(
1330 100,
1331 100,
1332 PixelFormat::Rgba,
1333 DType::U8,
1334 None,
1335 crate::CpuAccess::ReadWrite,
1336 )
1337 .unwrap();
1338 t.set_row_stride(512).unwrap();
1339 assert_eq!(t.row_stride(), Some(512));
1340 assert_eq!(t.effective_row_stride(), Some(512));
1341 }
1342
1343 #[test]
1344 fn set_row_stride_equals_min() {
1345 let mut t = TensorDyn::image(
1347 100,
1348 100,
1349 PixelFormat::Rgb,
1350 DType::U8,
1351 None,
1352 crate::CpuAccess::ReadWrite,
1353 )
1354 .unwrap();
1355 t.set_row_stride(300).unwrap();
1356 assert_eq!(t.row_stride(), Some(300));
1357 }
1358
1359 #[test]
1360 fn set_row_stride_too_small() {
1361 let mut t = TensorDyn::image(
1366 64,
1367 100,
1368 PixelFormat::Rgba,
1369 DType::U8,
1370 None,
1371 crate::CpuAccess::ReadWrite,
1372 )
1373 .unwrap();
1374 assert!(t.set_row_stride(200).is_err());
1375 assert_eq!(t.row_stride(), None);
1376 }
1377
1378 #[test]
1379 fn set_row_stride_zero() {
1380 let mut t = TensorDyn::image(
1381 100,
1382 100,
1383 PixelFormat::Rgb,
1384 DType::U8,
1385 None,
1386 crate::CpuAccess::ReadWrite,
1387 )
1388 .unwrap();
1389 assert!(t.set_row_stride(0).is_err());
1390 }
1391
1392 #[test]
1393 fn set_row_stride_requires_format() {
1394 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1395 assert!(t.set_row_stride(2048).is_err());
1396 }
1397
1398 #[test]
1399 fn effective_row_stride_without_stride() {
1400 let t = TensorDyn::image(
1405 64,
1406 100,
1407 PixelFormat::Rgb,
1408 DType::U8,
1409 None,
1410 crate::CpuAccess::ReadWrite,
1411 )
1412 .unwrap();
1413 assert_eq!(t.row_stride(), None);
1414 assert_eq!(t.effective_row_stride(), Some(192)); }
1416
1417 #[test]
1418 fn effective_row_stride_padded_packed_dma() {
1419 let t = match TensorDyn::image(
1425 100,
1426 100,
1427 PixelFormat::Rgb,
1428 DType::U8,
1429 Some(TensorMemory::Dma),
1430 crate::CpuAccess::ReadWrite,
1431 ) {
1432 Ok(t) if t.memory() == TensorMemory::Dma => t,
1433 _ => return,
1434 };
1435 assert_eq!(t.row_stride(), Some(320));
1436 assert_eq!(t.effective_row_stride(), Some(320));
1437 }
1438
1439 #[test]
1440 fn effective_row_stride_no_format() {
1441 let t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1442 assert_eq!(t.effective_row_stride(), None);
1443 }
1444
1445 #[test]
1446 fn with_row_stride_builder() {
1447 let t = TensorDyn::image(
1448 100,
1449 100,
1450 PixelFormat::Rgba,
1451 DType::U8,
1452 None,
1453 crate::CpuAccess::ReadWrite,
1454 )
1455 .unwrap()
1456 .with_row_stride(512)
1457 .unwrap();
1458 assert_eq!(t.row_stride(), Some(512));
1459 assert_eq!(t.effective_row_stride(), Some(512));
1460 }
1461
1462 #[test]
1463 fn with_row_stride_rejects_small() {
1464 let result = TensorDyn::image(
1465 100,
1466 100,
1467 PixelFormat::Rgba,
1468 DType::U8,
1469 None,
1470 crate::CpuAccess::ReadWrite,
1471 )
1472 .unwrap()
1473 .with_row_stride(200);
1474 assert!(result.is_err());
1475 }
1476
1477 #[test]
1478 fn set_format_clears_row_stride() {
1479 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1480 t.set_format(PixelFormat::Rgb).unwrap();
1481 t.set_row_stride(2048).unwrap();
1482 assert_eq!(t.row_stride(), Some(2048));
1483
1484 let _ = t.set_format(PixelFormat::Bgra);
1486 assert_eq!(t.row_stride(), Some(2048));
1487
1488 t.set_format(PixelFormat::Rgb).unwrap();
1490 assert_eq!(t.row_stride(), Some(2048));
1491
1492 t.reshape(&[480 * 640 * 3]).unwrap();
1494 assert_eq!(t.row_stride(), None);
1495 assert_eq!(t.format(), None);
1496 }
1497
1498 #[test]
1499 fn set_format_different_compatible_clears_stride() {
1500 let mut t = TensorDyn::new(&[480, 640, 4], DType::U8, None, None).unwrap();
1503 t.set_format(PixelFormat::Rgba).unwrap();
1504 t.set_row_stride(4096).unwrap();
1505 assert_eq!(t.row_stride(), Some(4096));
1506
1507 t.set_format(PixelFormat::Bgra).unwrap();
1509 assert_eq!(t.format(), Some(PixelFormat::Bgra));
1510 assert_eq!(t.row_stride(), None);
1511 }
1512
1513 #[test]
1514 fn set_format_same_preserves_stride() {
1515 let mut t = TensorDyn::image(
1516 100,
1517 100,
1518 PixelFormat::Rgb,
1519 DType::U8,
1520 None,
1521 crate::CpuAccess::ReadWrite,
1522 )
1523 .unwrap();
1524 t.set_row_stride(512).unwrap();
1525 t.set_format(PixelFormat::Rgb).unwrap();
1527 assert_eq!(t.row_stride(), Some(512));
1528 }
1529
1530 #[test]
1531 fn effective_row_stride_planar() {
1532 let t = TensorDyn::image(
1533 640,
1534 480,
1535 PixelFormat::PlanarRgb,
1536 DType::U8,
1537 None,
1538 crate::CpuAccess::ReadWrite,
1539 )
1540 .unwrap();
1541 assert_eq!(t.effective_row_stride(), Some(640)); }
1543
1544 #[test]
1545 fn effective_row_stride_nv12() {
1546 let t = TensorDyn::image(
1547 640,
1548 480,
1549 PixelFormat::Nv12,
1550 DType::U8,
1551 None,
1552 crate::CpuAccess::ReadWrite,
1553 )
1554 .unwrap();
1555 assert_eq!(t.effective_row_stride(), Some(640)); }
1557
1558 #[test]
1559 fn map_rejects_strided_tensor() {
1560 let mut t = Tensor::<u8>::image(
1561 100,
1562 100,
1563 PixelFormat::Rgba,
1564 Some(TensorMemory::Mem),
1565 crate::CpuAccess::ReadWrite,
1566 )
1567 .unwrap();
1568 assert!(t.map().is_ok());
1570 t.set_row_stride(512).unwrap();
1572 let err = t.map();
1573 assert!(err.is_err());
1574 }
1575
1576 #[test]
1579 fn plane_offset_default_none() {
1580 let t = TensorDyn::image(
1581 100,
1582 100,
1583 PixelFormat::Rgba,
1584 DType::U8,
1585 None,
1586 crate::CpuAccess::ReadWrite,
1587 )
1588 .unwrap();
1589 assert_eq!(t.plane_offset(), None);
1590 }
1591
1592 #[test]
1593 fn set_plane_offset_basic() {
1594 let mut t = TensorDyn::image(
1595 100,
1596 100,
1597 PixelFormat::Rgba,
1598 DType::U8,
1599 None,
1600 crate::CpuAccess::ReadWrite,
1601 )
1602 .unwrap();
1603 t.set_plane_offset(4096);
1604 assert_eq!(t.plane_offset(), Some(4096));
1605 }
1606
1607 #[test]
1608 fn set_plane_offset_zero() {
1609 let mut t = TensorDyn::image(
1610 100,
1611 100,
1612 PixelFormat::Rgb,
1613 DType::U8,
1614 None,
1615 crate::CpuAccess::ReadWrite,
1616 )
1617 .unwrap();
1618 t.set_plane_offset(0);
1619 assert_eq!(t.plane_offset(), Some(0));
1620 }
1621
1622 #[test]
1623 fn set_plane_offset_no_format() {
1624 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1626 t.set_plane_offset(4096);
1627 assert_eq!(t.plane_offset(), Some(4096));
1628 }
1629
1630 #[test]
1631 fn set_format_clears_plane_offset() {
1632 let mut t = TensorDyn::new(&[480, 640, 3], DType::U8, None, None).unwrap();
1633 t.set_format(PixelFormat::Rgb).unwrap();
1634 t.set_plane_offset(4096);
1635 assert_eq!(t.plane_offset(), Some(4096));
1636
1637 t.set_format(PixelFormat::Rgb).unwrap();
1639 assert_eq!(t.plane_offset(), Some(4096));
1640
1641 t.reshape(&[480 * 640 * 3]).unwrap();
1643 assert_eq!(t.plane_offset(), None);
1644 assert_eq!(t.format(), None);
1645 }
1646
1647 #[test]
1648 fn map_rejects_out_of_bounds_offset() {
1649 let mut t = Tensor::<u8>::image(
1650 100,
1651 100,
1652 PixelFormat::Rgba,
1653 Some(TensorMemory::Mem),
1654 crate::CpuAccess::ReadWrite,
1655 )
1656 .unwrap();
1657 assert!(t.map().is_ok());
1659 t.set_plane_offset(4096);
1662 assert!(t.map().is_err());
1663 }
1664
1665 #[test]
1666 fn mem_subview_in_bounds_maps_at_offset() {
1667 let parent = Tensor::<u8>::image(
1670 100,
1671 100,
1672 PixelFormat::Rgba,
1673 Some(TensorMemory::Mem),
1674 crate::CpuAccess::ReadWrite,
1675 )
1676 .unwrap();
1677 let view = parent.subview(4096, &[10, 10, 4]).unwrap();
1679 assert_eq!(view.plane_offset(), Some(4096));
1680 assert!(view.map().is_ok());
1681 }
1682
1683 #[test]
1684 fn dyn_batch_dispatches_every_dtype() {
1685 use DType::*;
1689 for dt in [U8, I8, U16, I16, U32, I32, U64, I64, F16, F32, F64] {
1690 let parent = TensorDyn::new(&[2, 4], dt, Some(TensorMemory::Mem), None).unwrap();
1691 let view = parent.batch(1).unwrap();
1692 assert_eq!(view.dtype(), dt, "batch must preserve dtype {dt:?}");
1693 assert_eq!(view.shape(), &[4], "{dt:?}");
1694 }
1695 }
1696
1697 #[test]
1698 fn map_accepts_zero_offset_tensor() {
1699 let mut t = Tensor::<u8>::image(
1700 100,
1701 100,
1702 PixelFormat::Rgba,
1703 Some(TensorMemory::Mem),
1704 crate::CpuAccess::ReadWrite,
1705 )
1706 .unwrap();
1707 t.set_plane_offset(0);
1708 assert!(t.map().is_ok());
1710 }
1711
1712 #[test]
1713 fn dyn_configure_image_nv12() {
1714 let mut t = TensorDyn::image(
1715 640,
1716 480,
1717 PixelFormat::Rgb,
1718 DType::U8,
1719 None,
1720 crate::CpuAccess::ReadWrite,
1721 )
1722 .unwrap();
1723 t.configure_image(320, 240, PixelFormat::Nv12).unwrap();
1724 assert_eq!(t.format(), Some(PixelFormat::Nv12));
1725 assert_eq!((t.width(), t.height()), (Some(320), Some(240)));
1726 }
1727
1728 #[test]
1729 fn tensordyn_colorimetry_roundtrip() {
1730 use crate::{ColorEncoding, Colorimetry, DType, PixelFormat};
1731 let mut t = TensorDyn::image(
1732 1280,
1733 720,
1734 PixelFormat::Nv12,
1735 DType::U8,
1736 None,
1737 crate::CpuAccess::ReadWrite,
1738 )
1739 .unwrap();
1740 assert_eq!(t.colorimetry(), None);
1741 let c = Colorimetry::default().with_encoding(ColorEncoding::Bt709);
1742 t.set_colorimetry(Some(c));
1743 assert_eq!(t.colorimetry(), Some(c));
1744 }
1745
1746 #[test]
1747 fn from_planes_propagates_plane_offset() {
1748 let mut luma =
1749 Tensor::<u8>::new(&[480, 640], Some(TensorMemory::Mem), Some("luma")).unwrap();
1750 luma.set_plane_offset(4096);
1751 let chroma =
1752 Tensor::<u8>::new(&[240, 640], Some(TensorMemory::Mem), Some("chroma")).unwrap();
1753 let combined = Tensor::<u8>::from_planes(luma, chroma, PixelFormat::Nv12).unwrap();
1754 assert_eq!(combined.plane_offset(), Some(4096));
1755 }
1756
1757 #[test]
1758 fn cuda_passthrough_none_for_mem_tensor() {
1759 let t: TensorDyn = Tensor::<f32>::new(&[10], Some(TensorMemory::Mem), None)
1762 .unwrap()
1763 .into();
1764 assert!(t.cuda().is_none());
1765 assert!(t.cuda_map().is_none());
1766 }
1767}