1use core::f32;
2
3use alloc::format;
4use alloc::vec::Vec;
5use bytemuck::checked::CheckedCastError;
6use rand::Rng;
7use thiserror::Error;
8
9use crate::Scalar;
10use crate::distribution::Distribution;
11use crate::element::{Element, ElementConversion};
12use crate::tensor::DType;
13use crate::{
14 AccessError, BoolStore, Bytes, ExecutionError, QuantMode, QuantScheme, QuantValue,
15 QuantizedBytes, Reader, Shape, Writer, bf16, f16,
16};
17
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Error, PartialEq, Eq)]
22pub enum DataError {
23 #[error("Failed to access TensorData storage: {0}")]
25 StorageAccess(#[from] AccessError),
26
27 #[error("TensorData storage is invalid for the requested element type: {0}")]
29 InvalidRepresentation(CheckedCastError),
30
31 #[error("Expected data type {expected:?}, but got {actual:?}")]
33 DTypeMismatch {
34 expected: DType,
36
37 actual: DType,
39 },
40
41 #[error("Unsupported data conversion from {from:?} to {to:?}")]
43 UnsupportedConversion {
44 from: DType,
46
47 to: DType,
49 },
50
51 #[error("TensorData shape describes {expected} element(s), but storage contains {actual}")]
53 ElementCountMismatch {
54 expected: usize,
56
57 actual: usize,
59 },
60
61 #[error(
63 "Shape {:?} with dtype {dtype:?} is invalid for input of size {len} bytes",
64 shape.as_slice()
65 )]
66 InvalidByteLength {
67 shape: Shape,
69
70 dtype: DType,
72
73 len: usize,
75 },
76}
77
78#[derive(Debug, Error)]
80pub enum TensorReadError {
81 #[error(transparent)]
83 Execution(#[from] ExecutionError),
84
85 #[error(transparent)]
87 Data(#[from] DataError),
88
89 #[error("Expected {expected} tensor element(s), but got {actual}")]
91 InvalidShape {
92 expected: usize,
94
95 actual: usize,
97 },
98}
99
100impl DataError {
101 pub(super) fn dtype_mismatch_as<E: Element>(actual: DType) -> Self {
103 Self::DTypeMismatch {
104 expected: E::dtype(),
105 actual,
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(try_from = "TensorDataDe")]
117pub struct TensorData {
118 pub(in crate::data) bytes: Bytes,
120
121 #[serde(with = "shape_inner")]
123 pub(in crate::data) shape: Shape,
124
125 pub(in crate::data) dtype: DType,
127}
128
129#[derive(Deserialize)]
132struct TensorDataDe {
133 bytes: Bytes,
134 #[serde(with = "shape_inner")]
135 shape: Shape,
136 dtype: DType,
137}
138
139impl TryFrom<TensorDataDe> for TensorData {
140 type Error = DataError;
141
142 fn try_from(data: TensorDataDe) -> Result<Self, Self::Error> {
143 TensorData::try_from_bytes(data.bytes, data.shape, data.dtype)
144 }
145}
146
147mod shape_inner {
149 use crate::SmallVec;
150
151 use super::*;
152
153 pub fn serialize<S: serde::Serializer>(
154 shape: &Shape,
155 serializer: S,
156 ) -> Result<S::Ok, S::Error> {
157 shape.as_slice().serialize(serializer)
158 }
159
160 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
161 deserializer: D,
162 ) -> Result<Shape, D::Error> {
163 let dims = SmallVec::<[usize; _]>::deserialize(deserializer)?;
164 Ok(Shape::new_raw(dims))
165 }
166}
167
168impl TensorData {
169 pub fn new<E: Element, S: Into<Shape>>(value: Vec<E>, shape: S) -> Self {
171 let shape = shape.into();
173 Self::check_data_len(&value, &shape);
174
175 Self {
176 bytes: Bytes::from_elems(value),
177 shape,
178 dtype: E::dtype(),
179 }
180 }
181
182 pub fn quantized<E: Element, S: Into<Shape>>(
184 value: Vec<E>,
185 shape: S,
186 scheme: QuantScheme,
187 qparams: &[f32],
188 global: Option<f32>,
189 ) -> Self {
190 let shape = shape.into();
191 Self::check_data_len(&value, &shape);
192
193 let q_bytes = QuantizedBytes::new(value, shape.clone(), scheme, qparams, global);
194
195 Self {
196 bytes: q_bytes.bytes,
197 shape,
198 dtype: DType::QFloat(q_bytes.scheme),
199 }
200 }
201
202 #[track_caller]
209 pub fn from_bytes<S: Into<Shape>>(bytes: Bytes, shape: S, dtype: DType) -> Self {
210 match Self::try_from_bytes(bytes, shape, dtype) {
211 Ok(data) => data,
212 Err(err) => panic!("{err}"),
213 }
214 }
215
216 #[track_caller]
226 pub fn from_bytes_vec<S: Into<Shape>>(bytes: Vec<u8>, shape: S, dtype: DType) -> Self {
227 Self::from_bytes(Bytes::from_bytes_vec(bytes), shape, dtype)
228 }
229
230 pub fn try_from_bytes<S: Into<Shape>>(
242 bytes: Bytes,
243 shape: S,
244 dtype: DType,
245 ) -> Result<Self, DataError> {
246 let shape = shape.into();
247 let expected = checked_numel(&shape).and_then(|numel| numel.checked_mul(dtype.size()));
252
253 if !matches!(dtype, DType::QFloat(_)) && expected != Some(bytes.len()) {
254 return Err(DataError::InvalidByteLength {
255 shape,
256 dtype,
257 len: bytes.len(),
258 });
259 }
260
261 Ok(Self {
262 bytes,
263 shape,
264 dtype,
265 })
266 }
267
268 pub fn try_from_bytes_vec<S: Into<Shape>>(
274 bytes: Vec<u8>,
275 shape: S,
276 dtype: DType,
277 ) -> Result<Self, DataError> {
278 Self::try_from_bytes(Bytes::from_bytes_vec(bytes), shape, dtype)
279 }
280
281 #[cfg(test)]
284 pub(crate) fn from_bytes_unchecked<S: Into<Shape>>(
285 bytes: Bytes,
286 shape: S,
287 dtype: DType,
288 ) -> Self {
289 Self {
290 bytes,
291 shape: shape.into(),
292 dtype,
293 }
294 }
295
296 pub fn shape(&self) -> &Shape {
298 &self.shape
299 }
300
301 pub fn dtype(&self) -> DType {
303 self.dtype
304 }
305
306 pub fn bytes(&self) -> &Bytes {
308 &self.bytes
309 }
310
311 pub fn into_parts(self) -> (Bytes, Shape, DType) {
313 (self.bytes, self.shape, self.dtype)
314 }
315
316 #[track_caller]
325 pub fn with_bytes_mut<'a, R>(
326 data: impl IntoIterator<Item = &'a mut TensorData>,
327 f: impl FnOnce(Vec<&mut Bytes>) -> R,
328 ) -> R {
329 let mut guard = LengthGuard::new(data.into_iter().collect());
330 let out = f(guard.data.iter_mut().map(|data| &mut data.bytes).collect());
331 let changed = guard.reset_changed();
332 assert!(!changed, "TensorData byte storage length must not change");
333 out
334 }
335
336 fn check_data_len<E: Element>(data: &[E], shape: &Shape) {
338 let expected_data_len = numel(shape);
339 let num_data = data.len();
340 assert_eq!(
341 expected_data_len, num_data,
342 "Shape {shape:?} is invalid for input of size {num_data:?}",
343 );
344 }
345
346 pub fn as_slice<E: Element>(&self) -> Result<&[E], DataError> {
355 if self.matches_target_dtype::<E>() {
356 bytemuck::checked::try_cast_slice(self.bytes.read(Reader::new())?)
357 .map_err(DataError::InvalidRepresentation)
358 } else {
359 Err(DataError::dtype_mismatch_as::<E>(self.dtype))
360 }
361 }
362
363 pub fn as_mut_slice<E: Element>(&mut self) -> Result<&mut [E], DataError> {
372 if self.matches_target_dtype::<E>() {
373 bytemuck::checked::try_cast_slice_mut(self.bytes.write(Writer::new())?)
374 .map_err(DataError::InvalidRepresentation)
375 } else {
376 Err(DataError::dtype_mismatch_as::<E>(self.dtype))
377 }
378 }
379
380 pub(super) fn matches_target_dtype<E: Element>(&self) -> bool {
381 let target_dtype = E::dtype();
382 match self.dtype {
383 DType::Bool(BoolStore::U8) => {
384 matches!(target_dtype, DType::U8 | DType::Bool(BoolStore::U8))
385 }
386 DType::Bool(BoolStore::U32) => {
387 matches!(target_dtype, DType::U32 | DType::Bool(BoolStore::U32))
388 }
389 dtype => dtype == target_dtype,
390 }
391 }
392
393 pub fn rank(&self) -> usize {
395 self.shape.len()
396 }
397
398 pub fn num_elements(&self) -> usize {
400 numel(&self.shape)
401 }
402
403 pub fn random<E: Element, R: Rng, S: Into<Shape>>(
405 shape: S,
406 distribution: Distribution,
407 rng: &mut R,
408 ) -> Self {
409 let shape = shape.into();
410 let num_elements = numel(&shape);
411 let mut data = Vec::with_capacity(num_elements);
412
413 for _ in 0..num_elements {
414 data.push(E::random(distribution, rng));
415 }
416
417 TensorData::new(data, shape)
418 }
419
420 pub fn zeros<E: Element, S: Into<Shape>>(shape: S) -> TensorData {
422 let shape = shape.into();
423 let num_elements = numel(&shape);
424 let mut data = Vec::<E>::with_capacity(num_elements);
425
426 for _ in 0..num_elements {
427 data.push(0.elem());
428 }
429
430 TensorData::new(data, shape)
431 }
432
433 pub fn ones<E: Element, S: Into<Shape>>(shape: S) -> TensorData {
435 let shape = shape.into();
436 let num_elements = numel(&shape);
437 let mut data = Vec::<E>::with_capacity(num_elements);
438
439 for _ in 0..num_elements {
440 data.push(1.elem());
441 }
442
443 TensorData::new(data, shape)
444 }
445
446 pub fn full<E: Element, S: Into<Shape>>(shape: S, fill_value: E) -> TensorData {
448 let shape = shape.into();
449 let num_elements = numel(&shape);
450 let mut data = Vec::<E>::with_capacity(num_elements);
451 for _ in 0..num_elements {
452 data.push(fill_value)
453 }
454
455 TensorData::new(data, shape)
456 }
457
458 pub fn full_dtype<E: Into<Scalar>, S: Into<Shape>>(
460 shape: S,
461 fill_value: E,
462 dtype: DType,
463 ) -> TensorData {
464 let fill_value = fill_value.into();
465 match dtype {
466 DType::F64 => Self::full::<f64, _>(shape, fill_value.elem()),
467 DType::F32 | DType::Flex32 => Self::full::<f32, _>(shape, fill_value.elem()),
468 DType::F16 => Self::full::<f16, _>(shape, fill_value.elem()),
469 DType::BF16 => Self::full::<bf16, _>(shape, fill_value.elem()),
470 DType::I64 => Self::full::<i64, _>(shape, fill_value.elem()),
471 DType::I32 => Self::full::<i32, _>(shape, fill_value.elem()),
472 DType::I16 => Self::full::<i16, _>(shape, fill_value.elem()),
473 DType::I8 => Self::full::<i8, _>(shape, fill_value.elem()),
474 DType::U64 => Self::full::<u64, _>(shape, fill_value.elem()),
475 DType::U32 => Self::full::<u32, _>(shape, fill_value.elem()),
476 DType::U16 => Self::full::<u16, _>(shape, fill_value.elem()),
477 DType::U8 => Self::full::<u8, _>(shape, fill_value.elem()),
478 DType::Bool(BoolStore::Native) => Self::full::<bool, _>(shape, fill_value.elem()),
479 DType::Bool(BoolStore::U8) => {
480 Self::full::<u8, _>(shape, fill_value.elem()).into_bool_u8()
481 }
482 DType::Bool(BoolStore::U32) => {
483 Self::full::<u32, _>(shape, fill_value.elem()).into_bool_u32()
484 }
485 DType::QFloat(_) => unreachable!(),
486 }
487 }
488
489 pub(super) fn into_bool_u8(mut self) -> Self {
491 self.dtype = DType::Bool(BoolStore::U8);
492 self
493 }
494
495 pub(super) fn into_bool_u32(mut self) -> Self {
497 self.dtype = DType::Bool(BoolStore::U32);
498 self
499 }
500
501 pub fn as_bytes(&self) -> &[u8] {
503 &self.bytes
504 }
505
506 pub fn into_bytes(self) -> Bytes {
508 self.bytes
509 }
510}
511
512struct LengthGuard<'a> {
515 data: Vec<&'a mut TensorData>,
516 lens: Vec<usize>,
517}
518
519impl<'a> LengthGuard<'a> {
520 fn new(data: Vec<&'a mut TensorData>) -> Self {
521 let lens = data.iter().map(|data| data.bytes.len()).collect();
522 Self { data, lens }
523 }
524
525 fn reset_changed(&mut self) -> bool {
527 let mut changed = false;
528 for (data, len) in self.data.iter_mut().zip(&self.lens) {
529 if data.bytes.len() != *len {
530 **data = TensorData::new(Vec::<u8>::new(), [0]);
531 changed = true;
532 }
533 }
534 changed
535 }
536}
537
538impl Drop for LengthGuard<'_> {
539 fn drop(&mut self) {
540 self.reset_changed();
541 }
542}
543
544fn checked_numel(shape: &[usize]) -> Option<usize> {
546 shape
547 .iter()
548 .try_fold(1usize, |numel, dim| numel.checked_mul(*dim))
549}
550
551fn numel(shape: &[usize]) -> usize {
556 match checked_numel(shape) {
557 Some(numel) => numel,
558 None => panic!("Shape {shape:?} has more elements than fit in usize"),
559 }
560}
561
562impl<E: Element, const A: usize> From<[E; A]> for TensorData {
563 fn from(elems: [E; A]) -> Self {
564 TensorData::new(elems.to_vec(), [A])
565 }
566}
567
568impl<const A: usize> From<[usize; A]> for TensorData {
569 fn from(elems: [usize; A]) -> Self {
570 TensorData::new(elems.iter().map(|&e| e as i64).collect(), [A])
571 }
572}
573
574impl From<&[usize]> for TensorData {
575 fn from(elems: &[usize]) -> Self {
576 let mut data = Vec::with_capacity(elems.len());
577 for elem in elems.iter() {
578 data.push(*elem as i64);
579 }
580
581 TensorData::new(data, [elems.len()])
582 }
583}
584
585impl<E: Element> From<&[E]> for TensorData {
586 fn from(elems: &[E]) -> Self {
587 let mut data = Vec::with_capacity(elems.len());
588 for elem in elems.iter() {
589 data.push(*elem);
590 }
591
592 TensorData::new(data, [elems.len()])
593 }
594}
595
596impl<E: Element, const A: usize, const B: usize> From<[[E; B]; A]> for TensorData {
597 fn from(elems: [[E; B]; A]) -> Self {
598 let mut data = Vec::with_capacity(A * B);
599 for elem in elems.into_iter().take(A) {
600 for elem in elem.into_iter().take(B) {
601 data.push(elem);
602 }
603 }
604
605 TensorData::new(data, [A, B])
606 }
607}
608
609impl<E: Element, const A: usize, const B: usize, const C: usize> From<[[[E; C]; B]; A]>
610 for TensorData
611{
612 fn from(elems: [[[E; C]; B]; A]) -> Self {
613 let mut data = Vec::with_capacity(A * B * C);
614
615 for elem in elems.into_iter().take(A) {
616 for elem in elem.into_iter().take(B) {
617 for elem in elem.into_iter().take(C) {
618 data.push(elem);
619 }
620 }
621 }
622
623 TensorData::new(data, [A, B, C])
624 }
625}
626
627impl<E: Element, const A: usize, const B: usize, const C: usize, const D: usize>
628 From<[[[[E; D]; C]; B]; A]> for TensorData
629{
630 fn from(elems: [[[[E; D]; C]; B]; A]) -> Self {
631 let mut data = Vec::with_capacity(A * B * C * D);
632
633 for elem in elems.into_iter().take(A) {
634 for elem in elem.into_iter().take(B) {
635 for elem in elem.into_iter().take(C) {
636 for elem in elem.into_iter().take(D) {
637 data.push(elem);
638 }
639 }
640 }
641 }
642
643 TensorData::new(data, [A, B, C, D])
644 }
645}
646
647impl<Elem: Element, const A: usize, const B: usize, const C: usize, const D: usize, const E: usize>
648 From<[[[[[Elem; E]; D]; C]; B]; A]> for TensorData
649{
650 fn from(elems: [[[[[Elem; E]; D]; C]; B]; A]) -> Self {
651 let mut data = Vec::with_capacity(A * B * C * D * E);
652
653 for elem in elems.into_iter().take(A) {
654 for elem in elem.into_iter().take(B) {
655 for elem in elem.into_iter().take(C) {
656 for elem in elem.into_iter().take(D) {
657 for elem in elem.into_iter().take(E) {
658 data.push(elem);
659 }
660 }
661 }
662 }
663 }
664
665 TensorData::new(data, [A, B, C, D, E])
666 }
667}
668impl core::fmt::Display for TensorData {
669 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
670 let fmt = match self.dtype {
671 DType::F64 => format!("{:?}", self.as_slice::<f64>().unwrap()),
672 DType::F32 | DType::Flex32 => format!("{:?}", self.as_slice::<f32>().unwrap()),
673 DType::F16 => format!("{:?}", self.as_slice::<f16>().unwrap()),
674 DType::BF16 => format!("{:?}", self.as_slice::<bf16>().unwrap()),
675 DType::I64 => format!("{:?}", self.as_slice::<i64>().unwrap()),
676 DType::I32 => format!("{:?}", self.as_slice::<i32>().unwrap()),
677 DType::I16 => format!("{:?}", self.as_slice::<i16>().unwrap()),
678 DType::I8 => format!("{:?}", self.as_slice::<i8>().unwrap()),
679 DType::U64 => format!("{:?}", self.as_slice::<u64>().unwrap()),
680 DType::U32 => format!("{:?}", self.as_slice::<u32>().unwrap()),
681 DType::U16 => format!("{:?}", self.as_slice::<u16>().unwrap()),
682 DType::U8 => format!("{:?}", self.as_slice::<u8>().unwrap()),
683 DType::Bool(BoolStore::Native) => format!("{:?}", self.as_slice::<bool>().unwrap()),
684 DType::Bool(BoolStore::U8) => format!("{:?}", self.as_slice::<u8>().unwrap()),
685 DType::Bool(BoolStore::U32) => format!("{:?}", self.as_slice::<u32>().unwrap()),
686 DType::QFloat(scheme) => match scheme {
687 QuantScheme {
688 mode: QuantMode::Symmetric,
689 value:
690 QuantValue::Q8F
691 | QuantValue::Q8S
692 | QuantValue::Q4F
694 | QuantValue::Q4S
695 | QuantValue::Q2F
696 | QuantValue::Q2S,
697 ..
698 } => {
699 format!("{:?} {scheme:?}", self.iter::<i8>().collect::<Vec<_>>())
700 },
701 QuantScheme {
702 mode: QuantMode::Symmetric,
703 value:
704 QuantValue::E4M3 | QuantValue::E5M2 | QuantValue::E2M1,
705 ..
706 } => {
707 format!("<float-quantized> {scheme:?}")
708 }
709 QuantScheme {
710 mode: QuantMode::Lookup,
711 ..
712 } => {
713 format!("<lookup-quantized> {scheme:?}")
714 }
715 },
716 };
717 f.write_str(fmt.as_str())
718 }
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724 use crate::*;
725 use ::rand::{
726 SeedableRng,
727 rngs::{StdRng, SysRng},
728 };
729 use alloc::string::ToString;
730 use alloc::vec;
731 use core::mem::{MaybeUninit, align_of, size_of};
732
733 #[test]
734 fn should_have_rank() {
735 let shape = [3, 5, 6];
736 let data = TensorData::random::<f32, _, _>(
737 shape,
738 Distribution::Default,
739 &mut StdRng::try_from_rng(&mut SysRng).unwrap(),
740 );
741
742 assert_eq!(data.rank(), 3);
743 }
744
745 #[test]
746 fn into_vec_should_yield_same_value_as_iter() {
747 let shape = [3, 5, 6];
748 let data = TensorData::random::<f32, _, _>(
749 shape,
750 Distribution::Default,
751 &mut StdRng::try_from_rng(&mut SysRng).unwrap(),
752 );
753
754 let expected = data.iter::<f32>().collect::<Vec<f32>>();
755 let actual = data.try_into_vec::<f32>().unwrap();
756
757 assert_eq!(expected, actual);
758 }
759
760 #[test]
761 #[should_panic]
762 fn into_vec_should_assert_wrong_dtype() {
763 let shape = [3, 5, 6];
764 let data = TensorData::random::<f32, _, _>(
765 shape,
766 Distribution::Default,
767 &mut StdRng::try_from_rng(&mut SysRng).unwrap(),
768 );
769
770 data.try_into_vec::<i32>().unwrap();
771 }
772
773 #[test]
774 fn should_have_right_num_elements() {
775 let shape = [3, 5, 6];
776 let num_elements: usize = shape.iter().product();
777 let data = TensorData::random::<f32, _, _>(
778 shape,
779 Distribution::Default,
780 &mut StdRng::try_from_rng(&mut SysRng).unwrap(),
781 );
782
783 assert_eq!(num_elements, data.bytes.len() / 4); assert_eq!(num_elements, data.as_slice::<f32>().unwrap().len());
785 }
786
787 #[test]
788 fn should_have_right_shape() {
789 let data = TensorData::from([[3.0, 5.0, 6.0]]);
790 assert_eq!(data.shape, shape![1, 3]);
791
792 let data = TensorData::from([[4.0, 5.0, 8.0], [3.0, 5.0, 6.0]]);
793 assert_eq!(data.shape, shape![2, 3]);
794
795 let data = TensorData::from([3.0, 5.0, 6.0]);
796 assert_eq!(data.shape, shape![3]);
797 }
798
799 #[test]
800 fn should_convert_bytes_correctly() {
801 let mut vector: Vec<f32> = Vec::with_capacity(5);
802 vector.push(2.0);
803 vector.push(3.0);
804 let data1 = TensorData::new(vector, vec![2]);
805
806 let factor = size_of::<f32>() / size_of::<u8>();
807 assert_eq!(data1.bytes.len(), 2 * factor);
808 assert_eq!(data1.bytes.capacity(), 5 * factor);
809 }
810
811 #[test]
812 fn should_convert_bytes_correctly_inplace() {
813 fn test_precision<E: Element>() {
814 let data = TensorData::new((0..32).collect(), [32]);
815 let self1 = data.clone().convert::<E>();
816 for (i, val) in self1.try_into_vec::<E>().unwrap().into_iter().enumerate() {
817 assert_eq!(i as u32, val.elem::<u32>())
818 }
819 }
820 test_precision::<f32>();
821 test_precision::<f16>();
822 test_precision::<i64>();
823 test_precision::<i32>();
824 }
825
826 #[test]
827 fn should_convert_negative_values_to_bool_store() {
828 for store in [BoolStore::U8, BoolStore::U32, BoolStore::Native] {
829 let data = TensorData::from([-1i32, 0, 1, -12]).convert_dtype(DType::Bool(store));
830 assert_eq!(data.dtype, DType::Bool(store));
831 assert_eq!(
832 data.iter::<bool>().collect::<Vec<_>>(),
833 [true, false, true, true]
834 );
835
836 let data = TensorData::from([-1.5f32, 0.0, 0.5]).convert_dtype(DType::Bool(store));
837 assert_eq!(data.iter::<bool>().collect::<Vec<_>>(), [true, false, true]);
838 }
839 }
840
841 macro_rules! test_dtypes {
842 ($test_name:ident, $($dtype:ty),*) => {
843 $(
844 paste::paste! {
845 #[test]
846 fn [<$test_name _ $dtype:snake>]() {
847 let full_dtype = TensorData::full_dtype([2, 16], 4, <$dtype>::dtype());
848 let full = TensorData::full::<$dtype, _>([2, 16], 4.elem());
849 assert_eq!(full_dtype, full);
850 }
851 }
852 )*
853 };
854}
855
856 test_dtypes!(
857 should_create_with_dtype,
858 bool,
859 i8,
860 i16,
861 i32,
862 i64,
863 u8,
864 u16,
865 u32,
866 u64,
867 f16,
868 bf16,
869 f32,
870 f64
871 );
872
873 #[test]
874 fn should_serialize_deserialize_tensor_data() {
875 let data = TensorData::new(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3]);
876 assert_eq!(
877 data.as_bytes(),
878 [
879 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64, 0, 0, 160, 64, 0, 0, 192,
880 64
881 ]
882 );
883 let serialized = serde_json::to_string(&data).unwrap();
884 let deserialized: TensorData = serde_json::from_str(&serialized).unwrap();
885 assert_eq!(data, deserialized);
886 }
887
888 #[test]
889 fn should_deserialize_tensor_data_with_shape_inner() {
890 let serialized = r#"{
892 "bytes": [0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 128, 64, 0, 0, 160, 64, 0, 0, 192, 64],
893 "shape": [2, 3],
894 "dtype": "F32"
895 }"#;
896
897 let data: TensorData = serde_json::from_str(serialized).unwrap();
898 assert_eq!(data.shape, shape![2, 3]);
899 assert_eq!(
900 data.as_slice::<f32>().unwrap(),
901 &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
902 );
903 }
904
905 #[test]
906 fn should_not_deserialize_tensor_data_with_shape_larger_than_bytes() {
907 let serialized = r#"{"bytes": [0, 0, 128, 63], "shape": [1000], "dtype": "F32"}"#;
910
911 let err = serde_json::from_str::<TensorData>(serialized).unwrap_err();
912 assert!(
913 err.to_string().contains("is invalid for input of size"),
914 "unexpected error: {err}"
915 );
916 }
917
918 #[test]
919 fn should_not_deserialize_tensor_data_with_overflowing_shape() {
920 let serialized = r#"{"bytes": [0, 0, 128, 63, 0, 0, 0, 64], "shape": [9223372036854775809, 2], "dtype": "F32"}"#;
923
924 let err = serde_json::from_str::<TensorData>(serialized).unwrap_err();
925 assert!(
926 err.to_string().contains("is invalid for input of size"),
927 "unexpected error: {err}"
928 );
929 }
930
931 #[test]
932 fn should_serialize_shape_as_flat_array() {
933 let data = TensorData::new(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3]);
936 let serialized = serde_json::to_string(&data).unwrap();
937 let json: serde_json::Value = serde_json::from_str(&serialized).unwrap();
938 assert_eq!(json["shape"], serde_json::json!([2, 3]));
939 }
940
941 #[test]
942 fn test_tensor_data_try_view_dtype_mismatch() {
943 let data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
944 let dtype = data.dtype;
945
946 assert_eq!(
947 data.try_view::<i32>().unwrap_err(),
948 DataError::DTypeMismatch {
949 expected: <i32 as Element>::dtype(),
950 actual: dtype,
951 }
952 );
953 }
954
955 #[test]
956 #[should_panic(expected = "Expected data type")]
957 fn test_tensor_data_expect_view_dtype_mismatch() {
958 let data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
959 let _view = data.view::<i32>();
960 }
961
962 #[test]
963 fn test_tensor_data_try_mut_view_dtype_mismatch() {
964 let mut data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
965
966 let result = data.try_mut_view::<i32>();
967 assert_eq!(
968 result.unwrap_err(),
969 DataError::DTypeMismatch {
970 actual: data.dtype,
971 expected: <i32 as Element>::dtype(),
972 }
973 );
974 }
975
976 #[test]
977 fn try_view_validates_storage() {
978 let invalid_representation =
979 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 3]), [1], DType::F32);
980 assert!(matches!(
981 invalid_representation.try_view::<f32>(),
982 Err(DataError::InvalidRepresentation(_))
983 ));
984
985 let invalid_count =
986 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 8]), [1], DType::F32);
987 assert!(matches!(
988 invalid_count.try_view::<f32>(),
989 Err(DataError::ElementCountMismatch {
990 expected: 1,
991 actual: 2
992 })
993 ));
994
995 let invalid_bool = TensorData::from_bytes_vec(vec![2], [1], DType::Bool(BoolStore::Native));
996 assert!(matches!(
997 invalid_bool.try_view::<bool>(),
998 Err(DataError::InvalidRepresentation(_))
999 ));
1000 }
1001
1002 #[test]
1003 fn try_from_bytes_rejects_invalid_length() {
1004 let err = TensorData::try_from_bytes_vec(vec![0; 3], [1], DType::F32).unwrap_err();
1005 assert_eq!(
1006 err,
1007 DataError::InvalidByteLength {
1008 shape: Shape::from([1]),
1009 dtype: DType::F32,
1010 len: 3,
1011 }
1012 );
1013
1014 assert!(TensorData::try_from_bytes_vec(vec![0; 8], [1], DType::F32).is_err());
1015 assert!(TensorData::try_from_bytes_vec(vec![], [1], DType::F32).is_err());
1016 assert!(TensorData::try_from_bytes_vec(vec![], [0usize; 0], DType::F32).is_err());
1017 assert!(
1019 TensorData::try_from_bytes_vec(vec![0; 8], [usize::MAX / 2 + 2, 2], DType::F32)
1020 .is_err()
1021 );
1022 }
1023
1024 #[test]
1025 #[should_panic(expected = "more elements than fit in usize")]
1026 fn new_rejects_overflowing_shape() {
1027 let _ = TensorData::new(vec![0.0f32; 2], [usize::MAX / 2 + 2, 2]);
1029 }
1030
1031 #[test]
1032 #[should_panic(expected = "more elements than fit in usize")]
1033 fn zeros_rejects_overflowing_shape() {
1034 let _ = TensorData::zeros::<f32, _>([usize::MAX / 2 + 2, 2]);
1035 }
1036
1037 #[test]
1038 fn try_from_bytes_accepts_valid_length() {
1039 let data = TensorData::try_from_bytes_vec(vec![0; 8], [2], DType::F32).unwrap();
1040 assert_eq!(data.shape(), &Shape::from([2]));
1041 assert_eq!(data.dtype(), DType::F32);
1042 assert_eq!(data.bytes().len(), 8);
1043
1044 let empty = TensorData::try_from_bytes_vec(vec![], [0, 3], DType::F32).unwrap();
1045 assert_eq!(empty.num_elements(), 0);
1046
1047 let scalar = TensorData::try_from_bytes_vec(vec![0; 4], [0usize; 0], DType::F32).unwrap();
1048 assert_eq!(scalar.rank(), 0);
1049 }
1050
1051 #[test]
1052 #[should_panic(expected = "is invalid for input of size 3 bytes")]
1053 fn from_bytes_panics_on_invalid_length() {
1054 let _ = TensorData::from_bytes_vec(vec![0; 3], [1], DType::F32);
1055 }
1056
1057 #[test]
1058 fn try_from_bytes_accepts_quantized_layout() {
1059 let data = TensorData::quantized(vec![0i8; 4], [4], QuantScheme::default(), &[1.0], None);
1062 let (bytes, shape, dtype) = data.clone().into_parts();
1063 assert_eq!(
1064 TensorData::try_from_bytes(bytes, shape, dtype).unwrap(),
1065 data
1066 );
1067
1068 let serialized = serde_json::to_string(&data).unwrap();
1069 let deserialized: TensorData = serde_json::from_str(&serialized).unwrap();
1070 assert_eq!(deserialized, data);
1071 }
1072
1073 #[test]
1074 fn with_bytes_mut_allows_length_preserving_writes() {
1075 let mut a = TensorData::from([1.0f32, 2.0]);
1076 let mut b = TensorData::from([3i32]);
1077
1078 TensorData::with_bytes_mut([&mut a, &mut b], |bytes| {
1079 assert_eq!(bytes.len(), 2);
1080 for bytes in bytes {
1081 bytes.fill(0);
1082 }
1083 });
1084
1085 assert_eq!(a, TensorData::from([0.0f32, 0.0]));
1086 assert_eq!(b, TensorData::from([0i32]));
1087 }
1088
1089 #[test]
1090 #[should_panic(expected = "length must not change")]
1091 fn with_bytes_mut_rejects_length_change() {
1092 let mut data = TensorData::from([1.0f32]);
1093 TensorData::with_bytes_mut([&mut data], |mut bytes| {
1094 bytes[0].extend_from_byte_slice(&[0; 4]);
1095 });
1096 }
1097
1098 #[cfg(feature = "std")]
1099 #[test]
1100 fn with_bytes_mut_leaves_no_invalid_data_after_unwind() {
1101 use std::panic::{AssertUnwindSafe, catch_unwind};
1102
1103 let empty = TensorData::new(Vec::<u8>::new(), [0]);
1104
1105 let mut changed = TensorData::from([1.0f32]);
1107 let mut unchanged = TensorData::from([2.0f32]);
1108 let result = catch_unwind(AssertUnwindSafe(|| {
1109 TensorData::with_bytes_mut([&mut changed, &mut unchanged], |mut bytes| {
1110 bytes[0].extend_from_byte_slice(&[0; 4]);
1111 })
1112 }));
1113 assert!(result.is_err());
1114 assert_eq!(changed, empty);
1115 assert_eq!(unchanged, TensorData::from([2.0f32]));
1116
1117 let mut data = TensorData::from([1.0f32]);
1119 let result = catch_unwind(AssertUnwindSafe(|| {
1120 TensorData::with_bytes_mut([&mut data], |mut bytes| {
1121 bytes[0].extend_from_byte_slice(&[0; 4]);
1122 panic!("callback failed");
1123 })
1124 }));
1125 assert!(result.is_err());
1126 assert_eq!(data, empty);
1127 }
1128
1129 #[test]
1130 fn try_view_propagates_storage_access_failure() {
1131 use alloc::boxed::Box;
1132
1133 #[derive(Debug)]
1134 struct FailingController;
1135
1136 impl AllocationController for FailingController {
1137 fn alloc_align(&self) -> usize {
1138 align_of::<f32>()
1139 }
1140
1141 fn property(&self) -> AllocationProperty {
1142 AllocationProperty::Other
1143 }
1144
1145 fn capacity(&self) -> usize {
1146 size_of::<f32>()
1147 }
1148
1149 fn memory(&self, _policy: AccessPolicy) -> Result<&[MaybeUninit<u8>], AccessError> {
1150 Err(AccessError::Read("test read failure".into()))
1151 }
1152
1153 unsafe fn memory_mut(
1154 &mut self,
1155 _policy: AccessPolicy,
1156 ) -> Result<&mut [MaybeUninit<u8>], AccessError> {
1157 Err(AccessError::Read("test write failure".into()))
1158 }
1159 }
1160
1161 let bytes =
1163 unsafe { Bytes::from_controller(Box::new(FailingController), size_of::<f32>()) };
1164 let data = TensorData::from_bytes(bytes, [1], DType::F32);
1165
1166 assert!(matches!(
1167 data.try_view::<f32>(),
1168 Err(DataError::StorageAccess(AccessError::Read(reason))) if reason == "test read failure"
1169 ));
1170 }
1171
1172 #[test]
1173 fn try_mut_view_validates_storage() {
1174 let mut invalid_representation =
1175 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 3]), [1], DType::F32);
1176 assert!(matches!(
1177 invalid_representation.try_mut_view::<f32>(),
1178 Err(DataError::InvalidRepresentation(_))
1179 ));
1180 }
1181
1182 #[test]
1183 fn try_cast_propagates_invalid_storage() {
1184 let invalid_inplace =
1185 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 3]), [1], DType::F32);
1186 assert!(matches!(
1187 invalid_inplace.try_cast(DType::I32),
1188 Err(DataError::InvalidRepresentation(_))
1189 ));
1190
1191 let invalid_clone =
1192 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 3]), [1], DType::F32);
1193 assert!(matches!(
1194 invalid_clone.try_cast(DType::F64),
1195 Err(DataError::InvalidRepresentation(_))
1196 ));
1197
1198 let invalid_count =
1199 TensorData::from_bytes_unchecked(Bytes::from_bytes_vec(vec![0; 8]), [1], DType::F32);
1200 assert!(matches!(
1201 invalid_count.try_cast(DType::F64),
1202 Err(DataError::ElementCountMismatch {
1203 expected: 1,
1204 actual: 2
1205 })
1206 ));
1207 }
1208
1209 #[test]
1210 fn try_cast_reports_unsupported_quantized_conversion() {
1211 let scheme = QuantScheme::default();
1212 let target = DType::QFloat(scheme);
1213
1214 assert_eq!(
1215 TensorData::from([1.0f32]).try_cast(target),
1216 Err(DataError::UnsupportedConversion {
1217 from: DType::F32,
1218 to: target,
1219 })
1220 );
1221
1222 let quantized = TensorData::quantized(vec![0i8], [1], scheme, &[1.0], None);
1223 assert_eq!(
1224 quantized.try_cast(DType::F32),
1225 Err(DataError::UnsupportedConversion {
1226 from: target,
1227 to: DType::F32,
1228 })
1229 );
1230 }
1231
1232 #[test]
1233 #[should_panic(expected = "Expected data type")]
1234 fn test_tensor_data_expect_mut_view_dtype_mismatch() {
1235 let mut data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
1236 let _view = data.mut_view::<i32>();
1237 }
1238
1239 #[test]
1240 fn test_tensor_data_index_view() {
1241 let data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
1242 let view = data.view::<f64>();
1243
1244 assert_eq!(view.shape(), &data.shape);
1245
1246 assert_eq!(view[&[0, 0]], 1.0);
1247 assert_eq!(view[&[0, 1]], 2.0);
1248 assert_eq!(view[&[1, 0]], 3.0);
1249 assert_eq!(view[&[1, 1]], 4.0);
1250 }
1251
1252 #[test]
1253 fn test_tensor_data_index_mut_view() {
1254 let mut data = TensorData::from([[1.0, 2.0], [3.0, 4.0]]);
1255 let shape = data.shape.clone();
1256
1257 let mut view = data.mut_view::<f64>();
1258
1259 assert_eq!(view.shape(), &shape);
1260
1261 assert_eq!(view[&[0, 0]], 1.0);
1262 assert_eq!(view[&[0, 1]], 2.0);
1263 assert_eq!(view[&[1, 0]], 3.0);
1264 assert_eq!(view[&[1, 1]], 4.0);
1265
1266 view[&[0, 0]] = 10.0;
1267 assert_eq!(view[&[0, 0]], 10.0);
1268 }
1269
1270 #[test]
1271 fn test_to_vec_as() {
1272 let data = TensorData::from([0.0f32, 1.0, 2.5]);
1273
1274 assert_eq!(data.try_to_vec_as::<f32>().unwrap(), vec![0.0f32, 1.0, 2.5]);
1276
1277 assert_eq!(data.try_to_vec_as::<f64>().unwrap(), vec![0.0f64, 1.0, 2.5]);
1279
1280 assert_eq!(data.try_to_vec_as::<i32>().unwrap(), vec![0i32, 1, 2]);
1282
1283 data.assert_eq(&TensorData::from([0.0f32, 1.0, 2.5]), true);
1285 }
1286
1287 #[test]
1288 fn test_into_vec_as() {
1289 let data = TensorData::from([0i32, 1, 2, 3]);
1290
1291 assert_eq!(
1293 data.clone().try_into_vec_as::<i32>().unwrap(),
1294 vec![0i32, 1, 2, 3]
1295 );
1296
1297 assert_eq!(
1299 data.clone().try_into_vec_as::<f32>().unwrap(),
1300 vec![0.0f32, 1.0, 2.0, 3.0]
1301 );
1302
1303 assert_eq!(data.try_into_vec_as::<u8>().unwrap(), vec![0u8, 1, 2, 3]);
1305 }
1306}