1use crate::format::{FormatError, FormatResult};
9
10pub(crate) const NBIT_ATOMIC: u32 = 1;
16const NBIT_ARRAY: u32 = 2;
17const NBIT_COMPOUND: u32 = 3;
18const NBIT_NOOPTYPE: u32 = 4;
19pub(crate) const NBIT_ORDER_LE: u32 = 0;
20pub(crate) const NBIT_ORDER_BE: u32 = 1;
21
22#[derive(Clone, Copy)]
24struct NbitAtomic {
25 size: u32,
26 order: u32,
27 precision: u32,
28 offset: u32,
29}
30
31struct BitWriter<'a> {
38 buf: &'a mut [u8],
39 j: usize,
40 acc: u64,
41 nacc: u32,
42}
43
44impl<'a> BitWriter<'a> {
45 fn new(buf: &'a mut [u8]) -> Self {
46 Self {
47 buf,
48 j: 0,
49 acc: 0,
50 nacc: 0,
51 }
52 }
53
54 #[inline]
56 fn put(&mut self, v: u64, n: u32) {
57 if n > 32 {
58 self.put_half(v >> 32, n - 32);
59 self.put_half(v, 32);
60 } else {
61 self.put_half(v, n);
62 }
63 }
64
65 #[inline]
67 fn put_half(&mut self, v: u64, n: u32) {
68 self.acc = (self.acc << n) | (v & mask_u64(n as usize));
69 self.nacc += n;
70 while self.nacc >= 8 {
71 self.nacc -= 8;
72 self.buf[self.j] = (self.acc >> self.nacc) as u8;
73 self.j += 1;
74 }
75 }
76
77 fn finish(self) -> usize {
80 if self.nacc > 0 {
81 self.buf[self.j] = (self.acc << (8 - self.nacc)) as u8;
82 }
83 self.j
84 }
85}
86
87struct BitReader<'a> {
93 buf: &'a [u8],
94 j: usize,
95 acc: u64,
96 nacc: u32,
97 short: &'static str,
98}
99
100impl<'a> BitReader<'a> {
101 fn new(buf: &'a [u8], short: &'static str) -> Self {
102 Self {
103 buf,
104 j: 0,
105 acc: 0,
106 nacc: 0,
107 short,
108 }
109 }
110
111 #[inline]
113 fn get(&mut self, n: u32) -> FormatResult<u64> {
114 if n > 32 {
115 let hi = self.get_half(n - 32)?;
116 let lo = self.get_half(32)?;
117 Ok((hi << 32) | lo)
118 } else {
119 self.get_half(n)
120 }
121 }
122
123 #[inline]
125 fn get_half(&mut self, n: u32) -> FormatResult<u64> {
126 while self.nacc < n {
127 let Some(&b) = self.buf.get(self.j) else {
128 return Err(FormatError::InvalidData(self.short.into()));
129 };
130 self.acc = (self.acc << 8) | u64::from(b);
131 self.j += 1;
132 self.nacc += 8;
133 }
134 self.nacc -= n;
135 Ok((self.acc >> self.nacc) & mask_u64(n as usize))
136 }
137}
138
139const NBIT_SHORT: &str = "nbit: buffer too short";
140
141fn nbit_bytes(p: &NbitAtomic) -> impl Iterator<Item = (usize, u32, u32)> {
149 let len = p.size * 8;
150 let top = p.precision + p.offset;
151 let (begin, end, step): (i64, i64, i64) = if p.order == NBIT_ORDER_LE {
152 let begin = if top.is_multiple_of(8) {
153 top / 8 - 1
154 } else {
155 top / 8
156 };
157 (i64::from(begin), i64::from(p.offset / 8), -1)
158 } else {
159 let end = if p.offset.is_multiple_of(8) {
160 (len - p.offset) / 8 - 1
161 } else {
162 (len - p.offset) / 8
163 };
164 (i64::from((len - top) / 8), i64::from(end), 1)
165 };
166 let p = *p;
167 std::iter::successors(Some(begin), move |&k| (k != end).then(|| k + step)).map(move |k| {
168 let (bits, shift) = if begin == end {
169 (p.precision, p.offset % 8)
170 } else if k == begin {
171 (8 - (len - top) % 8, 0)
172 } else if k == end {
173 let bits = 8 - p.offset % 8;
174 (bits, 8 - bits)
175 } else {
176 (8, 0)
177 };
178 (k as usize, bits, shift)
179 })
180}
181
182#[inline]
185fn load_uint<const N: usize>(bytes: [u8; N], le: bool) -> u64 {
186 let mut padded = [0u8; 8];
187 if le {
188 padded[..N].copy_from_slice(&bytes);
189 u64::from_le_bytes(padded)
190 } else {
191 padded[8 - N..].copy_from_slice(&bytes);
192 u64::from_be_bytes(padded)
193 }
194}
195
196#[inline]
198fn store_uint<const N: usize>(v: u64, le: bool) -> [u8; N] {
199 let mut out = [0u8; N];
200 if le {
201 out.copy_from_slice(&v.to_le_bytes()[..N]);
202 } else {
203 out.copy_from_slice(&v.to_be_bytes()[8 - N..]);
204 }
205 out
206}
207
208macro_rules! by_width {
212 ($size:expr, $f:ident($($arg:expr),* $(,)?)) => {
213 match $size {
214 1 => $f::<1>($($arg),*),
215 2 => $f::<2>($($arg),*),
216 4 => $f::<4>($($arg),*),
217 8 => $f::<8>($($arg),*),
218 n => unreachable!("element width {n} is not 1, 2, 4 or 8"),
219 }
220 };
221}
222
223#[inline]
225fn nbit_field<const N: usize>(elem: &[u8], le: bool, offset: u32) -> u64 {
226 load_uint::<N>(elem.try_into().expect("elem is N bytes"), le) >> offset
227}
228
229#[inline]
231fn nbit_place<const N: usize>(elem: &mut [u8], le: bool, offset: u32, field: u64) {
232 elem.copy_from_slice(&store_uint::<N>(field << offset, le));
233}
234
235fn nbit_compress_atomics<const N: usize>(data: &[u8], w: &mut BitWriter, p: &NbitAtomic) {
239 let le = p.order == NBIT_ORDER_LE;
240 let (elems, _) = data.as_chunks::<N>();
241 for &e in elems {
242 w.put(load_uint(e, le) >> p.offset, p.precision);
243 }
244}
245
246fn nbit_decompress_atomics<const N: usize>(
248 out: &mut [u8],
249 r: &mut BitReader,
250 p: &NbitAtomic,
251) -> FormatResult<()> {
252 let le = p.order == NBIT_ORDER_LE;
253 let (elems, _) = out.as_chunks_mut::<N>();
254 for e in elems {
255 *e = store_uint(r.get(p.precision)? << p.offset, le);
256 }
257 Ok(())
258}
259
260fn nbit_decompress_one_nooptype(
262 data: &mut [u8],
263 data_offset: usize,
264 r: &mut BitReader,
265 size: u32,
266) -> FormatResult<()> {
267 for b in &mut data[data_offset..data_offset + size as usize] {
268 *b = r.get(8)? as u8;
269 }
270 Ok(())
271}
272
273fn nbit_compress_one_nooptype(data: &[u8], data_offset: usize, w: &mut BitWriter, size: u32) {
275 for &b in &data[data_offset..data_offset + size as usize] {
276 w.put(u64::from(b), 8);
277 }
278}
279
280fn nbit_decompress_one_atomic(
285 data: &mut [u8],
286 data_offset: usize,
287 r: &mut BitReader,
288 p: &NbitAtomic,
289) -> FormatResult<()> {
290 let elem = &mut data[data_offset..data_offset + p.size as usize];
291 if matches!(p.size, 1 | 2 | 4 | 8) {
292 let field = r.get(p.precision)?;
293 let le = p.order == NBIT_ORDER_LE;
294 by_width!(p.size, nbit_place(elem, le, p.offset, field));
295 return Ok(());
296 }
297 for (k, bits, shift) in nbit_bytes(p) {
298 elem[k] = (r.get(bits)? << shift) as u8;
299 }
300 Ok(())
301}
302
303fn nbit_compress_one_atomic(data: &[u8], data_offset: usize, w: &mut BitWriter, p: &NbitAtomic) {
305 let elem = &data[data_offset..data_offset + p.size as usize];
306 if matches!(p.size, 1 | 2 | 4 | 8) {
307 let le = p.order == NBIT_ORDER_LE;
308 w.put(
309 by_width!(p.size, nbit_field(elem, le, p.offset)),
310 p.precision,
311 );
312 return;
313 }
314 for (k, bits, shift) in nbit_bytes(p) {
315 w.put(u64::from(elem[k] >> shift), bits);
316 }
317}
318
319fn read_atomic(parms: &[u32], idx: &mut usize) -> FormatResult<NbitAtomic> {
322 if *idx + 4 > parms.len() {
323 return Err(FormatError::InvalidData(
324 "nbit: parameter list truncated".into(),
325 ));
326 }
327 let p = NbitAtomic {
328 size: parms[*idx],
329 order: parms[*idx + 1],
330 precision: parms[*idx + 2],
331 offset: parms[*idx + 3],
332 };
333 *idx += 4;
334 let bits = p.size.checked_mul(8);
337 let span = p.precision.checked_add(p.offset);
338 match (bits, span) {
339 (Some(bits), Some(span))
340 if p.size > 0 && p.precision > 0 && p.precision <= bits && span <= bits => {}
341 _ => {
342 return Err(FormatError::InvalidData(format!(
343 "nbit: invalid atomic datatype (size={}, precision={}, offset={})",
344 p.size, p.precision, p.offset
345 )));
346 }
347 }
348 Ok(p)
349}
350
351fn nbit_decompress_one_array(
353 data: &mut [u8],
354 data_offset: usize,
355 r: &mut BitReader,
356 parms: &[u32],
357 parms_index: &mut usize,
358) -> FormatResult<()> {
359 if *parms_index + 2 > parms.len() {
360 return Err(FormatError::InvalidData(
361 "nbit: parameter list truncated".into(),
362 ));
363 }
364 let total_size = parms[*parms_index];
365 let base_class = parms[*parms_index + 1];
366 *parms_index += 2;
367
368 match base_class {
369 NBIT_ATOMIC => {
370 let p = read_atomic(parms, parms_index)?;
371 let n = total_size / p.size;
372 for i in 0..n as usize {
373 nbit_decompress_one_atomic(data, data_offset + i * p.size as usize, r, &p)?;
374 }
375 }
376 NBIT_ARRAY => {
377 let base_size = parms[*parms_index];
378 let n = total_size / base_size;
379 let begin_index = *parms_index;
380 for i in 0..n as usize {
381 *parms_index = begin_index;
382 nbit_decompress_one_array(
383 data,
384 data_offset + i * base_size as usize,
385 r,
386 parms,
387 parms_index,
388 )?;
389 }
390 }
391 NBIT_COMPOUND => {
392 let base_size = parms[*parms_index];
393 let n = total_size / base_size;
394 let begin_index = *parms_index;
395 for i in 0..n as usize {
396 *parms_index = begin_index;
397 nbit_decompress_one_compound(
398 data,
399 data_offset + i * base_size as usize,
400 r,
401 parms,
402 parms_index,
403 )?;
404 }
405 }
406 NBIT_NOOPTYPE => {
407 *parms_index += 1; nbit_decompress_one_nooptype(data, data_offset, r, total_size)?;
409 }
410 _ => {
411 return Err(FormatError::InvalidData(format!(
412 "nbit: bad base class {}",
413 base_class
414 )))
415 }
416 }
417 Ok(())
418}
419
420fn nbit_decompress_one_compound(
422 data: &mut [u8],
423 data_offset: usize,
424 r: &mut BitReader,
425 parms: &[u32],
426 parms_index: &mut usize,
427) -> FormatResult<()> {
428 if *parms_index + 2 > parms.len() {
429 return Err(FormatError::InvalidData(
430 "nbit: parameter list truncated".into(),
431 ));
432 }
433 *parms_index += 1; let nmembers = parms[*parms_index];
435 *parms_index += 1;
436
437 for _ in 0..nmembers {
438 if *parms_index + 2 > parms.len() {
439 return Err(FormatError::InvalidData(
440 "nbit: parameter list truncated".into(),
441 ));
442 }
443 let member_offset = parms[*parms_index] as usize;
444 let member_class = parms[*parms_index + 1];
445 *parms_index += 2;
446
447 match member_class {
448 NBIT_ATOMIC => {
449 let p = read_atomic(parms, parms_index)?;
450 nbit_decompress_one_atomic(data, data_offset + member_offset, r, &p)?;
451 }
452 NBIT_ARRAY => {
453 nbit_decompress_one_array(
454 data,
455 data_offset + member_offset,
456 r,
457 parms,
458 parms_index,
459 )?;
460 }
461 NBIT_COMPOUND => {
462 nbit_decompress_one_compound(
463 data,
464 data_offset + member_offset,
465 r,
466 parms,
467 parms_index,
468 )?;
469 }
470 NBIT_NOOPTYPE => {
471 let size = parms[*parms_index];
472 *parms_index += 1;
473 nbit_decompress_one_nooptype(data, data_offset + member_offset, r, size)?;
474 }
475 _ => {
476 return Err(FormatError::InvalidData(format!(
477 "nbit: bad member class {}",
478 member_class
479 )))
480 }
481 }
482 }
483 Ok(())
484}
485
486fn nbit_compress_one_array(
488 data: &[u8],
489 data_offset: usize,
490 w: &mut BitWriter,
491 parms: &[u32],
492 parms_index: &mut usize,
493) -> FormatResult<()> {
494 if *parms_index + 2 > parms.len() {
495 return Err(FormatError::InvalidData(
496 "nbit: parameter list truncated".into(),
497 ));
498 }
499 let total_size = parms[*parms_index];
500 let base_class = parms[*parms_index + 1];
501 *parms_index += 2;
502
503 match base_class {
504 NBIT_ATOMIC => {
505 let p = read_atomic(parms, parms_index)?;
506 let n = total_size / p.size;
507 for i in 0..n as usize {
508 nbit_compress_one_atomic(data, data_offset + i * p.size as usize, w, &p);
509 }
510 }
511 NBIT_ARRAY => {
512 let base_size = parms[*parms_index];
513 let n = total_size / base_size;
514 let begin_index = *parms_index;
515 for i in 0..n as usize {
516 *parms_index = begin_index;
517 nbit_compress_one_array(
518 data,
519 data_offset + i * base_size as usize,
520 w,
521 parms,
522 parms_index,
523 )?;
524 }
525 }
526 NBIT_COMPOUND => {
527 let base_size = parms[*parms_index];
528 let n = total_size / base_size;
529 let begin_index = *parms_index;
530 for i in 0..n as usize {
531 *parms_index = begin_index;
532 nbit_compress_one_compound(
533 data,
534 data_offset + i * base_size as usize,
535 w,
536 parms,
537 parms_index,
538 )?;
539 }
540 }
541 NBIT_NOOPTYPE => {
542 *parms_index += 1;
543 nbit_compress_one_nooptype(data, data_offset, w, total_size);
544 }
545 _ => {
546 return Err(FormatError::InvalidData(format!(
547 "nbit: bad base class {}",
548 base_class
549 )))
550 }
551 }
552 Ok(())
553}
554
555fn nbit_compress_one_compound(
557 data: &[u8],
558 data_offset: usize,
559 w: &mut BitWriter,
560 parms: &[u32],
561 parms_index: &mut usize,
562) -> FormatResult<()> {
563 if *parms_index + 2 > parms.len() {
564 return Err(FormatError::InvalidData(
565 "nbit: parameter list truncated".into(),
566 ));
567 }
568 *parms_index += 1;
569 let nmembers = parms[*parms_index];
570 *parms_index += 1;
571
572 for _ in 0..nmembers {
573 if *parms_index + 2 > parms.len() {
574 return Err(FormatError::InvalidData(
575 "nbit: parameter list truncated".into(),
576 ));
577 }
578 let member_offset = parms[*parms_index] as usize;
579 let member_class = parms[*parms_index + 1];
580 *parms_index += 2;
581
582 match member_class {
583 NBIT_ATOMIC => {
584 let p = read_atomic(parms, parms_index)?;
585 nbit_compress_one_atomic(data, data_offset + member_offset, w, &p);
586 }
587 NBIT_ARRAY => {
588 nbit_compress_one_array(data, data_offset + member_offset, w, parms, parms_index)?;
589 }
590 NBIT_COMPOUND => {
591 nbit_compress_one_compound(
592 data,
593 data_offset + member_offset,
594 w,
595 parms,
596 parms_index,
597 )?;
598 }
599 NBIT_NOOPTYPE => {
600 let size = parms[*parms_index];
601 *parms_index += 1;
602 nbit_compress_one_nooptype(data, data_offset + member_offset, w, size);
603 }
604 _ => {
605 return Err(FormatError::InvalidData(format!(
606 "nbit: bad member class {}",
607 member_class
608 )))
609 }
610 }
611 }
612 Ok(())
613}
614
615pub fn apply_nbit(data: &[u8], cd_values: &[u32], compress: bool) -> FormatResult<Vec<u8>> {
624 if cd_values.len() < 4 {
625 return Err(FormatError::InvalidData("nbit: cd_values too short".into()));
626 }
627 if cd_values[1] != 0 {
629 return Ok(data.to_vec());
630 }
631
632 let d_nelmts = cd_values[2] as usize;
633 let dtype_size = cd_values[4] as usize;
634 if dtype_size == 0 {
635 return Err(FormatError::InvalidData("nbit: zero datatype size".into()));
636 }
637 let unpacked_size = d_nelmts * dtype_size;
638
639 if compress {
640 if data.len() != unpacked_size {
641 return Err(FormatError::InvalidData(format!(
642 "nbit: input size {} != expected {}",
643 data.len(),
644 unpacked_size
645 )));
646 }
647 let mut buffer = vec![0u8; unpacked_size + 1];
649 let mut w = BitWriter::new(&mut buffer);
650 match cd_values[3] {
651 NBIT_ATOMIC => {
652 let mut idx = 4;
653 let p = read_atomic(cd_values, &mut idx)?;
654 if matches!(p.size, 1 | 2 | 4 | 8) {
655 by_width!(p.size, nbit_compress_atomics(data, &mut w, &p));
656 } else {
657 for i in 0..d_nelmts {
658 nbit_compress_one_atomic(data, i * p.size as usize, &mut w, &p);
659 }
660 }
661 }
662 NBIT_ARRAY => {
663 let size = cd_values[4] as usize;
664 for i in 0..d_nelmts {
665 let mut idx = 4;
666 nbit_compress_one_array(data, i * size, &mut w, cd_values, &mut idx)?;
667 }
668 }
669 NBIT_COMPOUND => {
670 let size = cd_values[4] as usize;
671 for i in 0..d_nelmts {
672 let mut idx = 4;
673 nbit_compress_one_compound(data, i * size, &mut w, cd_values, &mut idx)?;
674 }
675 }
676 other => {
677 return Err(FormatError::InvalidData(format!(
678 "nbit: unsupported top class {}",
679 other
680 )))
681 }
682 }
683 let j = w.finish();
685 buffer.truncate(j + 1);
686 Ok(buffer)
687 } else {
688 let mut out = vec![0u8; unpacked_size];
689 let mut r = BitReader::new(data, NBIT_SHORT);
690 match cd_values[3] {
691 NBIT_ATOMIC => {
692 let mut idx = 4;
693 let p = read_atomic(cd_values, &mut idx)?;
694 if p.precision > p.size * 8 || p.precision + p.offset > p.size * 8 {
695 return Err(FormatError::InvalidData(
696 "nbit: invalid precision/offset".into(),
697 ));
698 }
699 if matches!(p.size, 1 | 2 | 4 | 8) {
700 by_width!(p.size, nbit_decompress_atomics(&mut out, &mut r, &p))?;
701 } else {
702 for i in 0..d_nelmts {
703 nbit_decompress_one_atomic(&mut out, i * p.size as usize, &mut r, &p)?;
704 }
705 }
706 }
707 NBIT_ARRAY => {
708 let size = cd_values[4] as usize;
709 for i in 0..d_nelmts {
710 let mut idx = 4;
711 nbit_decompress_one_array(&mut out, i * size, &mut r, cd_values, &mut idx)?;
712 }
713 }
714 NBIT_COMPOUND => {
715 let size = cd_values[4] as usize;
716 for i in 0..d_nelmts {
717 let mut idx = 4;
718 nbit_decompress_one_compound(&mut out, i * size, &mut r, cd_values, &mut idx)?;
719 }
720 }
721 other => {
722 return Err(FormatError::InvalidData(format!(
723 "nbit: unsupported top class {}",
724 other
725 )))
726 }
727 }
728 Ok(out)
729 }
730}
731
732const SO_PARM_SCALETYPE: usize = 0;
738const SO_PARM_SCALEFACTOR: usize = 1;
739const SO_PARM_NELMTS: usize = 2;
740const SO_PARM_CLASS: usize = 3;
741const SO_PARM_SIZE: usize = 4;
742const SO_PARM_SIGN: usize = 5;
743const SO_PARM_ORDER: usize = 6;
744const SO_PARM_FILAVAIL: usize = 7;
745const SO_PARM_FILVAL: usize = 8;
747
748pub(crate) const SO_CLS_INTEGER: u32 = 0;
749pub(crate) const SO_CLS_FLOAT: u32 = 1;
750pub(crate) const SO_ORDER_LE: u32 = 0;
751const SO_FILL_DEFINED: u32 = 1;
752pub(crate) const SO_FLOAT_DSCALE: u32 = 0;
754pub(crate) const SO_INT: u32 = 2;
756pub(crate) const SO_SGN_NONE: u32 = 0;
758pub(crate) const SO_SGN_2: u32 = 1;
760pub(crate) const SO_ORDER_BE: u32 = 1;
762pub(crate) const SO_TOTAL_NPARMS: usize = 20;
764
765const SO_BUF_OFFSET: usize = 21;
767
768const SO_SHORT: &str = "scaleoffset: buffer too short";
769
770fn so_log2(num: u64) -> u32 {
772 let mut v = 0u32;
773 let mut lower_bound: u64 = 1;
774 let mut val = num;
775 while {
776 val >>= 1;
777 val != 0
778 } {
779 v += 1;
780 lower_bound <<= 1;
781 }
782 if num == lower_bound {
783 v
784 } else {
785 v + 1
786 }
787}
788
789#[derive(Clone, Copy)]
793struct SoParams {
794 scale_factor: i32,
795 d_nelmts: usize,
796 dtype_class: u32,
797 size: usize,
799 dtype_sign: u32,
800 order: u32,
801 fill_defined: bool,
802 filval: u64,
804}
805
806impl SoParams {
807 fn parse(cd_values: &[u32]) -> FormatResult<Self> {
808 if cd_values.len() < 8 {
809 return Err(FormatError::InvalidData(
810 "scaleoffset: cd_values too short".into(),
811 ));
812 }
813 let scale_type = cd_values[SO_PARM_SCALETYPE];
814 let dtype_class = cd_values[SO_PARM_CLASS];
815 let size = cd_values[SO_PARM_SIZE] as usize;
816 let fill_defined = cd_values[SO_PARM_FILAVAIL] == SO_FILL_DEFINED;
817
818 if !matches!(size, 1 | 2 | 4 | 8) {
821 return Err(FormatError::InvalidData(format!(
822 "scaleoffset: unsupported datatype size {}",
823 size
824 )));
825 }
826 if dtype_class == SO_CLS_FLOAT && scale_type != SO_FLOAT_DSCALE {
827 return Err(FormatError::UnsupportedFeature(
828 "scaleoffset E-scaling method is not supported".into(),
829 ));
830 }
831
832 let filval: u64 = if fill_defined {
839 let mut v: u64 = 0;
840 let n_cd = size.div_ceil(4);
841 if cd_values.len() < SO_PARM_FILVAL + n_cd {
842 return Err(FormatError::InvalidData(
843 "scaleoffset: cd_values missing fill value".into(),
844 ));
845 }
846 for (w, cd) in cd_values[SO_PARM_FILVAL..SO_PARM_FILVAL + n_cd]
847 .iter()
848 .enumerate()
849 {
850 v |= (*cd as u64) << (w * 32);
851 }
852 v & mask_u64(size * 8)
853 } else {
854 0
855 };
856
857 let mut scale_factor = cd_values[SO_PARM_SCALEFACTOR] as i32;
860 if dtype_class == SO_CLS_INTEGER && scale_factor < 0 {
861 scale_factor = 0;
862 }
863
864 Ok(Self {
865 scale_factor,
866 d_nelmts: cd_values[SO_PARM_NELMTS] as usize,
867 dtype_class,
868 size,
869 dtype_sign: cd_values[SO_PARM_SIGN],
870 order: cd_values[SO_PARM_ORDER],
871 fill_defined,
872 filval,
873 })
874 }
875
876 fn is_noop(&self) -> bool {
881 self.dtype_class == SO_CLS_INTEGER && self.scale_factor as usize == self.size * 8
882 }
883
884 fn dtype_len(&self) -> u32 {
886 (self.size * 8) as u32
887 }
888
889 fn width_mask(&self) -> u64 {
891 mask_u64(self.size * 8)
892 }
893}
894
895fn mask_u64(n: usize) -> u64 {
897 if n >= 64 {
898 u64::MAX
899 } else {
900 !(u64::MAX << n)
901 }
902}
903
904fn so_pack<const N: usize>(buf: &[u8], le: bool, minbits: u32, w: &mut BitWriter) {
906 let (elems, _) = buf.as_chunks::<N>();
907 for &elem in elems {
908 w.put(load_uint(elem, le), minbits);
909 }
910}
911
912fn so_unpack<const N: usize>(
914 out: &mut [u8],
915 le: bool,
916 minbits: u32,
917 r: &mut BitReader,
918) -> FormatResult<()> {
919 let (elems, _) = out.as_chunks_mut::<N>();
920 for elem in elems {
921 *elem = store_uint(r.get(minbits)?, le);
922 }
923 Ok(())
924}
925
926pub fn reverse_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
931 let p = SoParams::parse(cd_values)?;
932 let (d_nelmts, size, order) = (p.d_nelmts, p.size, p.order);
933 let size_out = d_nelmts * size;
934
935 if p.is_noop() {
936 if data.len() < size_out {
941 return Err(FormatError::InvalidData(SO_SHORT.into()));
942 }
943 return Ok(data[..size_out].to_vec());
944 }
945
946 if data.len() < SO_BUF_OFFSET {
948 return Err(FormatError::InvalidData(
949 "scaleoffset: buffer too short for header".into(),
950 ));
951 }
952 let mut minbits: u32 = 0;
953 for (i, &b) in data[..4].iter().enumerate() {
954 minbits |= (b as u32) << (i * 8);
955 }
956 if minbits as usize > size * 8 {
957 return Err(FormatError::InvalidData(
958 "scaleoffset: minbits exceeds datatype size".into(),
959 ));
960 }
961 let minval_size = std::cmp::min(8usize, data[4] as usize);
962 let mut minval: u64 = 0;
963 for i in 0..minval_size {
964 minval |= (data[5 + i] as u64) << (i * 8);
965 }
966
967 if minbits as usize == size * 8 {
969 if data.len() < SO_BUF_OFFSET + size_out {
970 return Err(FormatError::InvalidData(SO_SHORT.into()));
971 }
972 return Ok(data[SO_BUF_OFFSET..SO_BUF_OFFSET + size_out].to_vec());
973 }
974
975 let mut out = vec![0u8; size_out];
976
977 if minbits != 0 {
978 if data.len() < SO_BUF_OFFSET {
979 return Err(FormatError::InvalidData(SO_SHORT.into()));
980 }
981 let mut r = BitReader::new(&data[SO_BUF_OFFSET..], SO_SHORT);
982 let le = order == SO_ORDER_LE;
983 by_width!(size, so_unpack(&mut out, le, minbits, &mut r))?;
984 }
985 postdecompress(&mut out, &p, minbits, minval);
989
990 Ok(out)
991}
992
993pub fn forward_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
1002 let p = SoParams::parse(cd_values)?;
1003 let nbytes = p.d_nelmts * p.size;
1004 if data.len() != nbytes {
1005 return Err(FormatError::InvalidData(format!(
1006 "scaleoffset: chunk is {} bytes, but the filter parameters describe {} elements of \
1007 {} bytes",
1008 data.len(),
1009 p.d_nelmts,
1010 p.size
1011 )));
1012 }
1013 if p.is_noop() {
1014 return Ok(data.to_vec());
1015 }
1016 if p.dtype_class == SO_CLS_INTEGER && p.scale_factor as usize > p.size * 8 {
1017 return Err(FormatError::InvalidData(
1018 "scaleoffset: minimum number of bits exceeds the datatype".into(),
1019 ));
1020 }
1021
1022 let mut buf = data.to_vec();
1026 let (minbits, minval) = if p.dtype_class == SO_CLS_INTEGER {
1027 by_width!(p.size, precompress_int(&mut buf, &p))
1028 } else {
1029 precompress_float(&mut buf, &p)?
1030 };
1031 debug_assert!(minbits <= p.dtype_len());
1032
1033 let size_out = SO_BUF_OFFSET + nbytes * minbits as usize / (p.size * 8) + 1;
1036 let mut out = vec![0u8; size_out];
1037 out[..4].copy_from_slice(&minbits.to_le_bytes());
1038 out[4] = 8;
1042 out[5..13].copy_from_slice(&minval.to_le_bytes());
1043
1044 if minbits as usize == p.size * 8 {
1045 out.truncate(SO_BUF_OFFSET + nbytes);
1048 out[SO_BUF_OFFSET..].copy_from_slice(&buf);
1049 return Ok(out);
1050 }
1051 if minbits != 0 {
1052 let mut w = BitWriter::new(&mut out[SO_BUF_OFFSET..]);
1053 let le = p.order == SO_ORDER_LE;
1054 by_width!(p.size, so_pack(&buf, le, minbits, &mut w));
1055 w.finish();
1056 }
1057 Ok(out)
1060}
1061
1062fn precompress_int<const N: usize>(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1069 let signed = p.dtype_sign == SO_SGN_2;
1070 let le = p.order == SO_ORDER_LE;
1071 let width_mask = p.width_mask();
1072 let key = |raw: u64| -> i128 {
1075 if signed {
1076 i128::from(sign_extend::<N>(raw))
1077 } else {
1078 i128::from(raw)
1079 }
1080 };
1081 let (elems, _) = buf.as_chunks_mut::<N>();
1082
1083 let mut minbits = p.scale_factor as u32;
1084 let mut min: i128 = 0;
1085 let mut max: i128 = 0;
1086
1087 if p.fill_defined {
1088 let first = elems.iter().position(|&e| load_uint(e, le) != p.filval);
1090 if let Some(f) = first {
1091 min = key(load_uint(elems[f], le));
1092 max = min;
1093 for &e in &elems[f..] {
1094 let raw = load_uint(e, le);
1095 if raw == p.filval {
1096 continue;
1097 }
1098 let v = key(raw);
1099 max = max.max(v);
1100 min = min.min(v);
1101 }
1102 }
1103 if minbits == 0 {
1104 let span_minus_1 = (max - min) as u64;
1105 if span_minus_1 > width_mask - 2 {
1106 return (p.dtype_len(), 0);
1109 }
1110 minbits = so_log2(span_minus_1 + 2);
1111 }
1112 if minbits != p.dtype_len() {
1113 let sentinel = mask_u64(minbits as usize);
1114 for e in elems.iter_mut() {
1115 let raw = load_uint(*e, le);
1116 let v = if raw == p.filval {
1117 sentinel
1118 } else {
1119 (key(raw) - min) as u64 & width_mask
1120 };
1121 *e = store_uint(v, le);
1122 }
1123 }
1124 } else {
1125 if let Some(&e0) = elems.first() {
1126 min = key(load_uint(e0, le));
1127 max = min;
1128 }
1129 for &e in elems.iter() {
1130 let v = key(load_uint(e, le));
1131 max = max.max(v);
1132 min = min.min(v);
1133 }
1134 if minbits == 0 {
1135 let span_minus_1 = (max - min) as u64;
1136 if span_minus_1 > width_mask - 2 {
1137 return (p.dtype_len(), 0);
1138 }
1139 minbits = so_log2(span_minus_1 + 1);
1140 }
1141 if minbits != p.dtype_len() {
1142 for e in elems.iter_mut() {
1143 let v = (key(load_uint(*e, le)) - min) as u64 & width_mask;
1144 *e = store_uint(v, le);
1145 }
1146 }
1147 }
1148
1149 (
1150 minbits,
1151 min as i64 as u64 & if signed { u64::MAX } else { width_mask },
1152 )
1153}
1154
1155trait SoFloat:
1160 Copy
1161 + PartialOrd
1162 + std::ops::Mul<Output = Self>
1163 + std::ops::Sub<Output = Self>
1164 + std::ops::Div<Output = Self>
1165 + std::ops::Add<Output = Self>
1166{
1167 const ZERO: Self;
1168 fn from_stored(v: u64) -> Self;
1169 fn to_stored(self) -> u64;
1170 fn widen(self) -> f64;
1171 fn narrow(v: f64) -> Self;
1173 fn from_int(v: i64) -> Self;
1175 fn pow(base: f64, exp: f64) -> Self;
1177 fn abs(self) -> Self;
1178 fn round(self) -> Self;
1180 fn lround(self) -> i64;
1182}
1183
1184impl SoFloat for f32 {
1185 const ZERO: Self = 0.0;
1186 fn from_stored(v: u64) -> Self {
1187 f32::from_bits(v as u32)
1188 }
1189 fn to_stored(self) -> u64 {
1190 self.to_bits() as u64
1191 }
1192 fn widen(self) -> f64 {
1193 self as f64
1194 }
1195 fn narrow(v: f64) -> Self {
1196 v as f32
1197 }
1198 fn from_int(v: i64) -> Self {
1199 v as f32
1200 }
1201 fn pow(base: f64, exp: f64) -> Self {
1202 (base as f32).powf(exp as f32)
1203 }
1204 fn abs(self) -> Self {
1205 f32::abs(self)
1206 }
1207 fn round(self) -> Self {
1208 f32::round(self)
1209 }
1210 fn lround(self) -> i64 {
1211 f32::round(self) as i64
1212 }
1213}
1214
1215impl SoFloat for f64 {
1216 const ZERO: Self = 0.0;
1217 fn from_stored(v: u64) -> Self {
1218 f64::from_bits(v)
1219 }
1220 fn to_stored(self) -> u64 {
1221 self.to_bits()
1222 }
1223 fn widen(self) -> f64 {
1224 self
1225 }
1226 fn narrow(v: f64) -> Self {
1227 v
1228 }
1229 fn from_int(v: i64) -> Self {
1230 v as f64
1231 }
1232 fn pow(base: f64, exp: f64) -> Self {
1233 base.powf(exp)
1234 }
1235 fn abs(self) -> Self {
1236 f64::abs(self)
1237 }
1238 fn round(self) -> Self {
1239 f64::round(self)
1240 }
1241 fn lround(self) -> i64 {
1242 f64::round(self) as i64
1243 }
1244}
1245
1246fn precompress_float(buf: &mut [u8], p: &SoParams) -> FormatResult<(u32, u64)> {
1254 match p.size {
1255 4 => Ok(precompress_float_typed::<f32, 4>(buf, p)),
1256 8 => Ok(precompress_float_typed::<f64, 8>(buf, p)),
1257 n => Err(FormatError::InvalidData(format!(
1258 "scaleoffset: no floating-point type of {n} bytes"
1259 ))),
1260 }
1261}
1262
1263fn precompress_float_typed<T: SoFloat, const N: usize>(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1264 let d_val = p.scale_factor as f64;
1265 let pow10 = T::pow(10.0, d_val);
1266 let filval = T::from_stored(p.filval);
1267 let le = p.order == SO_ORDER_LE;
1268 let get = |e: [u8; N]| T::from_stored(load_uint(e, le));
1269 let (elems, _) = buf.as_chunks_mut::<N>();
1270 let scan_epsilon = 10f64.powf(-d_val);
1276 let is_fill_scan = |v: T| (v - filval).widen().abs() < scan_epsilon;
1277 let modify_epsilon = T::pow(10.0, -d_val);
1278 let is_fill_modify = |v: T| (v - filval).abs() < modify_epsilon;
1279
1280 let mut min = T::ZERO;
1281 let mut max = T::ZERO;
1282 if p.fill_defined {
1283 if let Some(f) = elems.iter().position(|&e| !is_fill_scan(get(e))) {
1284 min = get(elems[f]);
1285 max = min;
1286 for &e in &elems[f..] {
1287 let v = get(e);
1288 if is_fill_scan(v) {
1289 continue;
1290 }
1291 if v > max {
1292 max = v;
1293 }
1294 if v < min {
1295 min = v;
1296 }
1297 }
1298 }
1299 } else if let Some(&e0) = elems.first() {
1300 min = get(e0);
1301 max = min;
1302 for &e in elems.iter() {
1303 let v = get(e);
1304 if v > max {
1305 max = v;
1306 }
1307 if v < min {
1308 min = v;
1309 }
1310 }
1311 }
1312
1313 let dtype_len = p.dtype_len();
1318 let scaled = max * pow10 - min * pow10;
1319 if scaled.round() > T::pow(2.0, (dtype_len - 1) as f64) {
1320 return (dtype_len, 0);
1321 }
1322 let span = scaled.lround() as u64 + 1;
1323 let minbits = if p.fill_defined {
1324 so_log2(span + 1)
1325 } else {
1326 so_log2(span)
1327 };
1328
1329 if minbits != dtype_len {
1330 let sentinel = mask_u64(minbits as usize);
1331 for e in elems.iter_mut() {
1332 let v = get(*e);
1333 let stored = if p.fill_defined && is_fill_modify(v) {
1334 sentinel
1335 } else {
1336 (v * pow10 - min * pow10).lround() as u64 & p.width_mask()
1337 };
1338 *e = store_uint(stored, le);
1339 }
1340 }
1341
1342 (minbits, min.to_stored())
1343}
1344
1345#[inline]
1347fn sign_extend<const N: usize>(v: u64) -> i64 {
1348 let shift = 64 - 8 * N as u32;
1349 ((v << shift) as i64) >> shift
1350}
1351
1352fn postdecompress(out: &mut [u8], p: &SoParams, minbits: u32, minval: u64) {
1359 let sentinel = mask_u64(minbits as usize);
1362 if p.dtype_class == SO_CLS_INTEGER {
1363 by_width!(p.size, postdecompress_int(out, p, sentinel, minval));
1364 } else {
1365 match p.size {
1368 4 => postdecompress_float::<f32, 4>(out, p, sentinel, minval),
1369 8 => postdecompress_float::<f64, 8>(out, p, sentinel, minval),
1370 _ => {}
1371 }
1372 }
1373}
1374
1375fn postdecompress_int<const N: usize>(out: &mut [u8], p: &SoParams, sentinel: u64, minval: u64) {
1378 let le = p.order == SO_ORDER_LE;
1379 let width_mask = p.width_mask();
1380 let (elems, _) = out.as_chunks_mut::<N>();
1381 for e in elems {
1382 let v = load_uint(*e, le);
1383 let result = if p.fill_defined && v == sentinel {
1384 p.filval
1385 } else {
1386 v.wrapping_add(minval) & width_mask
1387 };
1388 *e = store_uint(result, le);
1389 }
1390}
1391
1392fn postdecompress_float<T: SoFloat, const N: usize>(
1393 out: &mut [u8],
1394 p: &SoParams,
1395 sentinel: u64,
1396 minval: u64,
1397) {
1398 let le = p.order == SO_ORDER_LE;
1399 let divisor = T::narrow(10f64.powf(p.scale_factor as f64));
1400 let min = T::from_stored(minval);
1401 let filval = T::from_stored(p.filval);
1402 let (elems, _) = out.as_chunks_mut::<N>();
1403 for e in elems {
1404 let raw = load_uint(*e, le);
1405 let val = if p.fill_defined && raw == sentinel {
1406 filval
1407 } else {
1408 T::from_int(sign_extend::<N>(raw)) / divisor + min
1409 };
1410 *e = store_uint(val.to_stored(), le);
1411 }
1412}
1413
1414use crate::format::messages::datatype::{ByteOrder, DatatypeMessage};
1419
1420fn is_standard_ieee_float(dt: &DatatypeMessage) -> bool {
1424 dt.ieee_format().is_some()
1425}
1426
1427pub fn datatype_needs_bit_conversion(dt: &DatatypeMessage) -> bool {
1441 match dt {
1442 DatatypeMessage::FixedPoint {
1443 size,
1444 bit_offset,
1445 bit_precision,
1446 ..
1447 } => *bit_offset != 0 || (*bit_precision as u32) < *size * 8,
1448 DatatypeMessage::FloatingPoint { .. } => !is_standard_ieee_float(dt),
1449 _ => false,
1450 }
1451}
1452
1453pub fn apply_datatype_conversion(buffer: &mut [u8], dt: &DatatypeMessage) -> FormatResult<()> {
1473 match dt {
1474 DatatypeMessage::FixedPoint {
1475 size,
1476 byte_order,
1477 signed,
1478 bit_offset,
1479 bit_precision,
1480 } => {
1481 let size = *size as usize;
1482 let precision = *bit_precision as usize;
1483 let offset = *bit_offset as usize;
1484
1485 if offset == 0 && precision == size * 8 {
1487 return Ok(());
1488 }
1489 if size == 0 || size > 8 {
1490 return Err(FormatError::InvalidData(format!(
1491 "datatype conversion: unsupported FixedPoint size {size}"
1492 )));
1493 }
1494 if precision == 0 || offset + precision > size * 8 {
1495 return Err(FormatError::InvalidData(format!(
1496 "datatype conversion: invalid bit layout (offset {offset}, \
1497 precision {precision}, size {size})"
1498 )));
1499 }
1500 if !buffer.len().is_multiple_of(size) {
1501 return Err(FormatError::InvalidData(format!(
1502 "datatype conversion: buffer length {} not a multiple of \
1503 element size {size}",
1504 buffer.len()
1505 )));
1506 }
1507
1508 let big_endian = matches!(byte_order, ByteOrder::BigEndian);
1509 let precision_mask: u64 = if precision == 64 {
1510 u64::MAX
1511 } else {
1512 (1u64 << precision) - 1
1513 };
1514 let sign_bit: u64 = 1u64 << (precision - 1);
1515
1516 for elem in buffer.chunks_exact_mut(size) {
1517 let mut raw: u64 = 0;
1519 if big_endian {
1520 for &b in elem.iter() {
1521 raw = (raw << 8) | b as u64;
1522 }
1523 } else {
1524 for (i, &b) in elem.iter().enumerate() {
1525 raw |= (b as u64) << (8 * i);
1526 }
1527 }
1528
1529 let mut value = (raw >> offset) & precision_mask;
1531
1532 if *signed && (value & sign_bit) != 0 {
1534 value |= !precision_mask;
1535 }
1536
1537 if big_endian {
1539 for i in 0..size {
1540 elem[size - 1 - i] = (value >> (8 * i)) as u8;
1541 }
1542 } else {
1543 for (i, b) in elem.iter_mut().enumerate() {
1544 *b = (value >> (8 * i)) as u8;
1545 }
1546 }
1547 }
1548 Ok(())
1549 }
1550 DatatypeMessage::FloatingPoint { .. } => {
1551 if is_standard_ieee_float(dt) {
1554 Ok(())
1555 } else {
1556 Err(FormatError::InvalidData(
1557 "datatype conversion: non-standard floating-point bit \
1558 layout cannot be converted"
1559 .into(),
1560 ))
1561 }
1562 }
1563 _ => Ok(()),
1564 }
1565}
1566
1567#[cfg(test)]
1571mod tests {
1572 use super::*;
1573
1574 fn nbit_atomic_cd(d_nelmts: u32, size: u32, precision: u32, offset: u32) -> Vec<u32> {
1576 let need_not_compress = if offset == 0 && precision == size * 8 {
1579 1
1580 } else {
1581 0
1582 };
1583 vec![
1584 8,
1585 need_not_compress,
1586 d_nelmts,
1587 NBIT_ATOMIC,
1588 size,
1589 NBIT_ORDER_LE,
1590 precision,
1591 offset,
1592 ]
1593 }
1594
1595 #[test]
1596 fn nbit_roundtrip_u16_precision12() {
1597 let values: Vec<u16> = (0..40u16).map(|i| (i * 71) & 0x0FFF).collect();
1599 let mut raw = Vec::new();
1600 for &v in &values {
1601 raw.extend_from_slice(&v.to_le_bytes());
1602 }
1603 let cd = nbit_atomic_cd(values.len() as u32, 2, 12, 0);
1604 let packed = apply_nbit(&raw, &cd, true).unwrap();
1605 assert!(packed.len() <= raw.len());
1606 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1607 assert_eq!(unpacked, raw);
1608 }
1609
1610 #[test]
1611 fn nbit_roundtrip_u32_precision20_offset4() {
1612 let values: Vec<u32> = (0..32u32).map(|i| ((i * 9999) & 0xFFFFF) << 4).collect();
1613 let mut raw = Vec::new();
1614 for &v in &values {
1615 raw.extend_from_slice(&v.to_le_bytes());
1616 }
1617 let cd = nbit_atomic_cd(values.len() as u32, 4, 20, 4);
1618 let packed = apply_nbit(&raw, &cd, true).unwrap();
1619 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1620 assert_eq!(unpacked, raw);
1621 }
1622
1623 #[test]
1624 fn nbit_passthrough_full_precision() {
1625 let raw: Vec<u8> = (0..64).collect();
1626 let cd = nbit_atomic_cd(16, 4, 32, 0); let packed = apply_nbit(&raw, &cd, true).unwrap();
1628 assert_eq!(packed, raw);
1629 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1630 assert_eq!(unpacked, raw);
1631 }
1632
1633 #[test]
1634 fn nbit_roundtrip_big_endian() {
1635 let values: Vec<u16> = (0..24u16).map(|i| (i * 53) & 0x03FF).collect();
1636 let mut raw = Vec::new();
1637 for &v in &values {
1638 raw.extend_from_slice(&v.to_be_bytes());
1639 }
1640 let mut cd = nbit_atomic_cd(values.len() as u32, 2, 10, 0);
1641 cd[5] = NBIT_ORDER_BE;
1642 let packed = apply_nbit(&raw, &cd, true).unwrap();
1643 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1644 assert_eq!(unpacked, raw);
1645 }
1646
1647 fn fixed(size: u32, signed: bool, offset: u16, precision: u16) -> DatatypeMessage {
1652 DatatypeMessage::FixedPoint {
1653 size,
1654 byte_order: ByteOrder::LittleEndian,
1655 signed,
1656 bit_offset: offset,
1657 bit_precision: precision,
1658 }
1659 }
1660
1661 #[test]
1662 fn conversion_noop_for_full_width_types() {
1663 let dt = fixed(4, false, 0, 32);
1665 assert!(!datatype_needs_bit_conversion(&dt));
1666 let mut buf = vec![0x78, 0x56, 0x34, 0x12, 0xFF, 0xFF, 0xFF, 0xFF];
1667 let before = buf.clone();
1668 apply_datatype_conversion(&mut buf, &dt).unwrap();
1669 assert_eq!(buf, before);
1670 }
1671
1672 #[test]
1673 fn conversion_noop_for_non_numeric_types() {
1674 let dt = DatatypeMessage::fixed_string(8);
1675 assert!(!datatype_needs_bit_conversion(&dt));
1676 let mut buf = b"hello!!\0".to_vec();
1677 let before = buf.clone();
1678 apply_datatype_conversion(&mut buf, &dt).unwrap();
1679 assert_eq!(buf, before);
1680 }
1681
1682 #[test]
1683 fn conversion_unsigned_offset_shifts_right() {
1684 let dt = fixed(2, false, 3, 10);
1688 assert!(datatype_needs_bit_conversion(&dt));
1689 let mut buf = (0x1528u16).to_le_bytes().to_vec();
1690 apply_datatype_conversion(&mut buf, &dt).unwrap();
1691 assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0x2A5);
1692 }
1693
1694 #[test]
1695 fn conversion_signed_negative_sign_extends() {
1696 let dt = fixed(2, true, 4, 8);
1699 let mut buf = (0x0FD0u16).to_le_bytes().to_vec();
1700 apply_datatype_conversion(&mut buf, &dt).unwrap();
1701 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), -3);
1702 }
1703
1704 #[test]
1705 fn conversion_signed_positive_stays_positive() {
1706 let dt = fixed(2, true, 4, 8);
1708 let mut buf = (0x0050u16).to_le_bytes().to_vec();
1709 apply_datatype_conversion(&mut buf, &dt).unwrap();
1710 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), 5);
1711 }
1712
1713 #[test]
1714 fn conversion_reduced_precision_offset_zero() {
1715 let dt = fixed(4, true, 0, 20);
1718 assert!(datatype_needs_bit_conversion(&dt));
1719 let mut buf = (0x000FFFFFu32).to_le_bytes().to_vec();
1720 apply_datatype_conversion(&mut buf, &dt).unwrap();
1721 assert_eq!(i32::from_le_bytes(buf.clone().try_into().unwrap()), -1);
1722 }
1723
1724 #[test]
1725 fn conversion_big_endian_signed() {
1726 let dt = DatatypeMessage::FixedPoint {
1728 size: 2,
1729 byte_order: ByteOrder::BigEndian,
1730 signed: true,
1731 bit_offset: 4,
1732 bit_precision: 8,
1733 };
1734 let mut buf = (0x0FD0u16).to_be_bytes().to_vec();
1735 apply_datatype_conversion(&mut buf, &dt).unwrap();
1736 assert_eq!(i16::from_be_bytes([buf[0], buf[1]]), -3);
1737 }
1738
1739 #[test]
1740 fn conversion_multiple_elements() {
1741 let dt = fixed(4, false, 5, 16);
1743 let vals: [u32; 3] = [0x1234, 0xABCD, 0x0001];
1744 let mut buf = Vec::new();
1745 for v in vals {
1746 buf.extend_from_slice(&(v << 5).to_le_bytes());
1747 }
1748 apply_datatype_conversion(&mut buf, &dt).unwrap();
1749 for (i, v) in vals.iter().enumerate() {
1750 let e = u32::from_le_bytes(buf[i * 4..i * 4 + 4].try_into().unwrap());
1751 assert_eq!(e, *v);
1752 }
1753 }
1754
1755 #[test]
1756 fn conversion_rejects_non_standard_float() {
1757 let dt = DatatypeMessage::FloatingPoint {
1759 size: 4,
1760 byte_order: ByteOrder::LittleEndian,
1761 sign_location: 30,
1762 bit_offset: 1,
1763 bit_precision: 31,
1764 exponent_location: 22,
1765 exponent_size: 8,
1766 mantissa_location: 0,
1767 mantissa_size: 22,
1768 exponent_bias: 127,
1769 };
1770 assert!(datatype_needs_bit_conversion(&dt));
1771 let mut buf = vec![0u8; 4];
1772 assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1773 }
1774
1775 #[test]
1776 fn conversion_standard_float_is_noop() {
1777 let dt = DatatypeMessage::f64_type();
1778 assert!(!datatype_needs_bit_conversion(&dt));
1779 let mut buf = 12.5f64.to_le_bytes().to_vec();
1780 let before = buf.clone();
1781 apply_datatype_conversion(&mut buf, &dt).unwrap();
1782 assert_eq!(buf, before);
1783 }
1784
1785 #[test]
1786 fn conversion_rejects_bad_buffer_length() {
1787 let dt = fixed(4, false, 3, 16);
1788 let mut buf = vec![0u8; 5]; assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1790 }
1791}