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> {
43 buf: &'a mut [u8],
44 j: usize,
45 acc: u64,
46 nacc: u32,
47 overrun: bool,
48}
49
50impl<'a> BitWriter<'a> {
51 fn new(buf: &'a mut [u8]) -> Self {
52 Self {
53 buf,
54 j: 0,
55 acc: 0,
56 nacc: 0,
57 overrun: false,
58 }
59 }
60
61 #[inline]
62 fn store(&mut self, b: u8) {
63 match self.buf.get_mut(self.j) {
64 Some(slot) => *slot = b,
65 None => self.overrun = true,
66 }
67 self.j += 1;
68 }
69
70 #[inline]
72 fn put(&mut self, v: u64, n: u32) {
73 if n > 32 {
74 self.put_half(v >> 32, n - 32);
75 self.put_half(v, 32);
76 } else {
77 self.put_half(v, n);
78 }
79 }
80
81 #[inline]
83 fn put_half(&mut self, v: u64, n: u32) {
84 self.acc = (self.acc << n) | (v & mask_u64(n as usize));
85 self.nacc += n;
86 while self.nacc >= 8 {
87 self.nacc -= 8;
88 self.store((self.acc >> self.nacc) as u8);
89 }
90 }
91
92 fn finish(mut self) -> FormatResult<usize> {
96 if self.nacc > 0 {
97 let b = (self.acc << (8 - self.nacc)) as u8;
98 match self.buf.get_mut(self.j) {
99 Some(slot) => *slot = b,
100 None => self.overrun = true,
101 }
102 }
103 if self.overrun {
104 return Err(FormatError::InvalidData(
105 "packed stream longer than the buffer it was sized for".into(),
106 ));
107 }
108 Ok(self.j)
109 }
110}
111
112struct BitReader<'a> {
118 buf: &'a [u8],
119 j: usize,
120 acc: u64,
121 nacc: u32,
122 short: &'static str,
123}
124
125impl<'a> BitReader<'a> {
126 fn new(buf: &'a [u8], short: &'static str) -> Self {
127 Self {
128 buf,
129 j: 0,
130 acc: 0,
131 nacc: 0,
132 short,
133 }
134 }
135
136 #[inline]
138 fn get(&mut self, n: u32) -> FormatResult<u64> {
139 if n > 32 {
140 let hi = self.get_half(n - 32)?;
141 let lo = self.get_half(32)?;
142 Ok((hi << 32) | lo)
143 } else {
144 self.get_half(n)
145 }
146 }
147
148 #[inline]
150 fn get_half(&mut self, n: u32) -> FormatResult<u64> {
151 while self.nacc < n {
152 let Some(&b) = self.buf.get(self.j) else {
153 return Err(FormatError::InvalidData(self.short.into()));
154 };
155 self.acc = (self.acc << 8) | u64::from(b);
156 self.j += 1;
157 self.nacc += 8;
158 }
159 self.nacc -= n;
160 Ok((self.acc >> self.nacc) & mask_u64(n as usize))
161 }
162}
163
164const NBIT_SHORT: &str = "nbit: buffer too short";
165
166fn nbit_bytes(p: &NbitAtomic) -> impl Iterator<Item = (usize, u32, u32)> {
174 let len = p.size * 8;
175 let top = p.precision + p.offset;
176 let (begin, end, step): (i64, i64, i64) = if p.order == NBIT_ORDER_LE {
177 let begin = if top.is_multiple_of(8) {
178 top / 8 - 1
179 } else {
180 top / 8
181 };
182 (i64::from(begin), i64::from(p.offset / 8), -1)
183 } else {
184 let end = if p.offset.is_multiple_of(8) {
185 (len - p.offset) / 8 - 1
186 } else {
187 (len - p.offset) / 8
188 };
189 (i64::from((len - top) / 8), i64::from(end), 1)
190 };
191 let p = *p;
192 std::iter::successors(Some(begin), move |&k| (k != end).then(|| k + step)).map(move |k| {
193 let (bits, shift) = if begin == end {
194 (p.precision, p.offset % 8)
195 } else if k == begin {
196 (8 - (len - top) % 8, 0)
197 } else if k == end {
198 let bits = 8 - p.offset % 8;
199 (bits, 8 - bits)
200 } else {
201 (8, 0)
202 };
203 (k as usize, bits, shift)
204 })
205}
206
207#[inline]
210fn load_uint<const N: usize>(bytes: [u8; N], le: bool) -> u64 {
211 let mut padded = [0u8; 8];
212 if le {
213 padded[..N].copy_from_slice(&bytes);
214 u64::from_le_bytes(padded)
215 } else {
216 padded[8 - N..].copy_from_slice(&bytes);
217 u64::from_be_bytes(padded)
218 }
219}
220
221#[inline]
223fn store_uint<const N: usize>(v: u64, le: bool) -> [u8; N] {
224 let mut out = [0u8; N];
225 if le {
226 out.copy_from_slice(&v.to_le_bytes()[..N]);
227 } else {
228 out.copy_from_slice(&v.to_be_bytes()[8 - N..]);
229 }
230 out
231}
232
233macro_rules! by_width {
237 ($size:expr, $f:ident($($arg:expr),* $(,)?)) => {
238 match $size {
239 1 => $f::<1>($($arg),*),
240 2 => $f::<2>($($arg),*),
241 4 => $f::<4>($($arg),*),
242 8 => $f::<8>($($arg),*),
243 n => unreachable!("element width {n} is not 1, 2, 4 or 8"),
244 }
245 };
246}
247
248#[inline]
250fn nbit_field<const N: usize>(elem: &[u8], le: bool, offset: u32) -> u64 {
251 load_uint::<N>(elem.try_into().expect("elem is N bytes"), le) >> offset
252}
253
254#[inline]
256fn nbit_place<const N: usize>(elem: &mut [u8], le: bool, offset: u32, field: u64) {
257 elem.copy_from_slice(&store_uint::<N>(field << offset, le));
258}
259
260fn nbit_compress_atomics<const N: usize>(data: &[u8], w: &mut BitWriter, p: &NbitAtomic) {
264 let le = p.order == NBIT_ORDER_LE;
265 let (elems, _) = data.as_chunks::<N>();
266 for &e in elems {
267 w.put(load_uint(e, le) >> p.offset, p.precision);
268 }
269}
270
271fn nbit_decompress_atomics<const N: usize>(
273 out: &mut [u8],
274 r: &mut BitReader,
275 p: &NbitAtomic,
276) -> FormatResult<()> {
277 let le = p.order == NBIT_ORDER_LE;
278 let (elems, _) = out.as_chunks_mut::<N>();
279 for e in elems {
280 *e = store_uint(r.get(p.precision)? << p.offset, le);
281 }
282 Ok(())
283}
284
285struct Parms<'a> {
294 list: &'a [u32],
295 pos: usize,
296}
297
298impl<'a> Parms<'a> {
299 fn at(list: &'a [u32], pos: usize) -> Self {
300 Self { list, pos }
301 }
302
303 fn next(&mut self) -> FormatResult<u32> {
305 let v = *self
306 .list
307 .get(self.pos)
308 .ok_or_else(|| FormatError::InvalidData("nbit: parameter list truncated".into()))?;
309 self.pos += 1;
310 Ok(v)
311 }
312
313 fn position(&self) -> usize {
314 self.pos
315 }
316
317 fn seek(&mut self, pos: usize) {
320 self.pos = pos;
321 }
322}
323
324fn element(data: &[u8], offset: usize, size: u32) -> FormatResult<&[u8]> {
328 offset
329 .checked_add(size as usize)
330 .and_then(|end| data.get(offset..end))
331 .ok_or_else(|| FormatError::InvalidData("nbit: element extends past buffer".into()))
332}
333
334fn element_mut(data: &mut [u8], offset: usize, size: u32) -> FormatResult<&mut [u8]> {
336 offset
337 .checked_add(size as usize)
338 .and_then(|end| data.get_mut(offset..end))
339 .ok_or_else(|| FormatError::InvalidData("nbit: element extends past buffer".into()))
340}
341
342fn repeat_count(total_size: u32, base_size: u32) -> FormatResult<usize> {
346 if base_size == 0 {
347 return Err(FormatError::InvalidData(
348 "nbit: zero-sized array base type".into(),
349 ));
350 }
351 Ok((total_size / base_size) as usize)
352}
353
354fn nbit_decompress_one_nooptype(
356 data: &mut [u8],
357 data_offset: usize,
358 r: &mut BitReader,
359 size: u32,
360) -> FormatResult<()> {
361 for b in element_mut(data, data_offset, size)? {
362 *b = r.get(8)? as u8;
363 }
364 Ok(())
365}
366
367fn nbit_compress_one_nooptype(
369 data: &[u8],
370 data_offset: usize,
371 w: &mut BitWriter,
372 size: u32,
373) -> FormatResult<()> {
374 for &b in element(data, data_offset, size)? {
375 w.put(u64::from(b), 8);
376 }
377 Ok(())
378}
379
380fn nbit_decompress_one_atomic(
385 data: &mut [u8],
386 data_offset: usize,
387 r: &mut BitReader,
388 p: &NbitAtomic,
389) -> FormatResult<()> {
390 let elem = element_mut(data, data_offset, p.size)?;
391 if matches!(p.size, 1 | 2 | 4 | 8) {
392 let field = r.get(p.precision)?;
393 let le = p.order == NBIT_ORDER_LE;
394 by_width!(p.size, nbit_place(elem, le, p.offset, field));
395 return Ok(());
396 }
397 for (k, bits, shift) in nbit_bytes(p) {
398 elem[k] = (r.get(bits)? << shift) as u8;
399 }
400 Ok(())
401}
402
403fn nbit_compress_one_atomic(
405 data: &[u8],
406 data_offset: usize,
407 w: &mut BitWriter,
408 p: &NbitAtomic,
409) -> FormatResult<()> {
410 let elem = element(data, data_offset, p.size)?;
411 if matches!(p.size, 1 | 2 | 4 | 8) {
412 let le = p.order == NBIT_ORDER_LE;
413 w.put(
414 by_width!(p.size, nbit_field(elem, le, p.offset)),
415 p.precision,
416 );
417 return Ok(());
418 }
419 for (k, bits, shift) in nbit_bytes(p) {
420 w.put(u64::from(elem[k] >> shift), bits);
421 }
422 Ok(())
423}
424
425fn read_atomic(parms: &mut Parms) -> FormatResult<NbitAtomic> {
428 let p = NbitAtomic {
429 size: parms.next()?,
430 order: parms.next()?,
431 precision: parms.next()?,
432 offset: parms.next()?,
433 };
434 let bits = p.size.checked_mul(8);
437 let span = p.precision.checked_add(p.offset);
438 match (bits, span) {
439 (Some(bits), Some(span))
440 if p.size > 0 && p.precision > 0 && p.precision <= bits && span <= bits => {}
441 _ => {
442 return Err(FormatError::InvalidData(format!(
443 "nbit: invalid atomic datatype (size={}, precision={}, offset={})",
444 p.size, p.precision, p.offset
445 )));
446 }
447 }
448 Ok(p)
449}
450
451fn nbit_decompress_one_array(
453 data: &mut [u8],
454 data_offset: usize,
455 r: &mut BitReader,
456 parms: &mut Parms,
457) -> FormatResult<()> {
458 let total_size = parms.next()?;
459 let base_class = parms.next()?;
460
461 match base_class {
462 NBIT_ATOMIC => {
463 let p = read_atomic(parms)?;
464 let n = repeat_count(total_size, p.size)?;
465 for i in 0..n {
466 nbit_decompress_one_atomic(data, data_offset + i * p.size as usize, r, &p)?;
467 }
468 }
469 NBIT_ARRAY => {
470 let begin = parms.position();
471 let base_size = parms.next()?;
472 let n = repeat_count(total_size, base_size)?;
473 for i in 0..n {
474 parms.seek(begin);
475 nbit_decompress_one_array(data, data_offset + i * base_size as usize, r, parms)?;
476 }
477 }
478 NBIT_COMPOUND => {
479 let begin = parms.position();
480 let base_size = parms.next()?;
481 let n = repeat_count(total_size, base_size)?;
482 for i in 0..n {
483 parms.seek(begin);
484 nbit_decompress_one_compound(data, data_offset + i * base_size as usize, r, parms)?;
485 }
486 }
487 NBIT_NOOPTYPE => {
488 parms.next()?; nbit_decompress_one_nooptype(data, data_offset, r, total_size)?;
490 }
491 _ => {
492 return Err(FormatError::InvalidData(format!(
493 "nbit: bad base class {}",
494 base_class
495 )))
496 }
497 }
498 Ok(())
499}
500
501fn nbit_decompress_one_compound(
503 data: &mut [u8],
504 data_offset: usize,
505 r: &mut BitReader,
506 parms: &mut Parms,
507) -> FormatResult<()> {
508 parms.next()?; let nmembers = parms.next()?;
510
511 for _ in 0..nmembers {
512 let member_offset = parms.next()? as usize;
513 let member_class = parms.next()?;
514
515 match member_class {
516 NBIT_ATOMIC => {
517 let p = read_atomic(parms)?;
518 nbit_decompress_one_atomic(data, data_offset + member_offset, r, &p)?;
519 }
520 NBIT_ARRAY => {
521 nbit_decompress_one_array(data, data_offset + member_offset, r, parms)?;
522 }
523 NBIT_COMPOUND => {
524 nbit_decompress_one_compound(data, data_offset + member_offset, r, parms)?;
525 }
526 NBIT_NOOPTYPE => {
527 let size = parms.next()?;
528 nbit_decompress_one_nooptype(data, data_offset + member_offset, r, size)?;
529 }
530 _ => {
531 return Err(FormatError::InvalidData(format!(
532 "nbit: bad member class {}",
533 member_class
534 )))
535 }
536 }
537 }
538 Ok(())
539}
540
541fn nbit_compress_one_array(
543 data: &[u8],
544 data_offset: usize,
545 w: &mut BitWriter,
546 parms: &mut Parms,
547) -> FormatResult<()> {
548 let total_size = parms.next()?;
549 let base_class = parms.next()?;
550
551 match base_class {
552 NBIT_ATOMIC => {
553 let p = read_atomic(parms)?;
554 let n = repeat_count(total_size, p.size)?;
555 for i in 0..n {
556 nbit_compress_one_atomic(data, data_offset + i * p.size as usize, w, &p)?;
557 }
558 }
559 NBIT_ARRAY => {
560 let begin = parms.position();
561 let base_size = parms.next()?;
562 let n = repeat_count(total_size, base_size)?;
563 for i in 0..n {
564 parms.seek(begin);
565 nbit_compress_one_array(data, data_offset + i * base_size as usize, w, parms)?;
566 }
567 }
568 NBIT_COMPOUND => {
569 let begin = parms.position();
570 let base_size = parms.next()?;
571 let n = repeat_count(total_size, base_size)?;
572 for i in 0..n {
573 parms.seek(begin);
574 nbit_compress_one_compound(data, data_offset + i * base_size as usize, w, parms)?;
575 }
576 }
577 NBIT_NOOPTYPE => {
578 parms.next()?;
579 nbit_compress_one_nooptype(data, data_offset, w, total_size)?;
580 }
581 _ => {
582 return Err(FormatError::InvalidData(format!(
583 "nbit: bad base class {}",
584 base_class
585 )))
586 }
587 }
588 Ok(())
589}
590
591fn nbit_compress_one_compound(
593 data: &[u8],
594 data_offset: usize,
595 w: &mut BitWriter,
596 parms: &mut Parms,
597) -> FormatResult<()> {
598 parms.next()?;
599 let nmembers = parms.next()?;
600
601 for _ in 0..nmembers {
602 let member_offset = parms.next()? as usize;
603 let member_class = parms.next()?;
604
605 match member_class {
606 NBIT_ATOMIC => {
607 let p = read_atomic(parms)?;
608 nbit_compress_one_atomic(data, data_offset + member_offset, w, &p)?;
609 }
610 NBIT_ARRAY => {
611 nbit_compress_one_array(data, data_offset + member_offset, w, parms)?;
612 }
613 NBIT_COMPOUND => {
614 nbit_compress_one_compound(data, data_offset + member_offset, w, parms)?;
615 }
616 NBIT_NOOPTYPE => {
617 let size = parms.next()?;
618 nbit_compress_one_nooptype(data, data_offset + member_offset, w, size)?;
619 }
620 _ => {
621 return Err(FormatError::InvalidData(format!(
622 "nbit: bad member class {}",
623 member_class
624 )))
625 }
626 }
627 }
628 Ok(())
629}
630
631const NBIT_HEADER_NPARMS: usize = 5;
634
635pub fn apply_nbit(data: &[u8], cd_values: &[u32], compress: bool) -> FormatResult<Vec<u8>> {
649 if cd_values.len() < NBIT_HEADER_NPARMS {
650 return Err(FormatError::InvalidData("nbit: cd_values too short".into()));
651 }
652 if cd_values[0] as usize != cd_values.len() {
653 return Err(FormatError::InvalidData(format!(
654 "nbit: cd_values[0] names {} parameters but {} are stored",
655 cd_values[0],
656 cd_values.len()
657 )));
658 }
659 if cd_values[1] != 0 {
661 return Ok(data.to_vec());
662 }
663
664 let d_nelmts = cd_values[2] as usize;
665 let dtype_size = cd_values[4] as usize;
666 if dtype_size == 0 {
667 return Err(FormatError::InvalidData("nbit: zero datatype size".into()));
668 }
669 let unpacked_size = d_nelmts.checked_mul(dtype_size).ok_or_else(|| {
670 FormatError::InvalidData("nbit: (de)compression buffer size overflow".into())
671 })?;
672
673 if compress {
674 if data.len() != unpacked_size {
675 return Err(FormatError::InvalidData(format!(
676 "nbit: input size {} != expected {}",
677 data.len(),
678 unpacked_size
679 )));
680 }
681 let mut buffer = vec![0u8; unpacked_size + 1];
683 let mut w = BitWriter::new(&mut buffer);
684 match cd_values[3] {
685 NBIT_ATOMIC => {
686 let p = read_atomic(&mut Parms::at(cd_values, 4))?;
687 if matches!(p.size, 1 | 2 | 4 | 8) {
688 by_width!(p.size, nbit_compress_atomics(data, &mut w, &p));
689 } else {
690 for i in 0..d_nelmts {
691 nbit_compress_one_atomic(data, i * p.size as usize, &mut w, &p)?;
692 }
693 }
694 }
695 NBIT_ARRAY => {
696 for i in 0..d_nelmts {
697 let mut parms = Parms::at(cd_values, 4);
698 nbit_compress_one_array(data, i * dtype_size, &mut w, &mut parms)?;
699 }
700 }
701 NBIT_COMPOUND => {
702 for i in 0..d_nelmts {
703 let mut parms = Parms::at(cd_values, 4);
704 nbit_compress_one_compound(data, i * dtype_size, &mut w, &mut parms)?;
705 }
706 }
707 other => {
708 return Err(FormatError::InvalidData(format!(
709 "nbit: unsupported top class {}",
710 other
711 )))
712 }
713 }
714 let j = w.finish()?;
716 buffer.truncate(j + 1);
717 Ok(buffer)
718 } else {
719 let mut out = Vec::new();
722 out.try_reserve_exact(unpacked_size).map_err(|_| {
723 FormatError::InvalidData(format!(
724 "nbit: cannot allocate {unpacked_size} bytes for decompression"
725 ))
726 })?;
727 out.resize(unpacked_size, 0);
728 let mut r = BitReader::new(data, NBIT_SHORT);
729 match cd_values[3] {
730 NBIT_ATOMIC => {
731 let p = read_atomic(&mut Parms::at(cd_values, 4))?;
732 if matches!(p.size, 1 | 2 | 4 | 8) {
733 by_width!(p.size, nbit_decompress_atomics(&mut out, &mut r, &p))?;
734 } else {
735 for i in 0..d_nelmts {
736 nbit_decompress_one_atomic(&mut out, i * p.size as usize, &mut r, &p)?;
737 }
738 }
739 }
740 NBIT_ARRAY => {
741 for i in 0..d_nelmts {
742 let mut parms = Parms::at(cd_values, 4);
743 nbit_decompress_one_array(&mut out, i * dtype_size, &mut r, &mut parms)?;
744 }
745 }
746 NBIT_COMPOUND => {
747 for i in 0..d_nelmts {
748 let mut parms = Parms::at(cd_values, 4);
749 nbit_decompress_one_compound(&mut out, i * dtype_size, &mut r, &mut parms)?;
750 }
751 }
752 other => {
753 return Err(FormatError::InvalidData(format!(
754 "nbit: unsupported top class {}",
755 other
756 )))
757 }
758 }
759 Ok(out)
760 }
761}
762
763const SO_PARM_SCALETYPE: usize = 0;
769const SO_PARM_SCALEFACTOR: usize = 1;
770const SO_PARM_NELMTS: usize = 2;
771const SO_PARM_CLASS: usize = 3;
772const SO_PARM_SIZE: usize = 4;
773const SO_PARM_SIGN: usize = 5;
774const SO_PARM_ORDER: usize = 6;
775const SO_PARM_FILAVAIL: usize = 7;
776const SO_PARM_FILVAL: usize = 8;
778
779pub(crate) const SO_CLS_INTEGER: u32 = 0;
780pub(crate) const SO_CLS_FLOAT: u32 = 1;
781pub(crate) const SO_ORDER_LE: u32 = 0;
782const SO_FILL_DEFINED: u32 = 1;
783pub(crate) const SO_FLOAT_DSCALE: u32 = 0;
785pub(crate) const SO_INT: u32 = 2;
787pub(crate) const SO_SGN_NONE: u32 = 0;
789pub(crate) const SO_SGN_2: u32 = 1;
791pub(crate) const SO_ORDER_BE: u32 = 1;
793pub(crate) const SO_TOTAL_NPARMS: usize = 20;
795
796const SO_BUF_OFFSET: usize = 21;
798
799const SO_SHORT: &str = "scaleoffset: buffer too short";
800
801fn so_log2(num: u64) -> u32 {
803 let mut v = 0u32;
804 let mut lower_bound: u64 = 1;
805 let mut val = num;
806 while {
807 val >>= 1;
808 val != 0
809 } {
810 v += 1;
811 lower_bound <<= 1;
812 }
813 if num == lower_bound {
814 v
815 } else {
816 v + 1
817 }
818}
819
820#[derive(Clone, Copy)]
824struct SoParams {
825 scale_factor: i32,
826 d_nelmts: usize,
827 dtype_class: u32,
828 size: usize,
830 dtype_sign: u32,
831 order: u32,
832 fill_defined: bool,
833 filval: u64,
835}
836
837impl SoParams {
838 fn parse(cd_values: &[u32]) -> FormatResult<Self> {
839 if cd_values.len() < 8 {
840 return Err(FormatError::InvalidData(
841 "scaleoffset: cd_values too short".into(),
842 ));
843 }
844 let scale_type = cd_values[SO_PARM_SCALETYPE];
845 let dtype_class = cd_values[SO_PARM_CLASS];
846 let size = cd_values[SO_PARM_SIZE] as usize;
847 let fill_defined = cd_values[SO_PARM_FILAVAIL] == SO_FILL_DEFINED;
848
849 if !matches!(size, 1 | 2 | 4 | 8) {
852 return Err(FormatError::InvalidData(format!(
853 "scaleoffset: unsupported datatype size {}",
854 size
855 )));
856 }
857 if dtype_class == SO_CLS_FLOAT && scale_type != SO_FLOAT_DSCALE {
858 return Err(FormatError::UnsupportedFeature(
859 "scaleoffset E-scaling method is not supported".into(),
860 ));
861 }
862
863 let filval: u64 = if fill_defined {
870 let mut v: u64 = 0;
871 let n_cd = size.div_ceil(4);
872 if cd_values.len() < SO_PARM_FILVAL + n_cd {
873 return Err(FormatError::InvalidData(
874 "scaleoffset: cd_values missing fill value".into(),
875 ));
876 }
877 for (w, cd) in cd_values[SO_PARM_FILVAL..SO_PARM_FILVAL + n_cd]
878 .iter()
879 .enumerate()
880 {
881 v |= (*cd as u64) << (w * 32);
882 }
883 v & mask_u64(size * 8)
884 } else {
885 0
886 };
887
888 let mut scale_factor = cd_values[SO_PARM_SCALEFACTOR] as i32;
891 if dtype_class == SO_CLS_INTEGER && scale_factor < 0 {
892 scale_factor = 0;
893 }
894
895 Ok(Self {
896 scale_factor,
897 d_nelmts: cd_values[SO_PARM_NELMTS] as usize,
898 dtype_class,
899 size,
900 dtype_sign: cd_values[SO_PARM_SIGN],
901 order: cd_values[SO_PARM_ORDER],
902 fill_defined,
903 filval,
904 })
905 }
906
907 fn is_noop(&self) -> bool {
912 self.dtype_class == SO_CLS_INTEGER && self.scale_factor as usize == self.size * 8
913 }
914
915 fn dtype_len(&self) -> u32 {
917 (self.size * 8) as u32
918 }
919
920 fn width_mask(&self) -> u64 {
922 mask_u64(self.size * 8)
923 }
924}
925
926fn mask_u64(n: usize) -> u64 {
928 if n >= 64 {
929 u64::MAX
930 } else {
931 !(u64::MAX << n)
932 }
933}
934
935fn so_pack<const N: usize>(buf: &[u8], le: bool, minbits: u32, w: &mut BitWriter) {
937 let (elems, _) = buf.as_chunks::<N>();
938 for &elem in elems {
939 w.put(load_uint(elem, le), minbits);
940 }
941}
942
943fn so_unpack<const N: usize>(
945 out: &mut [u8],
946 le: bool,
947 minbits: u32,
948 r: &mut BitReader,
949) -> FormatResult<()> {
950 let (elems, _) = out.as_chunks_mut::<N>();
951 for elem in elems {
952 *elem = store_uint(r.get(minbits)?, le);
953 }
954 Ok(())
955}
956
957pub fn reverse_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
962 let p = SoParams::parse(cd_values)?;
963 let (d_nelmts, size, order) = (p.d_nelmts, p.size, p.order);
964 let size_out = d_nelmts * size;
965
966 if p.is_noop() {
967 if data.len() < size_out {
972 return Err(FormatError::InvalidData(SO_SHORT.into()));
973 }
974 return Ok(data[..size_out].to_vec());
975 }
976
977 if data.len() < SO_BUF_OFFSET {
979 return Err(FormatError::InvalidData(
980 "scaleoffset: buffer too short for header".into(),
981 ));
982 }
983 let mut minbits: u32 = 0;
984 for (i, &b) in data[..4].iter().enumerate() {
985 minbits |= (b as u32) << (i * 8);
986 }
987 if minbits as usize > size * 8 {
988 return Err(FormatError::InvalidData(
989 "scaleoffset: minbits exceeds datatype size".into(),
990 ));
991 }
992 let minval_size = std::cmp::min(8usize, data[4] as usize);
993 let mut minval: u64 = 0;
994 for i in 0..minval_size {
995 minval |= (data[5 + i] as u64) << (i * 8);
996 }
997
998 if minbits as usize == size * 8 {
1000 if data.len() < SO_BUF_OFFSET + size_out {
1001 return Err(FormatError::InvalidData(SO_SHORT.into()));
1002 }
1003 return Ok(data[SO_BUF_OFFSET..SO_BUF_OFFSET + size_out].to_vec());
1004 }
1005
1006 let mut out = vec![0u8; size_out];
1007
1008 if minbits != 0 {
1009 if data.len() < SO_BUF_OFFSET {
1010 return Err(FormatError::InvalidData(SO_SHORT.into()));
1011 }
1012 let mut r = BitReader::new(&data[SO_BUF_OFFSET..], SO_SHORT);
1013 let le = order == SO_ORDER_LE;
1014 by_width!(size, so_unpack(&mut out, le, minbits, &mut r))?;
1015 }
1016 postdecompress(&mut out, &p, minbits, minval);
1020
1021 Ok(out)
1022}
1023
1024pub fn forward_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
1033 let p = SoParams::parse(cd_values)?;
1034 let nbytes = p.d_nelmts * p.size;
1035 if data.len() != nbytes {
1036 return Err(FormatError::InvalidData(format!(
1037 "scaleoffset: chunk is {} bytes, but the filter parameters describe {} elements of \
1038 {} bytes",
1039 data.len(),
1040 p.d_nelmts,
1041 p.size
1042 )));
1043 }
1044 if p.is_noop() {
1045 return Ok(data.to_vec());
1046 }
1047 if p.dtype_class == SO_CLS_INTEGER && p.scale_factor as usize > p.size * 8 {
1048 return Err(FormatError::InvalidData(
1049 "scaleoffset: minimum number of bits exceeds the datatype".into(),
1050 ));
1051 }
1052
1053 let mut buf = data.to_vec();
1057 let (minbits, minval) = if p.dtype_class == SO_CLS_INTEGER {
1058 by_width!(p.size, precompress_int(&mut buf, &p))
1059 } else {
1060 precompress_float(&mut buf, &p)?
1061 };
1062 debug_assert!(minbits <= p.dtype_len());
1063
1064 let size_out = SO_BUF_OFFSET + nbytes * minbits as usize / (p.size * 8) + 1;
1067 let mut out = vec![0u8; size_out];
1068 out[..4].copy_from_slice(&minbits.to_le_bytes());
1069 out[4] = 8;
1073 out[5..13].copy_from_slice(&minval.to_le_bytes());
1074
1075 if minbits as usize == p.size * 8 {
1076 out.truncate(SO_BUF_OFFSET + nbytes);
1079 out[SO_BUF_OFFSET..].copy_from_slice(&buf);
1080 return Ok(out);
1081 }
1082 if minbits != 0 {
1083 let mut w = BitWriter::new(&mut out[SO_BUF_OFFSET..]);
1084 let le = p.order == SO_ORDER_LE;
1085 by_width!(p.size, so_pack(&buf, le, minbits, &mut w));
1086 w.finish()?;
1087 }
1088 Ok(out)
1091}
1092
1093fn precompress_int<const N: usize>(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1100 let signed = p.dtype_sign == SO_SGN_2;
1101 let le = p.order == SO_ORDER_LE;
1102 let width_mask = p.width_mask();
1103 let key = |raw: u64| -> i128 {
1106 if signed {
1107 i128::from(sign_extend::<N>(raw))
1108 } else {
1109 i128::from(raw)
1110 }
1111 };
1112 let (elems, _) = buf.as_chunks_mut::<N>();
1113
1114 let mut minbits = p.scale_factor as u32;
1115 let mut min: i128 = 0;
1116 let mut max: i128 = 0;
1117
1118 if p.fill_defined {
1119 let first = elems.iter().position(|&e| load_uint(e, le) != p.filval);
1121 if let Some(f) = first {
1122 min = key(load_uint(elems[f], le));
1123 max = min;
1124 for &e in &elems[f..] {
1125 let raw = load_uint(e, le);
1126 if raw == p.filval {
1127 continue;
1128 }
1129 let v = key(raw);
1130 max = max.max(v);
1131 min = min.min(v);
1132 }
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);
1140 }
1141 minbits = so_log2(span_minus_1 + 2);
1142 }
1143 if minbits != p.dtype_len() {
1144 let sentinel = mask_u64(minbits as usize);
1145 for e in elems.iter_mut() {
1146 let raw = load_uint(*e, le);
1147 let v = if raw == p.filval {
1148 sentinel
1149 } else {
1150 (key(raw) - min) as u64 & width_mask
1151 };
1152 *e = store_uint(v, le);
1153 }
1154 }
1155 } else {
1156 if let Some(&e0) = elems.first() {
1157 min = key(load_uint(e0, le));
1158 max = min;
1159 }
1160 for &e in elems.iter() {
1161 let v = key(load_uint(e, le));
1162 max = max.max(v);
1163 min = min.min(v);
1164 }
1165 if minbits == 0 {
1166 let span_minus_1 = (max - min) as u64;
1167 if span_minus_1 > width_mask - 2 {
1168 return (p.dtype_len(), 0);
1169 }
1170 minbits = so_log2(span_minus_1 + 1);
1171 }
1172 if minbits != p.dtype_len() {
1173 for e in elems.iter_mut() {
1174 let v = (key(load_uint(*e, le)) - min) as u64 & width_mask;
1175 *e = store_uint(v, le);
1176 }
1177 }
1178 }
1179
1180 (
1181 minbits,
1182 min as i64 as u64 & if signed { u64::MAX } else { width_mask },
1183 )
1184}
1185
1186trait SoFloat:
1191 Copy
1192 + PartialOrd
1193 + std::ops::Mul<Output = Self>
1194 + std::ops::Sub<Output = Self>
1195 + std::ops::Div<Output = Self>
1196 + std::ops::Add<Output = Self>
1197{
1198 const ZERO: Self;
1199 fn from_stored(v: u64) -> Self;
1200 fn to_stored(self) -> u64;
1201 fn widen(self) -> f64;
1202 fn narrow(v: f64) -> Self;
1204 fn from_int(v: i64) -> Self;
1206 fn pow(base: f64, exp: f64) -> Self;
1208 fn abs(self) -> Self;
1209 fn round(self) -> Self;
1211 fn lround(self) -> i64;
1213}
1214
1215impl SoFloat for f32 {
1216 const ZERO: Self = 0.0;
1217 fn from_stored(v: u64) -> Self {
1218 f32::from_bits(v as u32)
1219 }
1220 fn to_stored(self) -> u64 {
1221 self.to_bits() as u64
1222 }
1223 fn widen(self) -> f64 {
1224 self as f64
1225 }
1226 fn narrow(v: f64) -> Self {
1227 v as f32
1228 }
1229 fn from_int(v: i64) -> Self {
1230 v as f32
1231 }
1232 fn pow(base: f64, exp: f64) -> Self {
1233 (base as f32).powf(exp as f32)
1234 }
1235 fn abs(self) -> Self {
1236 f32::abs(self)
1237 }
1238 fn round(self) -> Self {
1239 f32::round(self)
1240 }
1241 fn lround(self) -> i64 {
1242 f32::round(self) as i64
1243 }
1244}
1245
1246impl SoFloat for f64 {
1247 const ZERO: Self = 0.0;
1248 fn from_stored(v: u64) -> Self {
1249 f64::from_bits(v)
1250 }
1251 fn to_stored(self) -> u64 {
1252 self.to_bits()
1253 }
1254 fn widen(self) -> f64 {
1255 self
1256 }
1257 fn narrow(v: f64) -> Self {
1258 v
1259 }
1260 fn from_int(v: i64) -> Self {
1261 v as f64
1262 }
1263 fn pow(base: f64, exp: f64) -> Self {
1264 base.powf(exp)
1265 }
1266 fn abs(self) -> Self {
1267 f64::abs(self)
1268 }
1269 fn round(self) -> Self {
1270 f64::round(self)
1271 }
1272 fn lround(self) -> i64 {
1273 f64::round(self) as i64
1274 }
1275}
1276
1277fn precompress_float(buf: &mut [u8], p: &SoParams) -> FormatResult<(u32, u64)> {
1285 match p.size {
1286 4 => Ok(precompress_float_typed::<f32, 4>(buf, p)),
1287 8 => Ok(precompress_float_typed::<f64, 8>(buf, p)),
1288 n => Err(FormatError::InvalidData(format!(
1289 "scaleoffset: no floating-point type of {n} bytes"
1290 ))),
1291 }
1292}
1293
1294fn precompress_float_typed<T: SoFloat, const N: usize>(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1295 let d_val = p.scale_factor as f64;
1296 let pow10 = T::pow(10.0, d_val);
1297 let filval = T::from_stored(p.filval);
1298 let le = p.order == SO_ORDER_LE;
1299 let get = |e: [u8; N]| T::from_stored(load_uint(e, le));
1300 let (elems, _) = buf.as_chunks_mut::<N>();
1301 let scan_epsilon = 10f64.powf(-d_val);
1307 let is_fill_scan = |v: T| (v - filval).widen().abs() < scan_epsilon;
1308 let modify_epsilon = T::pow(10.0, -d_val);
1309 let is_fill_modify = |v: T| (v - filval).abs() < modify_epsilon;
1310
1311 let mut min = T::ZERO;
1312 let mut max = T::ZERO;
1313 if p.fill_defined {
1314 if let Some(f) = elems.iter().position(|&e| !is_fill_scan(get(e))) {
1315 min = get(elems[f]);
1316 max = min;
1317 for &e in &elems[f..] {
1318 let v = get(e);
1319 if is_fill_scan(v) {
1320 continue;
1321 }
1322 if v > max {
1323 max = v;
1324 }
1325 if v < min {
1326 min = v;
1327 }
1328 }
1329 }
1330 } else if let Some(&e0) = elems.first() {
1331 min = get(e0);
1332 max = min;
1333 for &e in elems.iter() {
1334 let v = get(e);
1335 if v > max {
1336 max = v;
1337 }
1338 if v < min {
1339 min = v;
1340 }
1341 }
1342 }
1343
1344 let dtype_len = p.dtype_len();
1349 let scaled = max * pow10 - min * pow10;
1350 if scaled.round() > T::pow(2.0, (dtype_len - 1) as f64) {
1351 return (dtype_len, 0);
1352 }
1353 let span = scaled.lround() as u64 + 1;
1354 let minbits = if p.fill_defined {
1355 so_log2(span + 1)
1356 } else {
1357 so_log2(span)
1358 };
1359
1360 if minbits != dtype_len {
1361 let sentinel = mask_u64(minbits as usize);
1362 for e in elems.iter_mut() {
1363 let v = get(*e);
1364 let stored = if p.fill_defined && is_fill_modify(v) {
1365 sentinel
1366 } else {
1367 (v * pow10 - min * pow10).lround() as u64 & p.width_mask()
1368 };
1369 *e = store_uint(stored, le);
1370 }
1371 }
1372
1373 (minbits, min.to_stored())
1374}
1375
1376#[inline]
1378fn sign_extend<const N: usize>(v: u64) -> i64 {
1379 let shift = 64 - 8 * N as u32;
1380 ((v << shift) as i64) >> shift
1381}
1382
1383fn postdecompress(out: &mut [u8], p: &SoParams, minbits: u32, minval: u64) {
1390 let sentinel = mask_u64(minbits as usize);
1393 if p.dtype_class == SO_CLS_INTEGER {
1394 by_width!(p.size, postdecompress_int(out, p, sentinel, minval));
1395 } else {
1396 match p.size {
1399 4 => postdecompress_float::<f32, 4>(out, p, sentinel, minval),
1400 8 => postdecompress_float::<f64, 8>(out, p, sentinel, minval),
1401 _ => {}
1402 }
1403 }
1404}
1405
1406fn postdecompress_int<const N: usize>(out: &mut [u8], p: &SoParams, sentinel: u64, minval: u64) {
1409 let le = p.order == SO_ORDER_LE;
1410 let width_mask = p.width_mask();
1411 let (elems, _) = out.as_chunks_mut::<N>();
1412 for e in elems {
1413 let v = load_uint(*e, le);
1414 let result = if p.fill_defined && v == sentinel {
1415 p.filval
1416 } else {
1417 v.wrapping_add(minval) & width_mask
1418 };
1419 *e = store_uint(result, le);
1420 }
1421}
1422
1423fn postdecompress_float<T: SoFloat, const N: usize>(
1424 out: &mut [u8],
1425 p: &SoParams,
1426 sentinel: u64,
1427 minval: u64,
1428) {
1429 let le = p.order == SO_ORDER_LE;
1430 let divisor = T::narrow(10f64.powf(p.scale_factor as f64));
1431 let min = T::from_stored(minval);
1432 let filval = T::from_stored(p.filval);
1433 let (elems, _) = out.as_chunks_mut::<N>();
1434 for e in elems {
1435 let raw = load_uint(*e, le);
1436 let val = if p.fill_defined && raw == sentinel {
1437 filval
1438 } else {
1439 T::from_int(sign_extend::<N>(raw)) / divisor + min
1440 };
1441 *e = store_uint(val.to_stored(), le);
1442 }
1443}
1444
1445use crate::format::messages::datatype::{ByteOrder, DatatypeMessage};
1450
1451fn is_standard_ieee_float(dt: &DatatypeMessage) -> bool {
1455 dt.ieee_format().is_some()
1456}
1457
1458pub fn datatype_needs_bit_conversion(dt: &DatatypeMessage) -> bool {
1472 match dt {
1473 DatatypeMessage::FixedPoint {
1474 size,
1475 bit_offset,
1476 bit_precision,
1477 ..
1478 } => *bit_offset != 0 || (*bit_precision as u32) < *size * 8,
1479 DatatypeMessage::FloatingPoint { .. } => !is_standard_ieee_float(dt),
1480 _ => false,
1481 }
1482}
1483
1484pub fn apply_datatype_conversion(buffer: &mut [u8], dt: &DatatypeMessage) -> FormatResult<()> {
1504 match dt {
1505 DatatypeMessage::FixedPoint {
1506 size,
1507 byte_order,
1508 signed,
1509 bit_offset,
1510 bit_precision,
1511 } => {
1512 let size = *size as usize;
1513 let precision = *bit_precision as usize;
1514 let offset = *bit_offset as usize;
1515
1516 if offset == 0 && precision == size * 8 {
1518 return Ok(());
1519 }
1520 if size == 0 || size > 8 {
1521 return Err(FormatError::InvalidData(format!(
1522 "datatype conversion: unsupported FixedPoint size {size}"
1523 )));
1524 }
1525 if precision == 0 || offset + precision > size * 8 {
1526 return Err(FormatError::InvalidData(format!(
1527 "datatype conversion: invalid bit layout (offset {offset}, \
1528 precision {precision}, size {size})"
1529 )));
1530 }
1531 if !buffer.len().is_multiple_of(size) {
1532 return Err(FormatError::InvalidData(format!(
1533 "datatype conversion: buffer length {} not a multiple of \
1534 element size {size}",
1535 buffer.len()
1536 )));
1537 }
1538
1539 let big_endian = matches!(byte_order, ByteOrder::BigEndian);
1540 let precision_mask: u64 = if precision == 64 {
1541 u64::MAX
1542 } else {
1543 (1u64 << precision) - 1
1544 };
1545 let sign_bit: u64 = 1u64 << (precision - 1);
1546
1547 for elem in buffer.chunks_exact_mut(size) {
1548 let mut raw: u64 = 0;
1550 if big_endian {
1551 for &b in elem.iter() {
1552 raw = (raw << 8) | b as u64;
1553 }
1554 } else {
1555 for (i, &b) in elem.iter().enumerate() {
1556 raw |= (b as u64) << (8 * i);
1557 }
1558 }
1559
1560 let mut value = (raw >> offset) & precision_mask;
1562
1563 if *signed && (value & sign_bit) != 0 {
1565 value |= !precision_mask;
1566 }
1567
1568 if big_endian {
1570 for i in 0..size {
1571 elem[size - 1 - i] = (value >> (8 * i)) as u8;
1572 }
1573 } else {
1574 for (i, b) in elem.iter_mut().enumerate() {
1575 *b = (value >> (8 * i)) as u8;
1576 }
1577 }
1578 }
1579 Ok(())
1580 }
1581 DatatypeMessage::FloatingPoint { .. } => {
1582 if is_standard_ieee_float(dt) {
1585 Ok(())
1586 } else {
1587 Err(FormatError::InvalidData(
1588 "datatype conversion: non-standard floating-point bit \
1589 layout cannot be converted"
1590 .into(),
1591 ))
1592 }
1593 }
1594 _ => Ok(()),
1595 }
1596}
1597
1598#[cfg(test)]
1602mod tests {
1603 use super::*;
1604
1605 fn nbit_atomic_cd(d_nelmts: u32, size: u32, precision: u32, offset: u32) -> Vec<u32> {
1607 let need_not_compress = if offset == 0 && precision == size * 8 {
1610 1
1611 } else {
1612 0
1613 };
1614 vec![
1615 8,
1616 need_not_compress,
1617 d_nelmts,
1618 NBIT_ATOMIC,
1619 size,
1620 NBIT_ORDER_LE,
1621 precision,
1622 offset,
1623 ]
1624 }
1625
1626 #[test]
1627 fn nbit_roundtrip_u16_precision12() {
1628 let values: Vec<u16> = (0..40u16).map(|i| (i * 71) & 0x0FFF).collect();
1630 let mut raw = Vec::new();
1631 for &v in &values {
1632 raw.extend_from_slice(&v.to_le_bytes());
1633 }
1634 let cd = nbit_atomic_cd(values.len() as u32, 2, 12, 0);
1635 let packed = apply_nbit(&raw, &cd, true).unwrap();
1636 assert!(packed.len() <= raw.len());
1637 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1638 assert_eq!(unpacked, raw);
1639 }
1640
1641 #[test]
1642 fn nbit_roundtrip_u32_precision20_offset4() {
1643 let values: Vec<u32> = (0..32u32).map(|i| ((i * 9999) & 0xFFFFF) << 4).collect();
1644 let mut raw = Vec::new();
1645 for &v in &values {
1646 raw.extend_from_slice(&v.to_le_bytes());
1647 }
1648 let cd = nbit_atomic_cd(values.len() as u32, 4, 20, 4);
1649 let packed = apply_nbit(&raw, &cd, true).unwrap();
1650 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1651 assert_eq!(unpacked, raw);
1652 }
1653
1654 #[test]
1655 fn nbit_passthrough_full_precision() {
1656 let raw: Vec<u8> = (0..64).collect();
1657 let cd = nbit_atomic_cd(16, 4, 32, 0); let packed = apply_nbit(&raw, &cd, true).unwrap();
1659 assert_eq!(packed, raw);
1660 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1661 assert_eq!(unpacked, raw);
1662 }
1663
1664 #[test]
1665 fn nbit_roundtrip_big_endian() {
1666 let values: Vec<u16> = (0..24u16).map(|i| (i * 53) & 0x03FF).collect();
1667 let mut raw = Vec::new();
1668 for &v in &values {
1669 raw.extend_from_slice(&v.to_be_bytes());
1670 }
1671 let mut cd = nbit_atomic_cd(values.len() as u32, 2, 10, 0);
1672 cd[5] = NBIT_ORDER_BE;
1673 let packed = apply_nbit(&raw, &cd, true).unwrap();
1674 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1675 assert_eq!(unpacked, raw);
1676 }
1677
1678 fn refusal(data: &[u8], cd: &[u32], compress: bool) -> String {
1688 match apply_nbit(data, cd, compress) {
1689 Ok(out) => panic!("accepted {cd:?}: {} bytes out", out.len()),
1690 Err(e) => e.to_string(),
1691 }
1692 }
1693
1694 #[test]
1695 fn a_list_without_the_datatype_size_is_refused() {
1696 let err = refusal(&[0; 8], &[4, 0, 1, NBIT_ATOMIC], false);
1698 assert!(err.contains("cd_values too short"), "{err}");
1699 }
1700
1701 #[test]
1702 fn a_count_that_disagrees_with_the_list_is_refused() {
1703 let mut cd = nbit_atomic_cd(1, 2, 12, 0);
1704 cd[0] = 9;
1705 let err = refusal(&[0; 2], &cd, false);
1706 assert!(err.contains("names 9 parameters but 8"), "{err}");
1707 }
1708
1709 #[test]
1710 fn an_array_whose_base_lies_past_the_list_is_refused() {
1711 let cd = [6, 0, 1, NBIT_ARRAY, 8, NBIT_ARRAY];
1713 for compress in [false, true] {
1714 let err = refusal(&[0; 8], &cd, compress);
1715 assert!(err.contains("parameter list truncated"), "{err}");
1716 }
1717 }
1718
1719 #[test]
1720 fn an_array_over_a_zero_sized_base_is_refused() {
1721 let cd = [8, 0, 1, NBIT_ARRAY, 8, NBIT_COMPOUND, 0, 0];
1723 for compress in [false, true] {
1724 let err = refusal(&[0; 8], &cd, compress);
1725 assert!(err.contains("zero-sized array base type"), "{err}");
1726 }
1727 }
1728
1729 #[test]
1730 fn a_member_past_the_element_is_refused() {
1731 let cd = [12, 0, 1, NBIT_COMPOUND, 4, 1, 8, NBIT_ATOMIC, 4, 0, 32, 0];
1733 for compress in [false, true] {
1734 let err = refusal(&[0; 4], &cd, compress);
1735 assert!(err.contains("element extends past buffer"), "{err}");
1736 }
1737 }
1738
1739 #[test]
1740 fn a_tree_that_packs_more_bits_than_the_element_holds_is_refused() {
1741 let cd = [
1744 18,
1745 0,
1746 1,
1747 NBIT_COMPOUND,
1748 4,
1749 2,
1750 0,
1751 NBIT_ATOMIC,
1752 4,
1753 0,
1754 32,
1755 0,
1756 0,
1757 NBIT_ATOMIC,
1758 4,
1759 0,
1760 32,
1761 0,
1762 ];
1763 let err = refusal(&[0xAB; 4], &cd, true);
1764 assert!(err.contains("packed stream longer"), "{err}");
1765 }
1766
1767 #[test]
1768 fn an_element_count_no_machine_can_hold_is_refused() {
1769 let cd = [8, 0, u32::MAX, NBIT_ATOMIC, u32::MAX, NBIT_ORDER_LE, 1, 0];
1770 let err = refusal(&[0; 8], &cd, false);
1771 assert!(err.contains("cannot allocate"), "{err}");
1772 }
1773
1774 fn fixed(size: u32, signed: bool, offset: u16, precision: u16) -> DatatypeMessage {
1779 DatatypeMessage::FixedPoint {
1780 size,
1781 byte_order: ByteOrder::LittleEndian,
1782 signed,
1783 bit_offset: offset,
1784 bit_precision: precision,
1785 }
1786 }
1787
1788 #[test]
1789 fn conversion_noop_for_full_width_types() {
1790 let dt = fixed(4, false, 0, 32);
1792 assert!(!datatype_needs_bit_conversion(&dt));
1793 let mut buf = vec![0x78, 0x56, 0x34, 0x12, 0xFF, 0xFF, 0xFF, 0xFF];
1794 let before = buf.clone();
1795 apply_datatype_conversion(&mut buf, &dt).unwrap();
1796 assert_eq!(buf, before);
1797 }
1798
1799 #[test]
1800 fn conversion_noop_for_non_numeric_types() {
1801 let dt = DatatypeMessage::fixed_string(8);
1802 assert!(!datatype_needs_bit_conversion(&dt));
1803 let mut buf = b"hello!!\0".to_vec();
1804 let before = buf.clone();
1805 apply_datatype_conversion(&mut buf, &dt).unwrap();
1806 assert_eq!(buf, before);
1807 }
1808
1809 #[test]
1810 fn conversion_unsigned_offset_shifts_right() {
1811 let dt = fixed(2, false, 3, 10);
1815 assert!(datatype_needs_bit_conversion(&dt));
1816 let mut buf = (0x1528u16).to_le_bytes().to_vec();
1817 apply_datatype_conversion(&mut buf, &dt).unwrap();
1818 assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0x2A5);
1819 }
1820
1821 #[test]
1822 fn conversion_signed_negative_sign_extends() {
1823 let dt = fixed(2, true, 4, 8);
1826 let mut buf = (0x0FD0u16).to_le_bytes().to_vec();
1827 apply_datatype_conversion(&mut buf, &dt).unwrap();
1828 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), -3);
1829 }
1830
1831 #[test]
1832 fn conversion_signed_positive_stays_positive() {
1833 let dt = fixed(2, true, 4, 8);
1835 let mut buf = (0x0050u16).to_le_bytes().to_vec();
1836 apply_datatype_conversion(&mut buf, &dt).unwrap();
1837 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), 5);
1838 }
1839
1840 #[test]
1841 fn conversion_reduced_precision_offset_zero() {
1842 let dt = fixed(4, true, 0, 20);
1845 assert!(datatype_needs_bit_conversion(&dt));
1846 let mut buf = (0x000FFFFFu32).to_le_bytes().to_vec();
1847 apply_datatype_conversion(&mut buf, &dt).unwrap();
1848 assert_eq!(i32::from_le_bytes(buf.clone().try_into().unwrap()), -1);
1849 }
1850
1851 #[test]
1852 fn conversion_big_endian_signed() {
1853 let dt = DatatypeMessage::FixedPoint {
1855 size: 2,
1856 byte_order: ByteOrder::BigEndian,
1857 signed: true,
1858 bit_offset: 4,
1859 bit_precision: 8,
1860 };
1861 let mut buf = (0x0FD0u16).to_be_bytes().to_vec();
1862 apply_datatype_conversion(&mut buf, &dt).unwrap();
1863 assert_eq!(i16::from_be_bytes([buf[0], buf[1]]), -3);
1864 }
1865
1866 #[test]
1867 fn conversion_multiple_elements() {
1868 let dt = fixed(4, false, 5, 16);
1870 let vals: [u32; 3] = [0x1234, 0xABCD, 0x0001];
1871 let mut buf = Vec::new();
1872 for v in vals {
1873 buf.extend_from_slice(&(v << 5).to_le_bytes());
1874 }
1875 apply_datatype_conversion(&mut buf, &dt).unwrap();
1876 for (i, v) in vals.iter().enumerate() {
1877 let e = u32::from_le_bytes(buf[i * 4..i * 4 + 4].try_into().unwrap());
1878 assert_eq!(e, *v);
1879 }
1880 }
1881
1882 #[test]
1883 fn conversion_rejects_non_standard_float() {
1884 let dt = DatatypeMessage::FloatingPoint {
1886 size: 4,
1887 byte_order: ByteOrder::LittleEndian,
1888 sign_location: 30,
1889 bit_offset: 1,
1890 bit_precision: 31,
1891 exponent_location: 22,
1892 exponent_size: 8,
1893 mantissa_location: 0,
1894 mantissa_size: 22,
1895 exponent_bias: 127,
1896 };
1897 assert!(datatype_needs_bit_conversion(&dt));
1898 let mut buf = vec![0u8; 4];
1899 assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1900 }
1901
1902 #[test]
1903 fn conversion_standard_float_is_noop() {
1904 let dt = DatatypeMessage::f64_type();
1905 assert!(!datatype_needs_bit_conversion(&dt));
1906 let mut buf = 12.5f64.to_le_bytes().to_vec();
1907 let before = buf.clone();
1908 apply_datatype_conversion(&mut buf, &dt).unwrap();
1909 assert_eq!(buf, before);
1910 }
1911
1912 #[test]
1913 fn conversion_rejects_bad_buffer_length() {
1914 let dt = fixed(4, false, 3, 16);
1915 let mut buf = vec![0u8; 5]; assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1917 }
1918}