1use crate::descriptor::cursor::{ByteCursor, ByteWriter};
22use crate::layout::{
23 BlockPagedLayout, BlockTableIndexType, CompositeLayout, CompositionRule, CooLayout, CscLayout,
24 CsfLayout, CsrLayout, HilbertLayout, InnerStrides, KvRole, LayoutDescriptor, MortonLayout,
25 OuterStrides, PrivateExtensionLayout, StridedLayout, TiledLayout, MAX_TILED_DEPTH,
26 TAG_BLOCK_PAGED, TAG_COL_MAJOR, TAG_COMPOSITE, TAG_COO, TAG_CSC, TAG_CSF, TAG_CSR, TAG_HILBERT,
27 TAG_MORTON, TAG_ROW_MAJOR, TAG_STRIDED, TAG_TILED,
28};
29use crate::{Error, Result};
30
31pub(crate) fn encode_layout_payload(
38 layout: &LayoutDescriptor,
39 rank: u32,
40 w: &mut ByteWriter,
41) -> Result<()> {
42 encode_layout_payload_at_depth(layout, rank, w, 0)
45}
46
47fn encode_layout_payload_at_depth(
51 layout: &LayoutDescriptor,
52 rank: u32,
53 w: &mut ByteWriter,
54 depth: u8,
55) -> Result<()> {
56 match layout {
57 LayoutDescriptor::RowMajor | LayoutDescriptor::ColMajor => {
58 Ok(())
60 }
61 LayoutDescriptor::Strided(s) => encode_strided(s, w),
62 LayoutDescriptor::Tiled(t) => encode_tiled(t, rank, w, depth),
63 LayoutDescriptor::Morton(m) => encode_morton(m, w),
64 LayoutDescriptor::Coo(c) => encode_coo(c, w),
65 LayoutDescriptor::Csr(c) => encode_csr(c, w),
66 LayoutDescriptor::Csc(c) => encode_csc(c, w),
67 LayoutDescriptor::Csf(c) => encode_csf(c, rank, w),
68 LayoutDescriptor::BlockPaged(bp) => encode_block_paged(bp, w),
69 LayoutDescriptor::Composite(c) => encode_composite(c, w),
70 LayoutDescriptor::Hilbert(h) => encode_hilbert(h, w),
71 LayoutDescriptor::PrivateExtension(p) => encode_private_extension(p, w),
72 LayoutDescriptor::Unknown(_) => {
73 Err(Error::UnknownLayoutTag(layout.tag()))
75 }
76 }
77}
78
79pub(crate) fn decode_layout_payload(
84 tag: u8,
85 rank: u32,
86 cursor: &mut ByteCursor<'_>,
87 depth: u8,
88) -> Result<LayoutDescriptor> {
89 if depth >= MAX_TILED_DEPTH as u8 {
90 return Err(Error::SubpavingNestingTooDeep);
91 }
92 match tag {
93 TAG_ROW_MAJOR => Ok(LayoutDescriptor::RowMajor),
94 TAG_COL_MAJOR => Ok(LayoutDescriptor::ColMajor),
95 TAG_STRIDED => decode_strided(cursor, rank),
96 TAG_TILED => Ok(LayoutDescriptor::Tiled(Box::new(decode_tiled(
97 cursor, rank, depth,
98 )?))),
99 TAG_MORTON => decode_morton(cursor, rank),
100 TAG_COO => decode_coo(cursor),
101 TAG_CSR => decode_csr(cursor),
102 TAG_CSC => decode_csc(cursor),
103 TAG_CSF => decode_csf(cursor, rank),
104 TAG_BLOCK_PAGED => decode_block_paged(cursor),
105 TAG_COMPOSITE => decode_composite(cursor),
106 TAG_HILBERT => decode_hilbert(cursor),
107 0xF0..=0xFE => decode_private_extension(tag, cursor),
108 0x00 | 0xFF => Err(Error::InvalidLayoutTag(tag)),
109 t if crate::layout::is_reserved_tag(t) => Err(Error::ReservedLayoutTag(tag)),
110 _ => Err(Error::UnknownLayoutTag(tag)),
111 }
112}
113
114fn encode_strided(layout: &StridedLayout, w: &mut ByteWriter) -> Result<()> {
117 for &stride in &layout.strides {
118 w.write_i64_le(stride);
119 }
120 Ok(())
121}
122
123fn decode_strided(cursor: &mut ByteCursor<'_>, rank: u32) -> Result<LayoutDescriptor> {
124 let mut strides = Vec::with_capacity(rank as usize);
125 for _ in 0..rank {
126 strides.push(cursor.read_i64_le()?);
127 }
128 Ok(LayoutDescriptor::Strided(StridedLayout::new(strides)))
129}
130
131#[allow(clippy::only_used_in_recursion)]
135fn encode_tiled(layout: &TiledLayout, rank: u32, w: &mut ByteWriter, depth: u8) -> Result<()> {
136 if depth >= MAX_TILED_DEPTH as u8 {
137 return Err(Error::SubpavingNestingTooDeep);
138 }
139 for &dim in &layout.tile_shape {
141 w.write_u64_le(dim);
142 }
143 w.write_u8(layout.outer_layout);
144 w.write_u8(layout.inner_layout);
145 w.write_zeros(2); if layout.outer_layout == TAG_STRIDED {
151 if let Some(os) = &layout.outer_strides {
152 for &s in &os.strides {
153 w.write_i64_le(s);
154 }
155 }
156 }
157
158 if layout.inner_layout == TAG_STRIDED {
160 if let Some(is) = &layout.inner_strides {
161 for &s in &is.strides {
162 w.write_i64_le(s);
163 }
164 }
165 }
166
167 if layout.inner_layout == TAG_TILED {
169 if let Some(inner) = &layout.inner_tiled {
170 encode_tiled(inner, rank, w, depth + 1)?;
171 }
172 }
173
174 Ok(())
175}
176
177fn decode_tiled(cursor: &mut ByteCursor<'_>, rank: u32, depth: u8) -> Result<TiledLayout> {
180 if depth >= MAX_TILED_DEPTH as u8 {
181 return Err(Error::SubpavingNestingTooDeep);
182 }
183
184 let mut tile_shape = Vec::with_capacity(rank as usize);
186 for _ in 0..rank {
187 tile_shape.push(cursor.read_u64_le()?);
188 }
189
190 let outer_layout = cursor.read_u8()?;
191 let inner_layout = cursor.read_u8()?;
192 let reserved = cursor.read_bytes(2)?;
193 if reserved != [0u8, 0] {
194 return Err(Error::ReservedBytesNonZero {
195 field: "tiled._reserved",
196 });
197 }
198
199 let outer_strides = if outer_layout == TAG_STRIDED {
201 let mut strides = Vec::with_capacity(rank as usize);
202 for _ in 0..rank {
203 strides.push(cursor.read_i64_le()?);
204 }
205 Some(OuterStrides::new(strides))
206 } else {
207 None
208 };
209
210 let inner_strides = if inner_layout == TAG_STRIDED {
212 let mut strides = Vec::with_capacity(rank as usize);
213 for _ in 0..rank {
214 strides.push(cursor.read_i64_le()?);
215 }
216 Some(InnerStrides::new(strides))
217 } else {
218 None
219 };
220
221 let inner_tiled: Option<Box<TiledLayout>> = if inner_layout == TAG_TILED {
223 Some(Box::new(decode_tiled(cursor, rank, depth + 1)?))
224 } else {
225 None
226 };
227
228 TiledLayout::new(
229 tile_shape,
230 outer_layout,
231 inner_layout,
232 outer_strides,
233 inner_strides,
234 inner_tiled,
235 )
236}
237
238fn encode_morton(layout: &MortonLayout, w: &mut ByteWriter) -> Result<()> {
241 for &bits in &layout.morton_bits {
242 w.write_u32_le(bits);
243 }
244 Ok(())
245}
246
247fn decode_morton(cursor: &mut ByteCursor<'_>, rank: u32) -> Result<LayoutDescriptor> {
248 let mut bits = Vec::with_capacity(rank as usize);
249 for _ in 0..rank {
250 bits.push(cursor.read_u32_le()?);
251 }
252 let layout = MortonLayout::new(bits)?;
253 Ok(LayoutDescriptor::Morton(layout))
254}
255
256fn encode_coo(layout: &CooLayout, w: &mut ByteWriter) -> Result<()> {
259 w.write_u64_le(layout.nnz);
260 w.write_u8(u8::from(layout.is_sorted));
261 w.write_zeros(7); Ok(())
263}
264
265fn decode_coo(cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
266 let nnz = cursor.read_u64_le()?;
267 let is_sorted = cursor.read_u8()? != 0;
268 let reserved = cursor.read_bytes(7)?;
269 if reserved.iter().any(|&b| b != 0) {
270 return Err(Error::ReservedBytesNonZero {
271 field: "coo._reserved",
272 });
273 }
274 Ok(LayoutDescriptor::Coo(CooLayout::new(nnz, is_sorted)))
275}
276
277fn encode_csr(layout: &CsrLayout, w: &mut ByteWriter) -> Result<()> {
280 w.write_u64_le(layout.nnz);
281 w.write_zeros(8); Ok(())
283}
284
285fn decode_csr(cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
286 let nnz = cursor.read_u64_le()?;
287 let reserved = cursor.read_bytes(8)?;
288 if reserved.iter().any(|&b| b != 0) {
289 return Err(Error::ReservedBytesNonZero {
290 field: "csr._reserved",
291 });
292 }
293 Ok(LayoutDescriptor::Csr(CsrLayout::new(nnz)))
294}
295
296fn encode_csc(layout: &CscLayout, w: &mut ByteWriter) -> Result<()> {
299 w.write_u64_le(layout.nnz);
300 w.write_zeros(8); Ok(())
302}
303
304fn decode_csc(cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
305 let nnz = cursor.read_u64_le()?;
306 let reserved = cursor.read_bytes(8)?;
307 if reserved.iter().any(|&b| b != 0) {
308 return Err(Error::ReservedBytesNonZero {
309 field: "csc._reserved",
310 });
311 }
312 Ok(LayoutDescriptor::Csc(CscLayout::new(nnz)))
313}
314
315fn encode_csf(layout: &CsfLayout, rank: u32, w: &mut ByteWriter) -> Result<()> {
326 w.write_u64_le(layout.nnz);
327 let mo_len = layout.mode_order.len() as u32;
331 if mo_len != rank {
332 return Err(Error::InvalidLayout(format!(
333 "csf encode: mode_order.len() ({mo_len}) != rank ({rank}); \
334 call validate_against_shape before encoding"
335 )));
336 }
337 for &dim in &layout.mode_order {
338 w.write_u32_le(dim);
339 }
340 w.write_zeros(8); Ok(())
342}
343
344fn decode_csf(cursor: &mut ByteCursor<'_>, rank: u32) -> Result<LayoutDescriptor> {
345 let nnz = cursor.read_u64_le()?;
346
347 let mut mode_order = Vec::with_capacity(rank as usize);
349 for _ in 0..rank {
350 mode_order.push(cursor.read_u32_le()?);
351 }
352
353 let reserved = cursor.read_bytes(8)?;
355 if reserved.iter().any(|&b| b != 0) {
356 return Err(Error::ReservedBytesNonZero {
357 field: "csf._reserved",
358 });
359 }
360
361 let rank_us = rank as usize;
366 let mut seen = 0u64;
367 for (level, &dim) in mode_order.iter().enumerate() {
368 if dim as usize >= rank_us {
369 return Err(Error::InvalidLayout(format!(
370 "csf: mode_order[{level}]={dim} out of range [0, {rank})"
371 )));
372 }
373 let bit = 1u64 << dim;
374 if seen & bit != 0 {
375 return Err(Error::InvalidLayout(format!(
376 "csf: mode_order contains duplicate value {dim}"
377 )));
378 }
379 seen |= bit;
380 }
381
382 Ok(LayoutDescriptor::Csf(CsfLayout::new(nnz, mode_order)))
383}
384
385fn encode_block_paged(layout: &BlockPagedLayout, w: &mut ByteWriter) -> crate::Result<()> {
401 w.write_u32_le(layout.page_size);
402 w.write_u64_le(layout.num_pages);
403 w.write_u32_le(layout.paged_axis);
404 w.write_u32_le(layout.num_seqs);
405 w.write_u8(layout.kv_role.wire_byte());
406 let layer_index_wire = layout
408 .layer_index
409 .unwrap_or(crate::layout::block_paged::LAYER_INDEX_NONE);
410 w.write_u32_le(layer_index_wire);
411 w.write_u8(layout.block_table_index_type.wire_byte());
412 w.write_zeros(6); Ok(())
414}
415
416fn decode_block_paged(cursor: &mut ByteCursor<'_>) -> crate::Result<LayoutDescriptor> {
417 let page_size = cursor.read_u32_le()?;
418 let num_pages = cursor.read_u64_le()?;
419 let paged_axis = cursor.read_u32_le()?;
420 let num_seqs = cursor.read_u32_le()?;
421
422 let kv_role_byte = cursor.read_u8()?;
423 let kv_role = KvRole::from_wire(kv_role_byte).ok_or_else(|| {
424 crate::Error::InvalidLayout(format!(
425 "block-paged: unknown kv_role byte 0x{kv_role_byte:02X}"
426 ))
427 })?;
428
429 let layer_index_wire = cursor.read_u32_le()?;
430 let layer_index = if layer_index_wire == crate::layout::block_paged::LAYER_INDEX_NONE {
431 None
432 } else {
433 Some(layer_index_wire)
434 };
435
436 let index_type_byte = cursor.read_u8()?;
437 let block_table_index_type =
438 BlockTableIndexType::from_wire(index_type_byte).ok_or_else(|| {
439 crate::Error::InvalidLayout(format!(
440 "block-paged: unknown block_table_index_type byte 0x{index_type_byte:02X}"
441 ))
442 })?;
443
444 let reserved = cursor.read_bytes(6)?;
446 if reserved.iter().any(|&b| b != 0) {
447 return Err(crate::Error::ReservedBytesNonZero {
448 field: "block_paged._reserved",
449 });
450 }
451
452 Ok(LayoutDescriptor::BlockPaged(BlockPagedLayout::new(
461 page_size,
462 num_pages,
463 paged_axis,
464 num_seqs,
465 kv_role,
466 layer_index,
467 block_table_index_type,
468 )))
469}
470
471fn encode_composite(layout: &CompositeLayout, w: &mut ByteWriter) -> Result<()> {
484 w.write_u8(layout.rule.rule_byte());
485 w.write_u8(layout.rule.combine_op_byte());
486 w.write_zeros(2); w.write_u32_le(layout.member_count);
488 Ok(())
489}
490
491fn decode_composite(cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
492 let rule_byte = cursor.read_u8()?;
493 let combine_op_byte = cursor.read_u8()?;
494
495 let reserved = cursor.read_bytes(2)?;
497 if reserved.iter().any(|&b| b != 0) {
498 return Err(Error::ReservedBytesNonZero {
499 field: "composite._reserved",
500 });
501 }
502
503 let member_count = cursor.read_u32_le()?;
504
505 let rule = CompositionRule::from_wire(rule_byte, combine_op_byte)?;
506 let layout = CompositeLayout::new(rule, member_count)?;
507 Ok(LayoutDescriptor::Composite(layout))
508}
509
510fn encode_hilbert(layout: &HilbertLayout, w: &mut ByteWriter) -> Result<()> {
513 w.write_u32_le(layout.hilbert_order);
514 w.write_u32_le(layout.hilbert_rank);
515 Ok(())
516}
517
518fn decode_hilbert(cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
519 let hilbert_order = cursor.read_u32_le()?;
520 let hilbert_rank = cursor.read_u32_le()?;
521 let layout = HilbertLayout::new(hilbert_order, hilbert_rank)?;
522 Ok(LayoutDescriptor::Hilbert(layout))
523}
524
525fn encode_private_extension(layout: &PrivateExtensionLayout, w: &mut ByteWriter) -> Result<()> {
528 w.write_u64_le(layout.extension_layout_id);
529 w.write_u32_le(layout.extension_data.len() as u32);
530 w.write_bytes(&layout.extension_data);
531 Ok(())
532}
533
534fn decode_private_extension(tag: u8, cursor: &mut ByteCursor<'_>) -> Result<LayoutDescriptor> {
535 let extension_layout_id = cursor.read_u64_le()?;
536 let extension_data_length = cursor.read_u32_le()?;
537 let data = cursor.read_bytes(extension_data_length as usize)?.to_vec();
538 let layout = PrivateExtensionLayout::new(tag, extension_layout_id, data)?;
539 Ok(LayoutDescriptor::PrivateExtension(layout))
540}
541
542#[cfg(test)]
545mod tests {
546 use super::*;
547 use crate::descriptor::cursor::{ByteCursor, ByteWriter};
548
549 fn round_trip(layout: &LayoutDescriptor, rank: u32) -> LayoutDescriptor {
550 let mut w = ByteWriter::new();
551 encode_layout_payload(layout, rank, &mut w).unwrap();
552 let bytes = w.into_vec();
553 let mut c = ByteCursor::new(&bytes, bytes.len());
554 decode_layout_payload(layout.tag(), rank, &mut c, 0).unwrap()
555 }
556
557 #[test]
558 fn row_major_round_trip() {
559 let layout = LayoutDescriptor::RowMajor;
560 assert_eq!(round_trip(&layout, 2), layout);
561 }
562
563 #[test]
564 fn col_major_round_trip() {
565 let layout = LayoutDescriptor::ColMajor;
566 assert_eq!(round_trip(&layout, 2), layout);
567 }
568
569 #[test]
570 fn strided_round_trip() {
571 let layout = LayoutDescriptor::Strided(StridedLayout::new(vec![4, 1]));
572 assert_eq!(round_trip(&layout, 2), layout);
573 }
574
575 #[test]
576 fn morton_round_trip() {
577 let layout = LayoutDescriptor::Morton(MortonLayout::new(vec![4, 4]).unwrap());
578 assert_eq!(round_trip(&layout, 2), layout);
579 }
580
581 #[test]
582 fn coo_round_trip() {
583 let layout = LayoutDescriptor::Coo(CooLayout::new(42, true));
584 assert_eq!(round_trip(&layout, 2), layout);
585 }
586
587 #[test]
588 fn csr_round_trip() {
589 let layout = LayoutDescriptor::Csr(CsrLayout::new(100));
590 assert_eq!(round_trip(&layout, 2), layout);
591 }
592
593 #[test]
594 fn csc_round_trip() {
595 let layout = LayoutDescriptor::Csc(CscLayout::new(50));
596 assert_eq!(round_trip(&layout, 2), layout);
597 }
598
599 #[test]
600 fn hilbert_round_trip() {
601 let layout = LayoutDescriptor::Hilbert(HilbertLayout::new(3, 2).unwrap());
602 assert_eq!(round_trip(&layout, 2), layout);
603 }
604
605 #[test]
606 fn private_extension_round_trip() {
607 let layout = LayoutDescriptor::PrivateExtension(
608 PrivateExtensionLayout::new(0xF0, 0xDEAD_BEEF, vec![1, 2, 3]).unwrap(),
609 );
610 assert_eq!(round_trip(&layout, 0), layout);
611 }
612
613 #[test]
614 fn tiled_row_row_round_trip() {
615 let t = TiledLayout::new(vec![4, 4], 0x01, 0x01, None, None, None).unwrap();
616 let layout = LayoutDescriptor::Tiled(Box::new(t));
617 assert_eq!(round_trip(&layout, 2), layout);
618 }
619
620 #[test]
621 fn tiled_strided_outer_round_trip() {
622 let os = OuterStrides::new(vec![2, 1]);
623 let t = TiledLayout::new(vec![8, 8], 0x03, 0x01, Some(os), None, None).unwrap();
624 let layout = LayoutDescriptor::Tiled(Box::new(t));
625 assert_eq!(round_trip(&layout, 2), layout);
626 }
627
628 #[test]
629 fn tiled_nested_round_trip() {
630 let inner = TiledLayout::new(vec![2, 2], 0x01, 0x02, None, None, None).unwrap();
631 let outer =
632 TiledLayout::new(vec![4, 4], 0x01, 0x04, None, None, Some(Box::new(inner))).unwrap();
633 let layout = LayoutDescriptor::Tiled(Box::new(outer));
634 assert_eq!(round_trip(&layout, 2), layout);
635 }
636
637 #[test]
640 fn composite_partition_round_trip() {
641 let layout = LayoutDescriptor::Composite(
642 CompositeLayout::new(CompositionRule::Partition, 3).unwrap(),
643 );
644 assert_eq!(round_trip(&layout, 0), layout);
645 }
646
647 #[test]
648 fn composite_group_round_trip() {
649 let layout =
650 LayoutDescriptor::Composite(CompositeLayout::new(CompositionRule::Group, 0).unwrap());
651 assert_eq!(round_trip(&layout, 0), layout);
652 }
653
654 #[test]
655 fn composite_overlay_replace_round_trip() {
656 use crate::layout::CombineOp;
657 let layout = LayoutDescriptor::Composite(
658 CompositeLayout::new(CompositionRule::Overlay(CombineOp::Replace), 2).unwrap(),
659 );
660 assert_eq!(round_trip(&layout, 0), layout);
661 }
662
663 #[test]
664 fn composite_overlay_add_round_trip() {
665 use crate::layout::CombineOp;
666 let layout = LayoutDescriptor::Composite(
667 CompositeLayout::new(CompositionRule::Overlay(CombineOp::Add), 5).unwrap(),
668 );
669 assert_eq!(round_trip(&layout, 0), layout);
670 }
671
672 #[test]
674 fn composite_reserved_bytes_rejected() {
675 let mut w = ByteWriter::new();
676 w.write_u8(0x01); w.write_u8(0x00); w.write_u8(0xFF); w.write_u8(0x00);
680 w.write_u32_le(0); let bytes = w.into_vec();
682 let mut c = ByteCursor::new(&bytes, bytes.len());
683 let err = decode_layout_payload(TAG_COMPOSITE, 0, &mut c, 0).unwrap_err();
684 assert!(matches!(err, Error::ReservedBytesNonZero { .. }));
685 }
686
687 #[test]
691 fn composite_open_sentinel_rejected_at_codec_layer() {
692 let mut w = ByteWriter::new();
693 w.write_u8(0x01); w.write_u8(0x00); w.write_zeros(2); w.write_u32_le(0xFFFF_FFFF); let bytes = w.into_vec();
698 let mut c = ByteCursor::new(&bytes, bytes.len());
699 let err = decode_layout_payload(TAG_COMPOSITE, 0, &mut c, 0).unwrap_err();
700 assert!(matches!(err, Error::OpenCompositeReserved));
701 }
702
703 #[test]
704 fn coo_reserved_bytes_rejected() {
705 let mut w = ByteWriter::new();
706 w.write_u64_le(0u64); w.write_u8(0u8); w.write_u8(0xFFu8); w.write_zeros(6);
710 let bytes = w.into_vec();
711 let mut c = ByteCursor::new(&bytes, bytes.len());
712 let err = decode_layout_payload(TAG_COO, 0, &mut c, 0).unwrap_err();
713 assert!(matches!(err, Error::ReservedBytesNonZero { .. }));
714 }
715
716 #[test]
717 fn invalid_tag_rejected() {
718 let bytes: &[u8] = &[];
719 let mut c = ByteCursor::new(bytes, 0);
720 let err = decode_layout_payload(0x00, 0, &mut c, 0).unwrap_err();
721 assert!(matches!(err, Error::InvalidLayoutTag(0x00)));
722 }
723
724 #[test]
725 fn reserved_tag_rejected() {
726 let bytes: &[u8] = &[];
728 let mut c = ByteCursor::new(bytes, 0);
729 let err = decode_layout_payload(0x0C, 0, &mut c, 0).unwrap_err();
730 assert!(matches!(err, Error::ReservedLayoutTag(0x0C)));
731 }
732
733 mod unknown_encode {
736 use crate::descriptor::TensorDescriptor;
737 use crate::layout::{LayoutDescriptor, UnknownLayout};
738 use crate::{
739 BufferHandle, DeviceTag, ElementType, Error, Shape, SyncMode, MIN_BUFFER_ALIGNMENT,
740 };
741
742 #[test]
749 fn encode_unknown_layout_returns_error() {
750 let unknown_layout =
752 LayoutDescriptor::Unknown(UnknownLayout::new(0x0C, vec![0x01, 0x02]).unwrap());
753 let shape = Shape::new(vec![4u64]).unwrap();
754 let buf = BufferHandle::new(
755 64,
756 MIN_BUFFER_ALIGNMENT,
757 DeviceTag::Cpu,
758 SyncMode::ProducerSynced,
759 )
760 .unwrap();
761 let desc = TensorDescriptor::new(
763 1,
764 0,
765 ElementType::Float32,
766 shape,
767 0,
768 unknown_layout,
769 vec![buf],
770 None,
771 None,
772 None,
773 None,
774 )
775 .unwrap();
776 let err = desc.encode().unwrap_err();
777 assert!(
778 matches!(err, Error::UnknownLayoutTag(_)),
779 "expected UnknownLayoutTag, got {err:?}"
780 );
781 }
782 }
783}