1use crate::format::TensorEntry;
13use crate::types::TensorDtype;
14
15pub const GROUP_SIZE: usize = 32;
16
17#[inline]
19pub fn f16_to_f32(h: u16) -> f32 {
20 let sign = (h >> 15) as u32;
21 let exp = ((h >> 10) & 0x1F) as u32;
22 let mant = (h & 0x3FF) as u32;
23 let bits = if exp == 0 {
24 if mant == 0 {
25 sign << 31
26 } else {
27 let mut e = 0u32;
33 let mut m = mant;
34 while m & 0x400 == 0 {
35 m <<= 1;
36 e += 1;
37 }
38 m &= 0x3FF;
39 (sign << 31) | ((113 - e) << 23) | (m << 13)
40 }
41 } else if exp == 0x1F {
42 (sign << 31) | (0xFF << 23) | (mant << 13)
43 } else {
44 (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13)
45 };
46 f32::from_bits(bits)
47}
48
49#[inline]
51pub fn f32_to_f16(x: f32) -> u16 {
52 let bits = x.to_bits();
53 let sign = ((bits >> 16) & 0x8000) as u16;
54 let mut exp = ((bits >> 23) & 0xFF) as i32;
55 let mant = bits & 0x7F_FFFF;
56
57 if exp == 0xFF {
58 return sign | 0x7C00 | if mant != 0 { 0x200 } else { 0 };
60 }
61 exp -= 127 - 15;
62 if exp >= 0x1F {
63 return sign | 0x7C00; }
65 if exp <= 0 {
66 if exp < -10 {
67 return sign; }
69 let m = mant | 0x80_0000;
71 let shift = (14 - exp) as u32;
72 let half = m >> shift;
73 let round = (m >> (shift - 1)) & 1;
74 return sign | ((half + round) as u16);
75 }
76 let half = ((exp as u32) << 10) | (mant >> 13);
77 let round = (mant >> 12) & 1;
78 let sticky = (mant & 0xFFF) != 0;
80 let bump = round & (sticky as u32 | (half & 1));
81 sign | ((half + bump) as u16)
82}
83
84#[inline]
86pub fn bf16_to_f32(h: u16) -> f32 {
87 f32::from_bits((h as u32) << 16)
88}
89
90pub fn dequant_q8_row(bytes: &[u8], out_dim: usize, in_dim: usize, dst: &mut [f32]) {
92 debug_assert_eq!(bytes.len(), out_dim * in_dim + out_dim * 2);
93 debug_assert_eq!(dst.len(), out_dim * in_dim);
94 let (q, scales) = bytes.split_at(out_dim * in_dim);
95 for o in 0..out_dim {
96 let s = f16_to_f32(u16::from_le_bytes([scales[o * 2], scales[o * 2 + 1]]));
97 let row = &q[o * in_dim..(o + 1) * in_dim];
98 let out = &mut dst[o * in_dim..(o + 1) * in_dim];
99 for (d, &b) in out.iter_mut().zip(row) {
100 *d = (b as i8) as f32 * s;
101 }
102 }
103}
104
105pub fn dequant_q8_2f(bytes: &[u8], out_dim: usize, in_dim: usize, dst: &mut [f32]) {
111 debug_assert_eq!(bytes.len(), out_dim * in_dim + out_dim * 2 + in_dim * 2);
112 debug_assert_eq!(dst.len(), out_dim * in_dim);
113 let (q, rest) = bytes.split_at(out_dim * in_dim);
114 let (scales, cols) = rest.split_at(out_dim * 2);
115 let col: Vec<f32> = (0..in_dim)
116 .map(|i| f16_to_f32(u16::from_le_bytes([cols[i * 2], cols[i * 2 + 1]])))
117 .collect();
118 for o in 0..out_dim {
119 let s = f16_to_f32(u16::from_le_bytes([scales[o * 2], scales[o * 2 + 1]]));
120 let row = &q[o * in_dim..(o + 1) * in_dim];
121 let out = &mut dst[o * in_dim..(o + 1) * in_dim];
122 for i in 0..in_dim {
123 out[i] = (row[i] as i8) as f32 * s * col[i];
124 }
125 }
126}
127
128pub fn dequant_q4_block(bytes: &[u8], dst: &mut [f32]) {
131 let n_groups = dst.len().div_ceil(GROUP_SIZE);
132 let packed_len = n_groups * GROUP_SIZE / 2;
133 debug_assert_eq!(bytes.len(), packed_len + n_groups * 2);
134 let (packed, scales) = bytes.split_at(packed_len);
135 for g in 0..n_groups {
136 let s = f16_to_f32(u16::from_le_bytes([scales[g * 2], scales[g * 2 + 1]]));
137 let base = g * GROUP_SIZE;
138 let pk = &packed[g * 16..(g + 1) * 16];
139 for (k, &byte) in pk.iter().enumerate() {
140 let i0 = base + k * 2;
141 let i1 = i0 + 1;
142 if i0 < dst.len() {
143 dst[i0] = ((byte & 0x0F) as f32 - 8.0) * s;
144 }
145 if i1 < dst.len() {
146 dst[i1] = (((byte >> 4) & 0x0F) as f32 - 8.0) * s;
147 }
148 }
149 }
150}
151
152pub fn dequant_vbit(bytes: &[u8], rows: usize, cols: usize, dst: &mut [f32]) -> Result<(), String> {
156 if cols % GROUP_SIZE != 0 {
157 return Err(format!("vbit: cols {cols} not a multiple of {GROUP_SIZE}"));
158 }
159 let ng = cols / GROUP_SIZE;
160 let bits = &bytes[..rows];
161 if let Some(&b) = bits.iter().find(|&&b| !(3..=8).contains(&b)) {
162 return Err(format!("vbit: bit-width {b} outside safe range 3..=8"));
163 }
164 let sc_off = rows;
165 let data_off = sc_off + rows * ng * 2;
166 let mut off = data_off;
167 for r in 0..rows {
168 let b = bits[r] as usize;
169 let l = ((1usize << (b - 1)) - 1) as f32;
170 let rowlen = (cols * b).div_ceil(8);
171 let data = &bytes[off..off + rowlen];
172 let (mut acc, mut nbits, mut idx) = (0u64, 0usize, 0usize);
173 for i in 0..cols {
174 while nbits < b {
175 acc = (acc << 8) | data[idx] as u64;
176 idx += 1;
177 nbits += 8;
178 }
179 let u = ((acc >> (nbits - b)) & ((1u64 << b) - 1)) as f32;
180 nbits -= b;
181 let so = (r * ng + i / GROUP_SIZE) * 2;
182 let s = f16_to_f32(u16::from_le_bytes([
183 bytes[sc_off + so],
184 bytes[sc_off + so + 1],
185 ]));
186 dst[r * cols + i] = (u - l) * s;
187 }
188 off += rowlen;
189 }
190 Ok(())
191}
192
193pub const Q4_TILE: usize = 18;
195
196pub const Q1_TILE: usize = 6;
198
199pub fn dequant_q1(bytes: &[u8], dst: &mut [f32]) {
204 let n_groups = dst.len().div_ceil(GROUP_SIZE);
205 debug_assert_eq!(bytes.len(), n_groups * Q1_TILE);
206 for g in 0..n_groups {
207 let tile = &bytes[g * Q1_TILE..(g + 1) * Q1_TILE];
208 let s = f16_to_f32(u16::from_le_bytes([tile[0], tile[1]]));
209 let base = g * GROUP_SIZE;
210 for (j, &byte) in tile[2..].iter().enumerate() {
211 for k in 0..8 {
212 let i = base + j * 8 + k;
213 if i < dst.len() {
214 dst[i] = if (byte >> k) & 1 == 1 { s } else { -s };
215 }
216 }
217 }
218 }
219}
220
221pub fn dequant_q1s(bytes: &[u8], dst: &mut [f32]) {
226 let n_groups = dst.len().div_ceil(GROUP_SIZE);
227 let base_len = n_groups * Q1_TILE;
228 dequant_q1(&bytes[..base_len.min(bytes.len())], dst);
229 let mut off = base_len;
230 if off + 4 > bytes.len() {
231 return;
232 }
233 let count =
234 u32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]]) as usize;
235 off += 4;
236 for _ in 0..count {
237 if off + 6 > bytes.len() {
238 break;
239 }
240 let idx = u32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]])
241 as usize;
242 let val = f16_to_f32(u16::from_le_bytes([bytes[off + 4], bytes[off + 5]]));
243 if idx < dst.len() {
244 dst[idx] = val;
245 }
246 off += 6;
247 }
248}
249
250pub const Q1T_TILE: usize = 9;
255const Q1T_POW3: [u16; 5] = [1, 3, 9, 27, 81];
256
257#[inline]
259pub fn q1t_code(codes: &[u8], k: usize) -> u8 {
260 ((codes[k / 5] as u16 / Q1T_POW3[k % 5]) % 3) as u8
261}
262
263#[inline]
266pub fn q1t_pack(codes: &mut [u8; 7], k: usize, code: u8) {
267 codes[k / 5] += code * Q1T_POW3[k % 5] as u8;
268}
269
270pub fn dequant_q1t(bytes: &[u8], rows: usize, cols: usize, dst: &mut [f32]) {
276 let n_groups = dst.len().div_ceil(GROUP_SIZE);
277 let base_len = n_groups * Q1T_TILE;
278 for g in 0..n_groups {
279 let off = g * Q1T_TILE;
280 if off + Q1T_TILE > bytes.len() {
281 break;
282 }
283 let s = f16_to_f32(u16::from_le_bytes([bytes[off], bytes[off + 1]]));
284 let codes = &bytes[off + 2..off + Q1T_TILE];
285 let base = g * GROUP_SIZE;
286 for k in 0..GROUP_SIZE {
287 let i = base + k;
288 if i < dst.len() {
289 dst[i] = match q1t_code(codes, k) {
290 1 => s,
291 2 => -s,
292 _ => 0.0,
293 };
294 }
295 }
296 }
297 let entries = base_len + (rows + 1) * 4;
299 if entries > bytes.len() {
300 return;
301 }
302 let rp = |r: usize| -> usize {
303 let o = base_len + r * 4;
304 u32::from_le_bytes([bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]]) as usize
305 };
306 for r in 0..rows {
307 for p in rp(r)..rp(r + 1) {
308 let e = entries + p * 4;
309 if e + 4 > bytes.len() {
310 return;
311 }
312 let col = u16::from_le_bytes([bytes[e], bytes[e + 1]]) as usize;
313 let val = f16_to_f32(u16::from_le_bytes([bytes[e + 2], bytes[e + 3]]));
314 let i = r * cols + col;
315 if i < dst.len() {
316 dst[i] = val;
317 }
318 }
319 }
320}
321
322pub fn dequant_q4_tiled(bytes: &[u8], dst: &mut [f32]) {
327 let n_groups = dst.len().div_ceil(GROUP_SIZE);
328 debug_assert_eq!(bytes.len(), n_groups * Q4_TILE);
329 for g in 0..n_groups {
330 let tile = &bytes[g * Q4_TILE..(g + 1) * Q4_TILE];
331 let s = f16_to_f32(u16::from_le_bytes([tile[0], tile[1]]));
332 let base = g * GROUP_SIZE;
333 for (k, &byte) in tile[2..].iter().enumerate() {
334 let i0 = base + k * 2;
335 let i1 = i0 + 1;
336 if i0 < dst.len() {
337 dst[i0] = ((byte & 0x0F) as f32 - 8.0) * s;
338 }
339 if i1 < dst.len() {
340 dst[i1] = (((byte >> 4) & 0x0F) as f32 - 8.0) * s;
341 }
342 }
343 }
344}
345
346pub fn vbit_ro_sections(rows: usize, cols: usize) -> (usize, usize, usize) {
352 let ng = cols / GROUP_SIZE;
353 let scales_off = rows;
354 let offsets_off = scales_off + rows * ng * 2;
355 let packed_off = offsets_off + (rows + 1) * 4;
356 (scales_off, offsets_off, packed_off)
357}
358
359#[inline]
361pub fn vbit_ro_offset(bytes: &[u8], offsets_off: usize, r: usize) -> usize {
362 let o = offsets_off + r * 4;
363 u32::from_le_bytes([bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]]) as usize
364}
365
366pub fn dequant_vbit_ro(
369 bytes: &[u8],
370 rows: usize,
371 cols: usize,
372 dst: &mut [f32],
373) -> Result<(), String> {
374 if cols % GROUP_SIZE != 0 {
375 return Err(format!(
376 "vbit_ro: cols {cols} not a multiple of {GROUP_SIZE}"
377 ));
378 }
379 let ng = cols / GROUP_SIZE;
380 let (sc_off, off_off, packed_off) = vbit_ro_sections(rows, cols);
381 let bits = &bytes[..rows];
382 for r in 0..rows {
383 let b = bits[r] as usize;
384 if !matches!(b, 3..=6 | 8) {
385 return Err(format!(
386 "vbit_ro row {r}: bit width {b} outside {{3,4,5,6,8}}"
387 ));
388 }
389 let l = ((1usize << (b - 1)) - 1) as f32;
390 let start = packed_off + vbit_ro_offset(bytes, off_off, r);
391 let end = packed_off + vbit_ro_offset(bytes, off_off, r + 1);
392 let data = &bytes[start..end];
393 let (mut acc, mut nbits, mut idx) = (0u64, 0usize, 0usize);
394 for i in 0..cols {
395 while nbits < b {
396 acc = (acc << 8) | data[idx] as u64;
397 idx += 1;
398 nbits += 8;
399 }
400 let u = ((acc >> (nbits - b)) & ((1u64 << b) - 1)) as f32;
401 nbits -= b;
402 let so = (r * ng + i / GROUP_SIZE) * 2;
403 let sc = f16_to_f32(u16::from_le_bytes([
404 bytes[sc_off + so],
405 bytes[sc_off + so + 1],
406 ]));
407 dst[r * cols + i] = (u - l) * sc;
408 }
409 }
410 Ok(())
411}
412
413pub fn expected_nbytes(dtype: TensorDtype, shape: &[usize]) -> Option<usize> {
415 let n = shape
416 .iter()
417 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))?;
418 match dtype {
419 TensorDtype::F32 => n.checked_mul(4),
420 TensorDtype::F16 | TensorDtype::Bf16 => n.checked_mul(2),
421 TensorDtype::Q8Row => {
422 let out = *shape.first()?;
423 n.checked_add(out.checked_mul(2)?)
424 }
425 TensorDtype::Q4Block => {
426 let groups = n.div_ceil(GROUP_SIZE);
427 groups.checked_mul(18)
428 }
429 TensorDtype::Q4Tiled => {
430 n.div_ceil(GROUP_SIZE).checked_mul(18)
432 }
433 TensorDtype::Q1 => {
434 n.div_ceil(GROUP_SIZE).checked_mul(Q1_TILE)
436 }
437 TensorDtype::Q8_2f => {
438 let out = *shape.first()?;
439 let inn = n / out.max(1);
440 n.checked_add(out.checked_mul(2)?)?
441 .checked_add(inn.checked_mul(2)?)
442 }
443 _ => None, }
445}
446
447fn has_fixed_payload_size(dtype: TensorDtype) -> bool {
448 matches!(
449 dtype,
450 TensorDtype::F32
451 | TensorDtype::F16
452 | TensorDtype::Bf16
453 | TensorDtype::Q8Row
454 | TensorDtype::Q4Block
455 | TensorDtype::Q4Tiled
456 | TensorDtype::Q1
457 | TensorDtype::Q8_2f
458 )
459}
460
461pub fn validate_payload(dtype: TensorDtype, shape: &[usize], bytes: &[u8]) -> Result<(), String> {
469 if dtype == TensorDtype::VbitRo {
470 if shape.len() != 2 {
471 return Err(format!("vbit_ro tensor must be 2-D, got {shape:?}"));
472 }
473 let (rows, cols) = (shape[0], shape[1]);
474 if cols == 0 || cols % GROUP_SIZE != 0 {
475 return Err(format!(
476 "vbit_ro cols {cols} not a positive multiple of {GROUP_SIZE}"
477 ));
478 }
479 let (_, off_off, packed_off) = vbit_ro_sections(rows, cols);
480 if bytes.len() < packed_off {
481 return Err(format!(
482 "vbit_ro payload {} bytes cannot hold headers ({packed_off})",
483 bytes.len()
484 ));
485 }
486 if vbit_ro_offset(bytes, off_off, 0) != 0 {
487 return Err("vbit_ro offsets[0] must be 0".to_string());
488 }
489 for r in 0..rows {
490 let b = bytes[r];
491 if !matches!(b, 3..=6 | 8) {
492 return Err(format!(
493 "vbit_ro row {r}: bit width {b} outside {{3,4,5,6,8}}"
494 ));
495 }
496 let want = (cols * b as usize).div_ceil(8);
497 let got = vbit_ro_offset(bytes, off_off, r + 1)
498 .checked_sub(vbit_ro_offset(bytes, off_off, r))
499 .ok_or_else(|| format!("vbit_ro offsets not monotonic at row {r}"))?;
500 if want != got {
501 return Err(format!(
502 "vbit_ro row {r}: offset span {got} != {want} for width {b}"
503 ));
504 }
505 }
506 let total = packed_off + vbit_ro_offset(bytes, off_off, rows);
507 if total != bytes.len() {
508 return Err(format!(
509 "vbit_ro payload length {} != computed {total}",
510 bytes.len()
511 ));
512 }
513 return Ok(());
514 }
515 if dtype == TensorDtype::Vbit {
516 if shape.len() != 2 {
517 return Err(format!("vbit tensor must be 2-D, got {shape:?}"));
518 }
519 let (rows, cols) = (shape[0], shape[1]);
520 if cols == 0 || cols % GROUP_SIZE != 0 {
521 return Err(format!(
522 "vbit cols {cols} not a positive multiple of {GROUP_SIZE}"
523 ));
524 }
525 if bytes.len() < rows {
527 return Err(format!(
528 "vbit payload {} bytes cannot hold the {rows}-byte width header",
529 bytes.len()
530 ));
531 }
532 let ng = cols / GROUP_SIZE;
533 let mut total = rows + rows * ng * 2;
534 for (r, &b) in bytes[..rows].iter().enumerate() {
535 if !matches!(b, 3..=6 | 8) {
536 return Err(format!("vbit row {r}: bit width {b} outside {{3,4,5,6,8}}"));
537 }
538 total += (cols * b as usize).div_ceil(8);
539 }
540 if total != bytes.len() {
541 return Err(format!(
542 "vbit payload length {} != computed {} (rows {rows}, cols {cols})",
543 bytes.len(),
544 total
545 ));
546 }
547 return Ok(());
548 }
549 if has_fixed_payload_size(dtype) && expected_nbytes(dtype, shape).is_none() {
550 return Err(format!(
551 "tensor size overflows usize for {dtype:?}{shape:?}"
552 ));
553 }
554 if let Some(expect) = expected_nbytes(dtype, shape) {
555 if expect != bytes.len() {
556 return Err(format!(
557 "payload length {} != expected {expect} for {dtype:?}{shape:?}",
558 bytes.len()
559 ));
560 }
561 }
562 Ok(())
563}
564
565pub fn dequant_tensor(entry: &TensorEntry, bytes: &[u8], dst: &mut [f32]) -> Result<(), String> {
567 let n: usize = entry.shape.iter().product();
568 if dst.len() != n {
569 return Err(format!(
570 "dst len {} != tensor elems {} for '{}'",
571 dst.len(),
572 n,
573 entry.name
574 ));
575 }
576 match entry.dtype {
577 TensorDtype::F32 => {
578 for (i, d) in dst.iter_mut().enumerate() {
579 *d = f32::from_le_bytes(bytes[i * 4..i * 4 + 4].try_into().unwrap());
580 }
581 }
582 TensorDtype::F16 => {
583 for (i, d) in dst.iter_mut().enumerate() {
584 *d = f16_to_f32(u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]));
585 }
586 }
587 TensorDtype::Bf16 => {
588 for (i, d) in dst.iter_mut().enumerate() {
589 *d = bf16_to_f32(u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]));
590 }
591 }
592 TensorDtype::Q8Row => {
593 if entry.shape.len() != 2 {
594 return Err(format!("q8_row tensor '{}' must be 2-D", entry.name));
595 }
596 dequant_q8_row(bytes, entry.shape[0], entry.shape[1], dst);
597 }
598 TensorDtype::Q4Block => dequant_q4_block(bytes, dst),
599 TensorDtype::Q4Tiled => dequant_q4_tiled(bytes, dst),
600 TensorDtype::Q1 => dequant_q1(bytes, dst),
601 TensorDtype::Q1S => dequant_q1s(bytes, dst),
602 TensorDtype::Q1T => {
603 if entry.shape.len() != 2 {
604 return Err(format!("q1t tensor '{}' must be 2-D", entry.name));
605 }
606 dequant_q1t(bytes, entry.shape[0], entry.shape[1], dst);
607 }
608 TensorDtype::Vbit => {
609 if entry.shape.len() != 2 {
610 return Err(format!("vbit tensor '{}' must be 2-D", entry.name));
611 }
612 dequant_vbit(bytes, entry.shape[0], entry.shape[1], dst)?;
613 }
614 TensorDtype::VbitRo => {
615 if entry.shape.len() != 2 {
616 return Err(format!("vbit_ro tensor '{}' must be 2-D", entry.name));
617 }
618 dequant_vbit_ro(bytes, entry.shape[0], entry.shape[1], dst)?;
619 }
620 TensorDtype::Q8_2f => {
621 if entry.shape.len() != 2 {
622 return Err(format!("q8_2f tensor '{}' must be 2-D", entry.name));
623 }
624 dequant_q8_2f(bytes, entry.shape[0], entry.shape[1], dst);
625 }
626 other => {
627 return Err(format!(
628 "dtype {} of '{}' is reserved — not decodable by this runtime",
629 other.name(),
630 entry.name
631 ));
632 }
633 }
634 Ok(())
635}
636
637pub fn bytes_per_weight(dtype: TensorDtype) -> f32 {
639 match dtype {
640 TensorDtype::F32 => 4.0,
641 TensorDtype::F16 | TensorDtype::Bf16 => 2.0,
642 TensorDtype::Q8Row | TensorDtype::Q8_2f => 1.0,
643 TensorDtype::Q4Block | TensorDtype::Q4Col | TensorDtype::Mix84 | TensorDtype::Q4Tiled => {
644 0.5625
645 }
646 TensorDtype::Vbit | TensorDtype::VbitRo => 0.5,
647 TensorDtype::Q1 => 0.1875, TensorDtype::Q1S => 0.3125,
651 TensorDtype::Q1T => 0.281_25, TensorDtype::U8 => 1.0,
653 }
654}
655
656#[cfg(test)]
657mod f16_tests {
658 use super::{GROUP_SIZE, f16_to_f32, f32_to_f16, validate_payload};
659
660 #[test]
661 fn f16_subnormals_decode_correctly() {
662 assert!((f16_to_f32(0x0001) - 5.9604645e-8).abs() < 1e-12);
664 assert!((f16_to_f32(0x03FF) - 6.097_555e-5).abs() < 1e-9);
666 assert!((f16_to_f32(0x812b) - -1.7821789e-5).abs() < 1e-9);
668 assert!((f16_to_f32(0x0400) - 6.1035156e-5).abs() < 1e-9);
670 }
671
672 #[test]
673 fn f16_roundtrip_including_subnormals() {
674 for &v in &[
675 0.0f32, 1.0, -2.5, 6.097e-5, 3.0e-5, 5.96e-8, -1.782e-5, 65504.0,
676 ] {
677 let back = f16_to_f32(f32_to_f16(v));
678 let tol = (v.abs() * 1e-3).max(1e-9);
679 assert!((back - v).abs() <= tol, "roundtrip {v} -> {back}");
680 }
681 }
682 #[test]
685 fn validate_payload_vbit_contract() {
686 use crate::types::TensorDtype as D;
687 let (rows, cols) = (3usize, 64usize);
688 let ng = cols / GROUP_SIZE;
689 let bits = [4u8, 3, 8];
690 let mut good = bits.to_vec();
691 good.extend(std::iter::repeat_n(0u8, rows * ng * 2)); for &b in &bits {
693 good.extend(std::iter::repeat_n(0u8, (cols * b as usize).div_ceil(8)));
694 }
695 assert!(validate_payload(D::Vbit, &[rows, cols], &good).is_ok());
696
697 assert!(validate_payload(D::Vbit, &[rows, cols], &good[..2]).is_err());
699 assert!(validate_payload(D::Vbit, &[rows, cols], &good[..good.len() - 1]).is_err());
701 let mut long = good.clone();
702 long.push(0);
703 assert!(validate_payload(D::Vbit, &[rows, cols], &long).is_err());
704 let mut bad = good.clone();
706 bad[0] = 7;
707 assert!(validate_payload(D::Vbit, &[rows, cols], &bad).is_err());
708 assert!(validate_payload(D::Vbit, &[rows * cols], &good).is_err());
710 assert!(validate_payload(D::Vbit, &[rows, 33], &good).is_err());
711
712 let q8 = vec![0u8; 2 * 8 + 2 * 2];
714 assert!(validate_payload(D::Q8Row, &[2, 8], &q8).is_ok());
715 assert!(validate_payload(D::Q8Row, &[2, 8], &q8[..q8.len() - 1]).is_err());
716 }
717
718 #[test]
720 fn vbit_is_supported() {
721 assert!(crate::types::TensorDtype::Vbit.is_supported());
722 }
723}