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 NbitCursor {
34 j: usize,
35 buf_len: usize,
36}
37
38impl NbitCursor {
39 fn next_byte(&mut self) {
40 self.j += 1;
41 self.buf_len = 8;
42 }
43}
44
45fn mask_u32(n: usize) -> u32 {
47 if n >= 32 {
48 u32::MAX
49 } else {
50 !(u32::MAX << n)
51 }
52}
53
54#[derive(Clone, Copy)]
59struct NbitByteRange<'p> {
60 data_offset: usize,
61 begin_i: u32,
62 end_i: u32,
63 p: &'p NbitAtomic,
64 datatype_len: u32,
65}
66
67fn nbit_decompress_one_byte(
69 data: &mut [u8],
70 k: u32,
71 buffer: &[u8],
72 cur: &mut NbitCursor,
73 range: &NbitByteRange,
74) -> FormatResult<()> {
75 let NbitByteRange {
76 data_offset,
77 begin_i,
78 end_i,
79 p,
80 datatype_len,
81 } = *range;
82 if cur.j >= buffer.len() {
83 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
84 }
85 let mut val = buffer[cur.j];
86 let mut dat_offset: usize = 0;
87 let mut dat_len: usize;
88
89 if begin_i != end_i {
90 if k == begin_i {
91 dat_len = 8 - ((datatype_len - p.precision - p.offset) % 8) as usize;
92 } else if k == end_i {
93 dat_len = 8 - (p.offset % 8) as usize;
94 dat_offset = 8 - dat_len;
95 } else {
96 dat_len = 8;
97 }
98 } else {
99 dat_offset = (p.offset % 8) as usize;
100 dat_len = p.precision as usize;
101 }
102
103 let idx = data_offset + k as usize;
104 if cur.buf_len > dat_len {
105 data[idx] =
106 (((val >> (cur.buf_len - dat_len)) as u32 & mask_u32(dat_len)) << dat_offset) as u8;
107 cur.buf_len -= dat_len;
108 } else {
109 data[idx] =
110 (((val as u32 & mask_u32(cur.buf_len)) << (dat_len - cur.buf_len)) << dat_offset) as u8;
111 dat_len -= cur.buf_len;
112 cur.next_byte();
113 if dat_len == 0 {
114 return Ok(());
115 }
116 if cur.j >= buffer.len() {
117 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
118 }
119 val = buffer[cur.j];
120 data[idx] |=
121 (((val >> (cur.buf_len - dat_len)) as u32 & mask_u32(dat_len)) << dat_offset) as u8;
122 cur.buf_len -= dat_len;
123 }
124 Ok(())
125}
126
127fn nbit_compress_one_byte(
129 data: &[u8],
130 k: u32,
131 buffer: &mut [u8],
132 cur: &mut NbitCursor,
133 range: &NbitByteRange,
134) {
135 let NbitByteRange {
136 data_offset,
137 begin_i,
138 end_i,
139 p,
140 datatype_len,
141 } = *range;
142 let mut val = data[data_offset + k as usize];
143 let mut dat_len: usize;
144
145 if begin_i != end_i {
146 if k == begin_i {
147 dat_len = 8 - ((datatype_len - p.precision - p.offset) % 8) as usize;
148 } else if k == end_i {
149 dat_len = 8 - (p.offset % 8) as usize;
150 val >>= 8 - dat_len;
151 } else {
152 dat_len = 8;
153 }
154 } else {
155 val >>= p.offset % 8;
156 dat_len = p.precision as usize;
157 }
158
159 if cur.buf_len > dat_len {
160 buffer[cur.j] |= ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
161 cur.buf_len -= dat_len;
162 } else {
163 buffer[cur.j] |= ((val as u32 >> (dat_len - cur.buf_len)) & mask_u32(cur.buf_len)) as u8;
164 dat_len -= cur.buf_len;
165 cur.next_byte();
166 if dat_len == 0 {
167 return;
168 }
169 buffer[cur.j] = ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
170 cur.buf_len -= dat_len;
171 }
172}
173
174fn nbit_decompress_one_nooptype(
176 data: &mut [u8],
177 data_offset: usize,
178 buffer: &[u8],
179 cur: &mut NbitCursor,
180 size: u32,
181) -> FormatResult<()> {
182 for i in 0..size as usize {
183 if cur.j >= buffer.len() {
184 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
185 }
186 let mut val = buffer[cur.j];
187 let mut dat_len: usize = 8;
188 data[data_offset + i] =
189 ((val as u32 & mask_u32(cur.buf_len)) << (dat_len - cur.buf_len)) as u8;
190 dat_len -= cur.buf_len;
191 cur.next_byte();
192 if dat_len == 0 {
193 continue;
194 }
195 if cur.j >= buffer.len() {
196 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
197 }
198 val = buffer[cur.j];
199 data[data_offset + i] |=
200 ((val >> (cur.buf_len - dat_len)) as u32 & mask_u32(dat_len)) as u8;
201 cur.buf_len -= dat_len;
202 }
203 Ok(())
204}
205
206fn nbit_compress_one_nooptype(
208 data: &[u8],
209 data_offset: usize,
210 buffer: &mut [u8],
211 cur: &mut NbitCursor,
212 size: u32,
213) {
214 for i in 0..size as usize {
215 let val = data[data_offset + i];
216 let mut dat_len: usize = 8;
217 buffer[cur.j] |= ((val as u32 >> (dat_len - cur.buf_len)) & mask_u32(cur.buf_len)) as u8;
218 dat_len -= cur.buf_len;
219 cur.next_byte();
220 if dat_len == 0 {
221 continue;
222 }
223 buffer[cur.j] = ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
224 cur.buf_len -= dat_len;
225 }
226}
227
228fn nbit_decompress_one_atomic(
230 data: &mut [u8],
231 data_offset: usize,
232 buffer: &[u8],
233 cur: &mut NbitCursor,
234 p: &NbitAtomic,
235) -> FormatResult<()> {
236 let datatype_len = p.size * 8;
237 if p.order == NBIT_ORDER_LE {
238 let begin_i = if !(p.precision + p.offset).is_multiple_of(8) {
239 (p.precision + p.offset) / 8
240 } else {
241 (p.precision + p.offset) / 8 - 1
242 };
243 let end_i = p.offset / 8;
244 let range = NbitByteRange {
245 data_offset,
246 begin_i,
247 end_i,
248 p,
249 datatype_len,
250 };
251 let mut k = begin_i as i64;
252 while k >= end_i as i64 {
253 nbit_decompress_one_byte(data, k as u32, buffer, cur, &range)?;
254 k -= 1;
255 }
256 } else {
257 let begin_i = (datatype_len - p.precision - p.offset) / 8;
258 let end_i = if !p.offset.is_multiple_of(8) {
259 (datatype_len - p.offset) / 8
260 } else {
261 (datatype_len - p.offset) / 8 - 1
262 };
263 let range = NbitByteRange {
264 data_offset,
265 begin_i,
266 end_i,
267 p,
268 datatype_len,
269 };
270 for k in begin_i..=end_i {
271 nbit_decompress_one_byte(data, k, buffer, cur, &range)?;
272 }
273 }
274 Ok(())
275}
276
277fn nbit_compress_one_atomic(
279 data: &[u8],
280 data_offset: usize,
281 buffer: &mut [u8],
282 cur: &mut NbitCursor,
283 p: &NbitAtomic,
284) {
285 let datatype_len = p.size * 8;
286 if p.order == NBIT_ORDER_LE {
287 let begin_i = if !(p.precision + p.offset).is_multiple_of(8) {
288 (p.precision + p.offset) / 8
289 } else {
290 (p.precision + p.offset) / 8 - 1
291 };
292 let end_i = p.offset / 8;
293 let range = NbitByteRange {
294 data_offset,
295 begin_i,
296 end_i,
297 p,
298 datatype_len,
299 };
300 let mut k = begin_i as i64;
301 while k >= end_i as i64 {
302 nbit_compress_one_byte(data, k as u32, buffer, cur, &range);
303 k -= 1;
304 }
305 } else {
306 let begin_i = (datatype_len - p.precision - p.offset) / 8;
307 let end_i = if !p.offset.is_multiple_of(8) {
308 (datatype_len - p.offset) / 8
309 } else {
310 (datatype_len - p.offset) / 8 - 1
311 };
312 let range = NbitByteRange {
313 data_offset,
314 begin_i,
315 end_i,
316 p,
317 datatype_len,
318 };
319 for k in begin_i..=end_i {
320 nbit_compress_one_byte(data, k, buffer, cur, &range);
321 }
322 }
323}
324
325fn read_atomic(parms: &[u32], idx: &mut usize) -> FormatResult<NbitAtomic> {
328 if *idx + 4 > parms.len() {
329 return Err(FormatError::InvalidData(
330 "nbit: parameter list truncated".into(),
331 ));
332 }
333 let p = NbitAtomic {
334 size: parms[*idx],
335 order: parms[*idx + 1],
336 precision: parms[*idx + 2],
337 offset: parms[*idx + 3],
338 };
339 *idx += 4;
340 let bits = p.size.checked_mul(8);
343 let span = p.precision.checked_add(p.offset);
344 match (bits, span) {
345 (Some(bits), Some(span))
346 if p.size > 0 && p.precision > 0 && p.precision <= bits && span <= bits => {}
347 _ => {
348 return Err(FormatError::InvalidData(format!(
349 "nbit: invalid atomic datatype (size={}, precision={}, offset={})",
350 p.size, p.precision, p.offset
351 )));
352 }
353 }
354 Ok(p)
355}
356
357fn nbit_decompress_one_array(
359 data: &mut [u8],
360 data_offset: usize,
361 buffer: &[u8],
362 cur: &mut NbitCursor,
363 parms: &[u32],
364 parms_index: &mut usize,
365) -> FormatResult<()> {
366 if *parms_index + 2 > parms.len() {
367 return Err(FormatError::InvalidData(
368 "nbit: parameter list truncated".into(),
369 ));
370 }
371 let total_size = parms[*parms_index];
372 let base_class = parms[*parms_index + 1];
373 *parms_index += 2;
374
375 match base_class {
376 NBIT_ATOMIC => {
377 let p = read_atomic(parms, parms_index)?;
378 let n = total_size / p.size;
379 for i in 0..n as usize {
380 nbit_decompress_one_atomic(
381 data,
382 data_offset + i * p.size as usize,
383 buffer,
384 cur,
385 &p,
386 )?;
387 }
388 }
389 NBIT_ARRAY => {
390 let base_size = parms[*parms_index];
391 let n = total_size / base_size;
392 let begin_index = *parms_index;
393 for i in 0..n as usize {
394 *parms_index = begin_index;
395 nbit_decompress_one_array(
396 data,
397 data_offset + i * base_size as usize,
398 buffer,
399 cur,
400 parms,
401 parms_index,
402 )?;
403 }
404 }
405 NBIT_COMPOUND => {
406 let base_size = parms[*parms_index];
407 let n = total_size / base_size;
408 let begin_index = *parms_index;
409 for i in 0..n as usize {
410 *parms_index = begin_index;
411 nbit_decompress_one_compound(
412 data,
413 data_offset + i * base_size as usize,
414 buffer,
415 cur,
416 parms,
417 parms_index,
418 )?;
419 }
420 }
421 NBIT_NOOPTYPE => {
422 *parms_index += 1; nbit_decompress_one_nooptype(data, data_offset, buffer, cur, total_size)?;
424 }
425 _ => {
426 return Err(FormatError::InvalidData(format!(
427 "nbit: bad base class {}",
428 base_class
429 )))
430 }
431 }
432 Ok(())
433}
434
435fn nbit_decompress_one_compound(
437 data: &mut [u8],
438 data_offset: usize,
439 buffer: &[u8],
440 cur: &mut NbitCursor,
441 parms: &[u32],
442 parms_index: &mut usize,
443) -> FormatResult<()> {
444 if *parms_index + 2 > parms.len() {
445 return Err(FormatError::InvalidData(
446 "nbit: parameter list truncated".into(),
447 ));
448 }
449 *parms_index += 1; let nmembers = parms[*parms_index];
451 *parms_index += 1;
452
453 for _ in 0..nmembers {
454 if *parms_index + 2 > parms.len() {
455 return Err(FormatError::InvalidData(
456 "nbit: parameter list truncated".into(),
457 ));
458 }
459 let member_offset = parms[*parms_index] as usize;
460 let member_class = parms[*parms_index + 1];
461 *parms_index += 2;
462
463 match member_class {
464 NBIT_ATOMIC => {
465 let p = read_atomic(parms, parms_index)?;
466 nbit_decompress_one_atomic(data, data_offset + member_offset, buffer, cur, &p)?;
467 }
468 NBIT_ARRAY => {
469 nbit_decompress_one_array(
470 data,
471 data_offset + member_offset,
472 buffer,
473 cur,
474 parms,
475 parms_index,
476 )?;
477 }
478 NBIT_COMPOUND => {
479 nbit_decompress_one_compound(
480 data,
481 data_offset + member_offset,
482 buffer,
483 cur,
484 parms,
485 parms_index,
486 )?;
487 }
488 NBIT_NOOPTYPE => {
489 let size = parms[*parms_index];
490 *parms_index += 1;
491 nbit_decompress_one_nooptype(data, data_offset + member_offset, buffer, cur, size)?;
492 }
493 _ => {
494 return Err(FormatError::InvalidData(format!(
495 "nbit: bad member class {}",
496 member_class
497 )))
498 }
499 }
500 }
501 Ok(())
502}
503
504fn nbit_compress_one_array(
506 data: &[u8],
507 data_offset: usize,
508 buffer: &mut [u8],
509 cur: &mut NbitCursor,
510 parms: &[u32],
511 parms_index: &mut usize,
512) -> FormatResult<()> {
513 if *parms_index + 2 > parms.len() {
514 return Err(FormatError::InvalidData(
515 "nbit: parameter list truncated".into(),
516 ));
517 }
518 let total_size = parms[*parms_index];
519 let base_class = parms[*parms_index + 1];
520 *parms_index += 2;
521
522 match base_class {
523 NBIT_ATOMIC => {
524 let p = read_atomic(parms, parms_index)?;
525 let n = total_size / p.size;
526 for i in 0..n as usize {
527 nbit_compress_one_atomic(data, data_offset + i * p.size as usize, buffer, cur, &p);
528 }
529 }
530 NBIT_ARRAY => {
531 let base_size = parms[*parms_index];
532 let n = total_size / base_size;
533 let begin_index = *parms_index;
534 for i in 0..n as usize {
535 *parms_index = begin_index;
536 nbit_compress_one_array(
537 data,
538 data_offset + i * base_size as usize,
539 buffer,
540 cur,
541 parms,
542 parms_index,
543 )?;
544 }
545 }
546 NBIT_COMPOUND => {
547 let base_size = parms[*parms_index];
548 let n = total_size / base_size;
549 let begin_index = *parms_index;
550 for i in 0..n as usize {
551 *parms_index = begin_index;
552 nbit_compress_one_compound(
553 data,
554 data_offset + i * base_size as usize,
555 buffer,
556 cur,
557 parms,
558 parms_index,
559 )?;
560 }
561 }
562 NBIT_NOOPTYPE => {
563 *parms_index += 1;
564 nbit_compress_one_nooptype(data, data_offset, buffer, cur, total_size);
565 }
566 _ => {
567 return Err(FormatError::InvalidData(format!(
568 "nbit: bad base class {}",
569 base_class
570 )))
571 }
572 }
573 Ok(())
574}
575
576fn nbit_compress_one_compound(
578 data: &[u8],
579 data_offset: usize,
580 buffer: &mut [u8],
581 cur: &mut NbitCursor,
582 parms: &[u32],
583 parms_index: &mut usize,
584) -> FormatResult<()> {
585 if *parms_index + 2 > parms.len() {
586 return Err(FormatError::InvalidData(
587 "nbit: parameter list truncated".into(),
588 ));
589 }
590 *parms_index += 1;
591 let nmembers = parms[*parms_index];
592 *parms_index += 1;
593
594 for _ in 0..nmembers {
595 if *parms_index + 2 > parms.len() {
596 return Err(FormatError::InvalidData(
597 "nbit: parameter list truncated".into(),
598 ));
599 }
600 let member_offset = parms[*parms_index] as usize;
601 let member_class = parms[*parms_index + 1];
602 *parms_index += 2;
603
604 match member_class {
605 NBIT_ATOMIC => {
606 let p = read_atomic(parms, parms_index)?;
607 nbit_compress_one_atomic(data, data_offset + member_offset, buffer, cur, &p);
608 }
609 NBIT_ARRAY => {
610 nbit_compress_one_array(
611 data,
612 data_offset + member_offset,
613 buffer,
614 cur,
615 parms,
616 parms_index,
617 )?;
618 }
619 NBIT_COMPOUND => {
620 nbit_compress_one_compound(
621 data,
622 data_offset + member_offset,
623 buffer,
624 cur,
625 parms,
626 parms_index,
627 )?;
628 }
629 NBIT_NOOPTYPE => {
630 let size = parms[*parms_index];
631 *parms_index += 1;
632 nbit_compress_one_nooptype(data, data_offset + member_offset, buffer, cur, size);
633 }
634 _ => {
635 return Err(FormatError::InvalidData(format!(
636 "nbit: bad member class {}",
637 member_class
638 )))
639 }
640 }
641 }
642 Ok(())
643}
644
645pub fn apply_nbit(data: &[u8], cd_values: &[u32], compress: bool) -> FormatResult<Vec<u8>> {
654 if cd_values.len() < 4 {
655 return Err(FormatError::InvalidData("nbit: cd_values too short".into()));
656 }
657 if cd_values[1] != 0 {
659 return Ok(data.to_vec());
660 }
661
662 let d_nelmts = cd_values[2] as usize;
663 let dtype_size = cd_values[4] as usize;
664 if dtype_size == 0 {
665 return Err(FormatError::InvalidData("nbit: zero datatype size".into()));
666 }
667 let unpacked_size = d_nelmts * dtype_size;
668
669 if compress {
670 if data.len() != unpacked_size {
671 return Err(FormatError::InvalidData(format!(
672 "nbit: input size {} != expected {}",
673 data.len(),
674 unpacked_size
675 )));
676 }
677 let mut buffer = vec![0u8; unpacked_size + 1];
679 let mut cur = NbitCursor { j: 0, buf_len: 8 };
680 match cd_values[3] {
681 NBIT_ATOMIC => {
682 let mut idx = 4;
683 let p = read_atomic(cd_values, &mut idx)?;
684 for i in 0..d_nelmts {
685 nbit_compress_one_atomic(data, i * p.size as usize, &mut buffer, &mut cur, &p);
686 }
687 }
688 NBIT_ARRAY => {
689 let size = cd_values[4] as usize;
690 for i in 0..d_nelmts {
691 let mut idx = 4;
692 nbit_compress_one_array(
693 data,
694 i * size,
695 &mut buffer,
696 &mut cur,
697 cd_values,
698 &mut idx,
699 )?;
700 }
701 }
702 NBIT_COMPOUND => {
703 let size = cd_values[4] as usize;
704 for i in 0..d_nelmts {
705 let mut idx = 4;
706 nbit_compress_one_compound(
707 data,
708 i * size,
709 &mut buffer,
710 &mut cur,
711 cd_values,
712 &mut idx,
713 )?;
714 }
715 }
716 other => {
717 return Err(FormatError::InvalidData(format!(
718 "nbit: unsupported top class {}",
719 other
720 )))
721 }
722 }
723 buffer.truncate(cur.j + 1);
725 Ok(buffer)
726 } else {
727 let mut out = vec![0u8; unpacked_size];
728 let mut cur = NbitCursor { j: 0, buf_len: 8 };
729 match cd_values[3] {
730 NBIT_ATOMIC => {
731 let mut idx = 4;
732 let p = read_atomic(cd_values, &mut idx)?;
733 if p.precision > p.size * 8 || p.precision + p.offset > p.size * 8 {
734 return Err(FormatError::InvalidData(
735 "nbit: invalid precision/offset".into(),
736 ));
737 }
738 for i in 0..d_nelmts {
739 nbit_decompress_one_atomic(&mut out, i * p.size as usize, data, &mut cur, &p)?;
740 }
741 }
742 NBIT_ARRAY => {
743 let size = cd_values[4] as usize;
744 for i in 0..d_nelmts {
745 let mut idx = 4;
746 nbit_decompress_one_array(
747 &mut out,
748 i * size,
749 data,
750 &mut cur,
751 cd_values,
752 &mut idx,
753 )?;
754 }
755 }
756 NBIT_COMPOUND => {
757 let size = cd_values[4] as usize;
758 for i in 0..d_nelmts {
759 let mut idx = 4;
760 nbit_decompress_one_compound(
761 &mut out,
762 i * size,
763 data,
764 &mut cur,
765 cd_values,
766 &mut idx,
767 )?;
768 }
769 }
770 other => {
771 return Err(FormatError::InvalidData(format!(
772 "nbit: unsupported top class {}",
773 other
774 )))
775 }
776 }
777 Ok(out)
778 }
779}
780
781const SO_PARM_SCALETYPE: usize = 0;
787const SO_PARM_SCALEFACTOR: usize = 1;
788const SO_PARM_NELMTS: usize = 2;
789const SO_PARM_CLASS: usize = 3;
790const SO_PARM_SIZE: usize = 4;
791const SO_PARM_SIGN: usize = 5;
792const SO_PARM_ORDER: usize = 6;
793const SO_PARM_FILAVAIL: usize = 7;
794const SO_PARM_FILVAL: usize = 8;
796
797pub(crate) const SO_CLS_INTEGER: u32 = 0;
798pub(crate) const SO_CLS_FLOAT: u32 = 1;
799pub(crate) const SO_ORDER_LE: u32 = 0;
800const SO_FILL_DEFINED: u32 = 1;
801pub(crate) const SO_FLOAT_DSCALE: u32 = 0;
803pub(crate) const SO_INT: u32 = 2;
805pub(crate) const SO_SGN_NONE: u32 = 0;
807pub(crate) const SO_SGN_2: u32 = 1;
809pub(crate) const SO_ORDER_BE: u32 = 1;
811pub(crate) const SO_TOTAL_NPARMS: usize = 20;
813
814const SO_BUF_OFFSET: usize = 21;
816
817fn so_log2(num: u64) -> u32 {
819 let mut v = 0u32;
820 let mut lower_bound: u64 = 1;
821 let mut val = num;
822 while {
823 val >>= 1;
824 val != 0
825 } {
826 v += 1;
827 lower_bound <<= 1;
828 }
829 if num == lower_bound {
830 v
831 } else {
832 v + 1
833 }
834}
835
836#[derive(Clone, Copy)]
840struct SoParams {
841 scale_factor: i32,
842 d_nelmts: usize,
843 dtype_class: u32,
844 size: usize,
846 dtype_sign: u32,
847 order: u32,
848 fill_defined: bool,
849 filval: u64,
851}
852
853impl SoParams {
854 fn parse(cd_values: &[u32]) -> FormatResult<Self> {
855 if cd_values.len() < 8 {
856 return Err(FormatError::InvalidData(
857 "scaleoffset: cd_values too short".into(),
858 ));
859 }
860 let scale_type = cd_values[SO_PARM_SCALETYPE];
861 let dtype_class = cd_values[SO_PARM_CLASS];
862 let size = cd_values[SO_PARM_SIZE] as usize;
863 let fill_defined = cd_values[SO_PARM_FILAVAIL] == SO_FILL_DEFINED;
864
865 if size == 0 || size > 8 {
866 return Err(FormatError::InvalidData(format!(
867 "scaleoffset: unsupported datatype size {}",
868 size
869 )));
870 }
871 if dtype_class == SO_CLS_FLOAT && scale_type != SO_FLOAT_DSCALE {
872 return Err(FormatError::UnsupportedFeature(
873 "scaleoffset E-scaling method is not supported".into(),
874 ));
875 }
876
877 let filval: u64 = if fill_defined {
884 let mut v: u64 = 0;
885 let n_cd = size.div_ceil(4);
886 if cd_values.len() < SO_PARM_FILVAL + n_cd {
887 return Err(FormatError::InvalidData(
888 "scaleoffset: cd_values missing fill value".into(),
889 ));
890 }
891 for (w, cd) in cd_values[SO_PARM_FILVAL..SO_PARM_FILVAL + n_cd]
892 .iter()
893 .enumerate()
894 {
895 v |= (*cd as u64) << (w * 32);
896 }
897 v & mask_u64(size * 8)
898 } else {
899 0
900 };
901
902 let mut scale_factor = cd_values[SO_PARM_SCALEFACTOR] as i32;
905 if dtype_class == SO_CLS_INTEGER && scale_factor < 0 {
906 scale_factor = 0;
907 }
908
909 Ok(Self {
910 scale_factor,
911 d_nelmts: cd_values[SO_PARM_NELMTS] as usize,
912 dtype_class,
913 size,
914 dtype_sign: cd_values[SO_PARM_SIGN],
915 order: cd_values[SO_PARM_ORDER],
916 fill_defined,
917 filval,
918 })
919 }
920
921 fn is_noop(&self) -> bool {
926 self.dtype_class == SO_CLS_INTEGER && self.scale_factor as usize == self.size * 8
927 }
928
929 fn dtype_len(&self) -> u32 {
931 (self.size * 8) as u32
932 }
933
934 fn width_mask(&self) -> u64 {
936 mask_u64(self.size * 8)
937 }
938}
939
940fn mask_u64(n: usize) -> u64 {
942 if n >= 64 {
943 u64::MAX
944 } else {
945 !(u64::MAX << n)
946 }
947}
948
949#[derive(Clone, Copy)]
955struct SoByteRange {
956 data_offset: usize,
957 begin_i: u32,
958 minbits: u32,
959 dtype_len: u32,
960}
961
962fn so_decompress_one_byte(
965 data: &mut [u8],
966 k: u32,
967 buffer: &[u8],
968 cur: &mut NbitCursor,
969 range: &SoByteRange,
970) -> FormatResult<()> {
971 let SoByteRange {
972 data_offset,
973 begin_i,
974 minbits,
975 dtype_len,
976 } = *range;
977 if cur.j >= buffer.len() {
978 return Err(FormatError::InvalidData(
979 "scaleoffset: buffer too short".into(),
980 ));
981 }
982 let mut val = buffer[cur.j];
983 let mut bits_to_copy: usize = if k == begin_i {
984 8 - ((dtype_len - minbits) % 8) as usize
985 } else {
986 8
987 };
988
989 let idx = data_offset + k as usize;
990 if cur.buf_len > bits_to_copy {
991 data[idx] = ((val >> (cur.buf_len - bits_to_copy)) as u32 & mask_u32(bits_to_copy)) as u8;
992 cur.buf_len -= bits_to_copy;
993 } else {
994 data[idx] = ((val as u32 & mask_u32(cur.buf_len)) << (bits_to_copy - cur.buf_len)) as u8;
995 bits_to_copy -= cur.buf_len;
996 cur.next_byte();
997 if bits_to_copy == 0 {
998 return Ok(());
999 }
1000 if cur.j >= buffer.len() {
1001 return Err(FormatError::InvalidData(
1002 "scaleoffset: buffer too short".into(),
1003 ));
1004 }
1005 val = buffer[cur.j];
1006 data[idx] |= ((val >> (cur.buf_len - bits_to_copy)) as u32 & mask_u32(bits_to_copy)) as u8;
1007 cur.buf_len -= bits_to_copy;
1008 }
1009 Ok(())
1010}
1011
1012fn so_decompress_one_atomic(
1015 data: &mut [u8],
1016 data_offset: usize,
1017 buffer: &[u8],
1018 cur: &mut NbitCursor,
1019 size: u32,
1020 minbits: u32,
1021 order: u32,
1022) -> FormatResult<()> {
1023 let dtype_len = size * 8;
1024 if order == SO_ORDER_LE {
1025 let begin_i = size - 1 - (dtype_len - minbits) / 8;
1026 let range = SoByteRange {
1027 data_offset,
1028 begin_i,
1029 minbits,
1030 dtype_len,
1031 };
1032 let mut k = begin_i as i64;
1033 while k >= 0 {
1034 so_decompress_one_byte(data, k as u32, buffer, cur, &range)?;
1035 k -= 1;
1036 }
1037 } else {
1038 let begin_i = (dtype_len - minbits) / 8;
1039 let range = SoByteRange {
1040 data_offset,
1041 begin_i,
1042 minbits,
1043 dtype_len,
1044 };
1045 for k in begin_i..=(size - 1) {
1046 so_decompress_one_byte(data, k, buffer, cur, &range)?;
1047 }
1048 }
1049 Ok(())
1050}
1051
1052fn so_compress_one_byte(
1058 data: &[u8],
1059 k: u32,
1060 buffer: &mut [u8],
1061 cur: &mut NbitCursor,
1062 range: &SoByteRange,
1063) {
1064 let SoByteRange {
1065 data_offset,
1066 begin_i,
1067 minbits,
1068 dtype_len,
1069 } = *range;
1070 let val = data[data_offset + k as usize];
1071 let mut bits_to_copy: usize = if k == begin_i {
1072 8 - ((dtype_len - minbits) % 8) as usize
1073 } else {
1074 8
1075 };
1076
1077 if cur.buf_len > bits_to_copy {
1078 buffer[cur.j] |=
1079 ((val as u32 & mask_u32(bits_to_copy)) << (cur.buf_len - bits_to_copy)) as u8;
1080 cur.buf_len -= bits_to_copy;
1081 } else {
1082 buffer[cur.j] |=
1083 ((val >> (bits_to_copy - cur.buf_len)) as u32 & mask_u32(cur.buf_len)) as u8;
1084 bits_to_copy -= cur.buf_len;
1085 cur.next_byte();
1086 if bits_to_copy == 0 {
1087 return;
1088 }
1089 buffer[cur.j] =
1090 ((val as u32 & mask_u32(bits_to_copy)) << (cur.buf_len - bits_to_copy)) as u8;
1091 cur.buf_len -= bits_to_copy;
1092 }
1093}
1094
1095fn so_compress_one_atomic(
1098 data: &[u8],
1099 data_offset: usize,
1100 buffer: &mut [u8],
1101 cur: &mut NbitCursor,
1102 size: u32,
1103 minbits: u32,
1104 order: u32,
1105) {
1106 let dtype_len = size * 8;
1107 if order == SO_ORDER_LE {
1108 let begin_i = size - 1 - (dtype_len - minbits) / 8;
1109 let range = SoByteRange {
1110 data_offset,
1111 begin_i,
1112 minbits,
1113 dtype_len,
1114 };
1115 let mut k = begin_i as i64;
1116 while k >= 0 {
1117 so_compress_one_byte(data, k as u32, buffer, cur, &range);
1118 k -= 1;
1119 }
1120 } else {
1121 let begin_i = (dtype_len - minbits) / 8;
1122 let range = SoByteRange {
1123 data_offset,
1124 begin_i,
1125 minbits,
1126 dtype_len,
1127 };
1128 for k in begin_i..=(size - 1) {
1129 so_compress_one_byte(data, k, buffer, cur, &range);
1130 }
1131 }
1132}
1133
1134fn read_uint(data: &[u8], offset: usize, size: usize, order: u32) -> u64 {
1136 let mut v: u64 = 0;
1137 if order == SO_ORDER_LE {
1138 for i in 0..size {
1139 v |= (data[offset + i] as u64) << (i * 8);
1140 }
1141 } else {
1142 for i in 0..size {
1143 v = (v << 8) | data[offset + i] as u64;
1144 }
1145 }
1146 v
1147}
1148
1149fn write_uint(data: &mut [u8], offset: usize, size: usize, order: u32, v: u64) {
1151 if order == SO_ORDER_LE {
1152 for i in 0..size {
1153 data[offset + i] = (v >> (i * 8)) as u8;
1154 }
1155 } else {
1156 for i in 0..size {
1157 data[offset + i] = (v >> ((size - 1 - i) * 8)) as u8;
1158 }
1159 }
1160}
1161
1162pub fn reverse_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
1167 let p = SoParams::parse(cd_values)?;
1168 let (d_nelmts, size, order) = (p.d_nelmts, p.size, p.order);
1169 let size_out = d_nelmts * size;
1170
1171 if p.is_noop() {
1172 if data.len() < size_out {
1177 return Err(FormatError::InvalidData(
1178 "scaleoffset: buffer too short".into(),
1179 ));
1180 }
1181 return Ok(data[..size_out].to_vec());
1182 }
1183
1184 if data.len() < SO_BUF_OFFSET {
1186 return Err(FormatError::InvalidData(
1187 "scaleoffset: buffer too short for header".into(),
1188 ));
1189 }
1190 let mut minbits: u32 = 0;
1191 for (i, &b) in data[..4].iter().enumerate() {
1192 minbits |= (b as u32) << (i * 8);
1193 }
1194 if minbits as usize > size * 8 {
1195 return Err(FormatError::InvalidData(
1196 "scaleoffset: minbits exceeds datatype size".into(),
1197 ));
1198 }
1199 let minval_size = std::cmp::min(8usize, data[4] as usize);
1200 let mut minval: u64 = 0;
1201 for i in 0..minval_size {
1202 minval |= (data[5 + i] as u64) << (i * 8);
1203 }
1204
1205 if minbits as usize == size * 8 {
1207 if data.len() < SO_BUF_OFFSET + size_out {
1208 return Err(FormatError::InvalidData(
1209 "scaleoffset: buffer too short".into(),
1210 ));
1211 }
1212 return Ok(data[SO_BUF_OFFSET..SO_BUF_OFFSET + size_out].to_vec());
1213 }
1214
1215 let mut out = vec![0u8; size_out];
1216
1217 if minbits != 0 {
1218 if data.len() < SO_BUF_OFFSET {
1219 return Err(FormatError::InvalidData(
1220 "scaleoffset: buffer too short".into(),
1221 ));
1222 }
1223 let payload = &data[SO_BUF_OFFSET..];
1224 let mut cur = NbitCursor { j: 0, buf_len: 8 };
1225 for i in 0..d_nelmts {
1226 so_decompress_one_atomic(
1227 &mut out,
1228 i * size,
1229 payload,
1230 &mut cur,
1231 size as u32,
1232 minbits,
1233 order,
1234 )?;
1235 }
1236 }
1237 postdecompress(&mut out, &p, minbits, minval);
1241
1242 Ok(out)
1243}
1244
1245pub fn forward_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
1254 let p = SoParams::parse(cd_values)?;
1255 let nbytes = p.d_nelmts * p.size;
1256 if data.len() != nbytes {
1257 return Err(FormatError::InvalidData(format!(
1258 "scaleoffset: chunk is {} bytes, but the filter parameters describe {} elements of \
1259 {} bytes",
1260 data.len(),
1261 p.d_nelmts,
1262 p.size
1263 )));
1264 }
1265 if p.is_noop() {
1266 return Ok(data.to_vec());
1267 }
1268 if p.dtype_class == SO_CLS_INTEGER && p.scale_factor as usize > p.size * 8 {
1269 return Err(FormatError::InvalidData(
1270 "scaleoffset: minimum number of bits exceeds the datatype".into(),
1271 ));
1272 }
1273
1274 let mut buf = data.to_vec();
1278 let (minbits, minval) = if p.dtype_class == SO_CLS_INTEGER {
1279 precompress_int(&mut buf, &p)
1280 } else {
1281 precompress_float(&mut buf, &p)?
1282 };
1283 debug_assert!(minbits <= p.dtype_len());
1284
1285 let size_out = SO_BUF_OFFSET + nbytes * minbits as usize / (p.size * 8) + 1;
1288 let mut out = vec![0u8; size_out];
1289 out[..4].copy_from_slice(&minbits.to_le_bytes());
1290 out[4] = 8;
1294 out[5..13].copy_from_slice(&minval.to_le_bytes());
1295
1296 if minbits as usize == p.size * 8 {
1297 out.truncate(SO_BUF_OFFSET + nbytes);
1300 out[SO_BUF_OFFSET..].copy_from_slice(&buf);
1301 return Ok(out);
1302 }
1303 if minbits != 0 {
1304 let (header, payload) = out.split_at_mut(SO_BUF_OFFSET);
1305 let _ = header;
1306 let mut cur = NbitCursor { j: 0, buf_len: 8 };
1307 for i in 0..p.d_nelmts {
1308 so_compress_one_atomic(
1309 &buf,
1310 i * p.size,
1311 payload,
1312 &mut cur,
1313 p.size as u32,
1314 minbits,
1315 p.order,
1316 );
1317 }
1318 }
1319 Ok(out)
1322}
1323
1324fn precompress_int(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1330 let signed = p.dtype_sign == SO_SGN_2;
1331 let width_mask = p.width_mask();
1332 let key = |raw: u64| -> i128 {
1335 if signed {
1336 sign_extend(raw, p.size) as i128
1337 } else {
1338 raw as i128
1339 }
1340 };
1341 let elem = |buf: &[u8], i: usize| read_uint(buf, i * p.size, p.size, p.order);
1342
1343 let mut minbits = p.scale_factor as u32;
1344 let mut min: i128 = 0;
1345 let mut max: i128 = 0;
1346
1347 if p.fill_defined {
1348 let first = (0..p.d_nelmts).find(|&i| elem(buf, i) != p.filval);
1350 if let Some(f) = first {
1351 min = key(elem(buf, f));
1352 max = min;
1353 for i in f..p.d_nelmts {
1354 let raw = elem(buf, i);
1355 if raw == p.filval {
1356 continue;
1357 }
1358 let v = key(raw);
1359 max = max.max(v);
1360 min = min.min(v);
1361 }
1362 }
1363 if minbits == 0 {
1364 let span_minus_1 = (max - min) as u64;
1365 if span_minus_1 > width_mask - 2 {
1366 return (p.dtype_len(), 0);
1369 }
1370 minbits = so_log2(span_minus_1 + 2);
1371 }
1372 if minbits != p.dtype_len() {
1373 let sentinel = mask_u64(minbits as usize);
1374 for i in 0..p.d_nelmts {
1375 let raw = elem(buf, i);
1376 let v = if raw == p.filval {
1377 sentinel
1378 } else {
1379 (key(raw) - min) as u64 & width_mask
1380 };
1381 write_uint(buf, i * p.size, p.size, p.order, v);
1382 }
1383 }
1384 } else {
1385 min = key(elem(buf, 0));
1386 max = min;
1387 for i in 0..p.d_nelmts {
1388 let v = key(elem(buf, i));
1389 max = max.max(v);
1390 min = min.min(v);
1391 }
1392 if minbits == 0 {
1393 let span_minus_1 = (max - min) as u64;
1394 if span_minus_1 > width_mask - 2 {
1395 return (p.dtype_len(), 0);
1396 }
1397 minbits = so_log2(span_minus_1 + 1);
1398 }
1399 if minbits != p.dtype_len() {
1400 for i in 0..p.d_nelmts {
1401 let v = (key(elem(buf, i)) - min) as u64 & width_mask;
1402 write_uint(buf, i * p.size, p.size, p.order, v);
1403 }
1404 }
1405 }
1406
1407 (
1408 minbits,
1409 min as i64 as u64 & if signed { u64::MAX } else { width_mask },
1410 )
1411}
1412
1413trait SoFloat: Copy + PartialOrd + std::ops::Mul<Output = Self> + std::ops::Sub<Output = Self> {
1418 const ZERO: Self;
1419 fn from_stored(v: u64) -> Self;
1420 fn to_stored(self) -> u64;
1421 fn widen(self) -> f64;
1422 fn pow(base: f64, exp: f64) -> Self;
1424 fn abs(self) -> Self;
1425 fn round(self) -> Self;
1427 fn lround(self) -> i64;
1429}
1430
1431impl SoFloat for f32 {
1432 const ZERO: Self = 0.0;
1433 fn from_stored(v: u64) -> Self {
1434 f32::from_bits(v as u32)
1435 }
1436 fn to_stored(self) -> u64 {
1437 self.to_bits() as u64
1438 }
1439 fn widen(self) -> f64 {
1440 self as f64
1441 }
1442 fn pow(base: f64, exp: f64) -> Self {
1443 (base as f32).powf(exp as f32)
1444 }
1445 fn abs(self) -> Self {
1446 f32::abs(self)
1447 }
1448 fn round(self) -> Self {
1449 f32::round(self)
1450 }
1451 fn lround(self) -> i64 {
1452 f32::round(self) as i64
1453 }
1454}
1455
1456impl SoFloat for f64 {
1457 const ZERO: Self = 0.0;
1458 fn from_stored(v: u64) -> Self {
1459 f64::from_bits(v)
1460 }
1461 fn to_stored(self) -> u64 {
1462 self.to_bits()
1463 }
1464 fn widen(self) -> f64 {
1465 self
1466 }
1467 fn pow(base: f64, exp: f64) -> Self {
1468 base.powf(exp)
1469 }
1470 fn abs(self) -> Self {
1471 f64::abs(self)
1472 }
1473 fn round(self) -> Self {
1474 f64::round(self)
1475 }
1476 fn lround(self) -> i64 {
1477 f64::round(self) as i64
1478 }
1479}
1480
1481fn precompress_float(buf: &mut [u8], p: &SoParams) -> FormatResult<(u32, u64)> {
1489 match p.size {
1490 4 => Ok(precompress_float_typed::<f32>(buf, p)),
1491 8 => Ok(precompress_float_typed::<f64>(buf, p)),
1492 n => Err(FormatError::InvalidData(format!(
1493 "scaleoffset: no floating-point type of {n} bytes"
1494 ))),
1495 }
1496}
1497
1498fn precompress_float_typed<T: SoFloat>(buf: &mut [u8], p: &SoParams) -> (u32, u64) {
1499 let d_val = p.scale_factor as f64;
1500 let pow10 = T::pow(10.0, d_val);
1501 let filval = T::from_stored(p.filval);
1502 let get = |buf: &[u8], i: usize| T::from_stored(read_uint(buf, i * p.size, p.size, p.order));
1503 let scan_epsilon = 10f64.powf(-d_val);
1509 let is_fill_scan = |v: T| (v - filval).widen().abs() < scan_epsilon;
1510 let modify_epsilon = T::pow(10.0, -d_val);
1511 let is_fill_modify = |v: T| (v - filval).abs() < modify_epsilon;
1512
1513 let mut min = T::ZERO;
1514 let mut max = T::ZERO;
1515 if p.fill_defined {
1516 if let Some(f) = (0..p.d_nelmts).find(|&i| !is_fill_scan(get(buf, i))) {
1517 min = get(buf, f);
1518 max = min;
1519 for i in f..p.d_nelmts {
1520 let v = get(buf, i);
1521 if is_fill_scan(v) {
1522 continue;
1523 }
1524 if v > max {
1525 max = v;
1526 }
1527 if v < min {
1528 min = v;
1529 }
1530 }
1531 }
1532 } else if p.d_nelmts > 0 {
1533 min = get(buf, 0);
1534 max = min;
1535 for i in 0..p.d_nelmts {
1536 let v = get(buf, i);
1537 if v > max {
1538 max = v;
1539 }
1540 if v < min {
1541 min = v;
1542 }
1543 }
1544 }
1545
1546 let dtype_len = p.dtype_len();
1551 let scaled = max * pow10 - min * pow10;
1552 if scaled.round() > T::pow(2.0, (dtype_len - 1) as f64) {
1553 return (dtype_len, 0);
1554 }
1555 let span = scaled.lround() as u64 + 1;
1556 let minbits = if p.fill_defined {
1557 so_log2(span + 1)
1558 } else {
1559 so_log2(span)
1560 };
1561
1562 if minbits != dtype_len {
1563 let sentinel = mask_u64(minbits as usize);
1564 for i in 0..p.d_nelmts {
1565 let v = get(buf, i);
1566 let stored = if p.fill_defined && is_fill_modify(v) {
1567 sentinel
1568 } else {
1569 (v * pow10 - min * pow10).lround() as u64 & p.width_mask()
1570 };
1571 write_uint(buf, i * p.size, p.size, p.order, stored);
1572 }
1573 }
1574
1575 (minbits, min.to_stored())
1576}
1577
1578fn sign_extend(v: u64, size: usize) -> i64 {
1580 if size >= 8 {
1581 return v as i64;
1582 }
1583 let bits = size * 8;
1584 let shift = 64 - bits;
1585 ((v << shift) as i64) >> shift
1586}
1587
1588fn postdecompress(out: &mut [u8], p: &SoParams, minbits: u32, minval: u64) {
1595 let SoParams {
1596 scale_factor,
1597 d_nelmts,
1598 dtype_class,
1599 size,
1600 dtype_sign,
1601 order,
1602 fill_defined,
1603 filval,
1604 } = *p;
1605
1606 let sentinel: u64 = if (minbits as usize) >= 64 {
1609 u64::MAX
1610 } else {
1611 (1u64 << minbits) - 1
1612 };
1613 let width_mask: u64 = if size >= 8 {
1614 u64::MAX
1615 } else {
1616 (1u64 << (size * 8)) - 1
1617 };
1618
1619 if dtype_class == SO_CLS_INTEGER {
1620 for i in 0..d_nelmts {
1622 let off = i * size;
1623 let v = read_uint(out, off, size, order);
1624 let result = if fill_defined && v == sentinel {
1625 filval
1626 } else {
1627 v.wrapping_add(minval) & width_mask
1628 };
1629 write_uint(out, off, size, order, result);
1630 }
1631 let _ = dtype_sign;
1632 } else {
1633 let d_val = scale_factor as f64;
1636 let divisor = 10f64.powf(d_val);
1637 if size == 4 {
1638 let min = f32::from_bits(minval as u32);
1639 let filval_f = f32::from_bits(filval as u32);
1640 for i in 0..d_nelmts {
1641 let off = i * size;
1642 let raw = read_uint(out, off, size, order);
1643 let val = if fill_defined && raw == sentinel {
1644 filval_f
1645 } else {
1646 (sign_extend(raw, size) as f32) / (divisor as f32) + min
1647 };
1648 write_uint(out, off, size, order, val.to_bits() as u64);
1649 }
1650 } else if size == 8 {
1651 let min = f64::from_bits(minval);
1652 let filval_f = f64::from_bits(filval);
1653 for i in 0..d_nelmts {
1654 let off = i * size;
1655 let raw = read_uint(out, off, size, order);
1656 if fill_defined && raw == sentinel {
1657 write_uint(out, off, size, order, filval_f.to_bits());
1658 continue;
1659 }
1660 let val = (sign_extend(raw, size) as f64) / divisor + min;
1661 write_uint(out, off, size, order, val.to_bits());
1662 }
1663 }
1664 }
1665}
1666
1667use crate::format::messages::datatype::{ByteOrder, DatatypeMessage};
1672
1673fn is_standard_ieee_float(dt: &DatatypeMessage) -> bool {
1677 dt.ieee_format().is_some()
1678}
1679
1680pub fn datatype_needs_bit_conversion(dt: &DatatypeMessage) -> bool {
1694 match dt {
1695 DatatypeMessage::FixedPoint {
1696 size,
1697 bit_offset,
1698 bit_precision,
1699 ..
1700 } => *bit_offset != 0 || (*bit_precision as u32) < *size * 8,
1701 DatatypeMessage::FloatingPoint { .. } => !is_standard_ieee_float(dt),
1702 _ => false,
1703 }
1704}
1705
1706pub fn apply_datatype_conversion(buffer: &mut [u8], dt: &DatatypeMessage) -> FormatResult<()> {
1726 match dt {
1727 DatatypeMessage::FixedPoint {
1728 size,
1729 byte_order,
1730 signed,
1731 bit_offset,
1732 bit_precision,
1733 } => {
1734 let size = *size as usize;
1735 let precision = *bit_precision as usize;
1736 let offset = *bit_offset as usize;
1737
1738 if offset == 0 && precision == size * 8 {
1740 return Ok(());
1741 }
1742 if size == 0 || size > 8 {
1743 return Err(FormatError::InvalidData(format!(
1744 "datatype conversion: unsupported FixedPoint size {size}"
1745 )));
1746 }
1747 if precision == 0 || offset + precision > size * 8 {
1748 return Err(FormatError::InvalidData(format!(
1749 "datatype conversion: invalid bit layout (offset {offset}, \
1750 precision {precision}, size {size})"
1751 )));
1752 }
1753 if !buffer.len().is_multiple_of(size) {
1754 return Err(FormatError::InvalidData(format!(
1755 "datatype conversion: buffer length {} not a multiple of \
1756 element size {size}",
1757 buffer.len()
1758 )));
1759 }
1760
1761 let big_endian = matches!(byte_order, ByteOrder::BigEndian);
1762 let precision_mask: u64 = if precision == 64 {
1763 u64::MAX
1764 } else {
1765 (1u64 << precision) - 1
1766 };
1767 let sign_bit: u64 = 1u64 << (precision - 1);
1768
1769 for elem in buffer.chunks_exact_mut(size) {
1770 let mut raw: u64 = 0;
1772 if big_endian {
1773 for &b in elem.iter() {
1774 raw = (raw << 8) | b as u64;
1775 }
1776 } else {
1777 for (i, &b) in elem.iter().enumerate() {
1778 raw |= (b as u64) << (8 * i);
1779 }
1780 }
1781
1782 let mut value = (raw >> offset) & precision_mask;
1784
1785 if *signed && (value & sign_bit) != 0 {
1787 value |= !precision_mask;
1788 }
1789
1790 if big_endian {
1792 for i in 0..size {
1793 elem[size - 1 - i] = (value >> (8 * i)) as u8;
1794 }
1795 } else {
1796 for (i, b) in elem.iter_mut().enumerate() {
1797 *b = (value >> (8 * i)) as u8;
1798 }
1799 }
1800 }
1801 Ok(())
1802 }
1803 DatatypeMessage::FloatingPoint { .. } => {
1804 if is_standard_ieee_float(dt) {
1807 Ok(())
1808 } else {
1809 Err(FormatError::InvalidData(
1810 "datatype conversion: non-standard floating-point bit \
1811 layout cannot be converted"
1812 .into(),
1813 ))
1814 }
1815 }
1816 _ => Ok(()),
1817 }
1818}
1819
1820#[cfg(test)]
1824mod tests {
1825 use super::*;
1826
1827 fn nbit_atomic_cd(d_nelmts: u32, size: u32, precision: u32, offset: u32) -> Vec<u32> {
1829 let need_not_compress = if offset == 0 && precision == size * 8 {
1832 1
1833 } else {
1834 0
1835 };
1836 vec![
1837 8,
1838 need_not_compress,
1839 d_nelmts,
1840 NBIT_ATOMIC,
1841 size,
1842 NBIT_ORDER_LE,
1843 precision,
1844 offset,
1845 ]
1846 }
1847
1848 #[test]
1849 fn nbit_roundtrip_u16_precision12() {
1850 let values: Vec<u16> = (0..40u16).map(|i| (i * 71) & 0x0FFF).collect();
1852 let mut raw = Vec::new();
1853 for &v in &values {
1854 raw.extend_from_slice(&v.to_le_bytes());
1855 }
1856 let cd = nbit_atomic_cd(values.len() as u32, 2, 12, 0);
1857 let packed = apply_nbit(&raw, &cd, true).unwrap();
1858 assert!(packed.len() <= raw.len());
1859 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1860 assert_eq!(unpacked, raw);
1861 }
1862
1863 #[test]
1864 fn nbit_roundtrip_u32_precision20_offset4() {
1865 let values: Vec<u32> = (0..32u32).map(|i| ((i * 9999) & 0xFFFFF) << 4).collect();
1866 let mut raw = Vec::new();
1867 for &v in &values {
1868 raw.extend_from_slice(&v.to_le_bytes());
1869 }
1870 let cd = nbit_atomic_cd(values.len() as u32, 4, 20, 4);
1871 let packed = apply_nbit(&raw, &cd, true).unwrap();
1872 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1873 assert_eq!(unpacked, raw);
1874 }
1875
1876 #[test]
1877 fn nbit_passthrough_full_precision() {
1878 let raw: Vec<u8> = (0..64).collect();
1879 let cd = nbit_atomic_cd(16, 4, 32, 0); let packed = apply_nbit(&raw, &cd, true).unwrap();
1881 assert_eq!(packed, raw);
1882 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1883 assert_eq!(unpacked, raw);
1884 }
1885
1886 #[test]
1887 fn nbit_roundtrip_big_endian() {
1888 let values: Vec<u16> = (0..24u16).map(|i| (i * 53) & 0x03FF).collect();
1889 let mut raw = Vec::new();
1890 for &v in &values {
1891 raw.extend_from_slice(&v.to_be_bytes());
1892 }
1893 let mut cd = nbit_atomic_cd(values.len() as u32, 2, 10, 0);
1894 cd[5] = NBIT_ORDER_BE;
1895 let packed = apply_nbit(&raw, &cd, true).unwrap();
1896 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1897 assert_eq!(unpacked, raw);
1898 }
1899
1900 fn fixed(size: u32, signed: bool, offset: u16, precision: u16) -> DatatypeMessage {
1905 DatatypeMessage::FixedPoint {
1906 size,
1907 byte_order: ByteOrder::LittleEndian,
1908 signed,
1909 bit_offset: offset,
1910 bit_precision: precision,
1911 }
1912 }
1913
1914 #[test]
1915 fn conversion_noop_for_full_width_types() {
1916 let dt = fixed(4, false, 0, 32);
1918 assert!(!datatype_needs_bit_conversion(&dt));
1919 let mut buf = vec![0x78, 0x56, 0x34, 0x12, 0xFF, 0xFF, 0xFF, 0xFF];
1920 let before = buf.clone();
1921 apply_datatype_conversion(&mut buf, &dt).unwrap();
1922 assert_eq!(buf, before);
1923 }
1924
1925 #[test]
1926 fn conversion_noop_for_non_numeric_types() {
1927 let dt = DatatypeMessage::fixed_string(8);
1928 assert!(!datatype_needs_bit_conversion(&dt));
1929 let mut buf = b"hello!!\0".to_vec();
1930 let before = buf.clone();
1931 apply_datatype_conversion(&mut buf, &dt).unwrap();
1932 assert_eq!(buf, before);
1933 }
1934
1935 #[test]
1936 fn conversion_unsigned_offset_shifts_right() {
1937 let dt = fixed(2, false, 3, 10);
1941 assert!(datatype_needs_bit_conversion(&dt));
1942 let mut buf = (0x1528u16).to_le_bytes().to_vec();
1943 apply_datatype_conversion(&mut buf, &dt).unwrap();
1944 assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0x2A5);
1945 }
1946
1947 #[test]
1948 fn conversion_signed_negative_sign_extends() {
1949 let dt = fixed(2, true, 4, 8);
1952 let mut buf = (0x0FD0u16).to_le_bytes().to_vec();
1953 apply_datatype_conversion(&mut buf, &dt).unwrap();
1954 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), -3);
1955 }
1956
1957 #[test]
1958 fn conversion_signed_positive_stays_positive() {
1959 let dt = fixed(2, true, 4, 8);
1961 let mut buf = (0x0050u16).to_le_bytes().to_vec();
1962 apply_datatype_conversion(&mut buf, &dt).unwrap();
1963 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), 5);
1964 }
1965
1966 #[test]
1967 fn conversion_reduced_precision_offset_zero() {
1968 let dt = fixed(4, true, 0, 20);
1971 assert!(datatype_needs_bit_conversion(&dt));
1972 let mut buf = (0x000FFFFFu32).to_le_bytes().to_vec();
1973 apply_datatype_conversion(&mut buf, &dt).unwrap();
1974 assert_eq!(i32::from_le_bytes(buf.clone().try_into().unwrap()), -1);
1975 }
1976
1977 #[test]
1978 fn conversion_big_endian_signed() {
1979 let dt = DatatypeMessage::FixedPoint {
1981 size: 2,
1982 byte_order: ByteOrder::BigEndian,
1983 signed: true,
1984 bit_offset: 4,
1985 bit_precision: 8,
1986 };
1987 let mut buf = (0x0FD0u16).to_be_bytes().to_vec();
1988 apply_datatype_conversion(&mut buf, &dt).unwrap();
1989 assert_eq!(i16::from_be_bytes([buf[0], buf[1]]), -3);
1990 }
1991
1992 #[test]
1993 fn conversion_multiple_elements() {
1994 let dt = fixed(4, false, 5, 16);
1996 let vals: [u32; 3] = [0x1234, 0xABCD, 0x0001];
1997 let mut buf = Vec::new();
1998 for v in vals {
1999 buf.extend_from_slice(&(v << 5).to_le_bytes());
2000 }
2001 apply_datatype_conversion(&mut buf, &dt).unwrap();
2002 for (i, v) in vals.iter().enumerate() {
2003 let e = u32::from_le_bytes(buf[i * 4..i * 4 + 4].try_into().unwrap());
2004 assert_eq!(e, *v);
2005 }
2006 }
2007
2008 #[test]
2009 fn conversion_rejects_non_standard_float() {
2010 let dt = DatatypeMessage::FloatingPoint {
2012 size: 4,
2013 byte_order: ByteOrder::LittleEndian,
2014 sign_location: 30,
2015 bit_offset: 1,
2016 bit_precision: 31,
2017 exponent_location: 22,
2018 exponent_size: 8,
2019 mantissa_location: 0,
2020 mantissa_size: 22,
2021 exponent_bias: 127,
2022 };
2023 assert!(datatype_needs_bit_conversion(&dt));
2024 let mut buf = vec![0u8; 4];
2025 assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
2026 }
2027
2028 #[test]
2029 fn conversion_standard_float_is_noop() {
2030 let dt = DatatypeMessage::f64_type();
2031 assert!(!datatype_needs_bit_conversion(&dt));
2032 let mut buf = 12.5f64.to_le_bytes().to_vec();
2033 let before = buf.clone();
2034 apply_datatype_conversion(&mut buf, &dt).unwrap();
2035 assert_eq!(buf, before);
2036 }
2037
2038 #[test]
2039 fn conversion_rejects_bad_buffer_length() {
2040 let dt = fixed(4, false, 3, 16);
2041 let mut buf = vec![0u8; 5]; assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
2043 }
2044}