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#[allow(clippy::too_many_arguments)]
56fn nbit_decompress_one_byte(
57 data: &mut [u8],
58 data_offset: usize,
59 k: u32,
60 begin_i: u32,
61 end_i: u32,
62 buffer: &[u8],
63 cur: &mut NbitCursor,
64 p: &NbitAtomic,
65 datatype_len: u32,
66) -> FormatResult<()> {
67 if cur.j >= buffer.len() {
68 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
69 }
70 let mut val = buffer[cur.j];
71 let mut dat_offset: usize = 0;
72 let mut dat_len: usize;
73
74 if begin_i != end_i {
75 if k == begin_i {
76 dat_len = 8 - ((datatype_len - p.precision - p.offset) % 8) as usize;
77 } else if k == end_i {
78 dat_len = 8 - (p.offset % 8) as usize;
79 dat_offset = 8 - dat_len;
80 } else {
81 dat_len = 8;
82 }
83 } else {
84 dat_offset = (p.offset % 8) as usize;
85 dat_len = p.precision as usize;
86 }
87
88 let idx = data_offset + k as usize;
89 if cur.buf_len > dat_len {
90 data[idx] =
91 (((val >> (cur.buf_len - dat_len)) as u32 & mask_u32(dat_len)) << dat_offset) as u8;
92 cur.buf_len -= dat_len;
93 } else {
94 data[idx] =
95 (((val as u32 & mask_u32(cur.buf_len)) << (dat_len - cur.buf_len)) << dat_offset) as u8;
96 dat_len -= cur.buf_len;
97 cur.next_byte();
98 if dat_len == 0 {
99 return Ok(());
100 }
101 if cur.j >= buffer.len() {
102 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
103 }
104 val = buffer[cur.j];
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 }
109 Ok(())
110}
111
112#[allow(clippy::too_many_arguments)]
114fn nbit_compress_one_byte(
115 data: &[u8],
116 data_offset: usize,
117 k: u32,
118 begin_i: u32,
119 end_i: u32,
120 buffer: &mut [u8],
121 cur: &mut NbitCursor,
122 p: &NbitAtomic,
123 datatype_len: u32,
124) {
125 let mut val = data[data_offset + k as usize];
126 let mut dat_len: usize;
127
128 if begin_i != end_i {
129 if k == begin_i {
130 dat_len = 8 - ((datatype_len - p.precision - p.offset) % 8) as usize;
131 } else if k == end_i {
132 dat_len = 8 - (p.offset % 8) as usize;
133 val >>= 8 - dat_len;
134 } else {
135 dat_len = 8;
136 }
137 } else {
138 val >>= p.offset % 8;
139 dat_len = p.precision as usize;
140 }
141
142 if cur.buf_len > dat_len {
143 buffer[cur.j] |= ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
144 cur.buf_len -= dat_len;
145 } else {
146 buffer[cur.j] |= ((val as u32 >> (dat_len - cur.buf_len)) & mask_u32(cur.buf_len)) as u8;
147 dat_len -= cur.buf_len;
148 cur.next_byte();
149 if dat_len == 0 {
150 return;
151 }
152 buffer[cur.j] = ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
153 cur.buf_len -= dat_len;
154 }
155}
156
157fn nbit_decompress_one_nooptype(
159 data: &mut [u8],
160 data_offset: usize,
161 buffer: &[u8],
162 cur: &mut NbitCursor,
163 size: u32,
164) -> FormatResult<()> {
165 for i in 0..size as usize {
166 if cur.j >= buffer.len() {
167 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
168 }
169 let mut val = buffer[cur.j];
170 let mut dat_len: usize = 8;
171 data[data_offset + i] =
172 ((val as u32 & mask_u32(cur.buf_len)) << (dat_len - cur.buf_len)) as u8;
173 dat_len -= cur.buf_len;
174 cur.next_byte();
175 if dat_len == 0 {
176 continue;
177 }
178 if cur.j >= buffer.len() {
179 return Err(FormatError::InvalidData("nbit: buffer too short".into()));
180 }
181 val = buffer[cur.j];
182 data[data_offset + i] |=
183 ((val >> (cur.buf_len - dat_len)) as u32 & mask_u32(dat_len)) as u8;
184 cur.buf_len -= dat_len;
185 }
186 Ok(())
187}
188
189fn nbit_compress_one_nooptype(
191 data: &[u8],
192 data_offset: usize,
193 buffer: &mut [u8],
194 cur: &mut NbitCursor,
195 size: u32,
196) {
197 for i in 0..size as usize {
198 let val = data[data_offset + i];
199 let mut dat_len: usize = 8;
200 buffer[cur.j] |= ((val as u32 >> (dat_len - cur.buf_len)) & mask_u32(cur.buf_len)) as u8;
201 dat_len -= cur.buf_len;
202 cur.next_byte();
203 if dat_len == 0 {
204 continue;
205 }
206 buffer[cur.j] = ((val as u32 & mask_u32(dat_len)) << (cur.buf_len - dat_len)) as u8;
207 cur.buf_len -= dat_len;
208 }
209}
210
211fn nbit_decompress_one_atomic(
213 data: &mut [u8],
214 data_offset: usize,
215 buffer: &[u8],
216 cur: &mut NbitCursor,
217 p: &NbitAtomic,
218) -> FormatResult<()> {
219 let datatype_len = p.size * 8;
220 if p.order == NBIT_ORDER_LE {
221 let begin_i = if !(p.precision + p.offset).is_multiple_of(8) {
222 (p.precision + p.offset) / 8
223 } else {
224 (p.precision + p.offset) / 8 - 1
225 };
226 let end_i = p.offset / 8;
227 let mut k = begin_i as i64;
228 while k >= end_i as i64 {
229 nbit_decompress_one_byte(
230 data,
231 data_offset,
232 k as u32,
233 begin_i,
234 end_i,
235 buffer,
236 cur,
237 p,
238 datatype_len,
239 )?;
240 k -= 1;
241 }
242 } else {
243 let begin_i = (datatype_len - p.precision - p.offset) / 8;
244 let end_i = if !p.offset.is_multiple_of(8) {
245 (datatype_len - p.offset) / 8
246 } else {
247 (datatype_len - p.offset) / 8 - 1
248 };
249 for k in begin_i..=end_i {
250 nbit_decompress_one_byte(
251 data,
252 data_offset,
253 k,
254 begin_i,
255 end_i,
256 buffer,
257 cur,
258 p,
259 datatype_len,
260 )?;
261 }
262 }
263 Ok(())
264}
265
266fn nbit_compress_one_atomic(
268 data: &[u8],
269 data_offset: usize,
270 buffer: &mut [u8],
271 cur: &mut NbitCursor,
272 p: &NbitAtomic,
273) {
274 let datatype_len = p.size * 8;
275 if p.order == NBIT_ORDER_LE {
276 let begin_i = if !(p.precision + p.offset).is_multiple_of(8) {
277 (p.precision + p.offset) / 8
278 } else {
279 (p.precision + p.offset) / 8 - 1
280 };
281 let end_i = p.offset / 8;
282 let mut k = begin_i as i64;
283 while k >= end_i as i64 {
284 nbit_compress_one_byte(
285 data,
286 data_offset,
287 k as u32,
288 begin_i,
289 end_i,
290 buffer,
291 cur,
292 p,
293 datatype_len,
294 );
295 k -= 1;
296 }
297 } else {
298 let begin_i = (datatype_len - p.precision - p.offset) / 8;
299 let end_i = if !p.offset.is_multiple_of(8) {
300 (datatype_len - p.offset) / 8
301 } else {
302 (datatype_len - p.offset) / 8 - 1
303 };
304 for k in begin_i..=end_i {
305 nbit_compress_one_byte(
306 data,
307 data_offset,
308 k,
309 begin_i,
310 end_i,
311 buffer,
312 cur,
313 p,
314 datatype_len,
315 );
316 }
317 }
318}
319
320fn read_atomic(parms: &[u32], idx: &mut usize) -> FormatResult<NbitAtomic> {
323 if *idx + 4 > parms.len() {
324 return Err(FormatError::InvalidData(
325 "nbit: parameter list truncated".into(),
326 ));
327 }
328 let p = NbitAtomic {
329 size: parms[*idx],
330 order: parms[*idx + 1],
331 precision: parms[*idx + 2],
332 offset: parms[*idx + 3],
333 };
334 *idx += 4;
335 let bits = p.size.checked_mul(8);
338 let span = p.precision.checked_add(p.offset);
339 match (bits, span) {
340 (Some(bits), Some(span))
341 if p.size > 0 && p.precision > 0 && p.precision <= bits && span <= bits => {}
342 _ => {
343 return Err(FormatError::InvalidData(format!(
344 "nbit: invalid atomic datatype (size={}, precision={}, offset={})",
345 p.size, p.precision, p.offset
346 )));
347 }
348 }
349 Ok(p)
350}
351
352fn nbit_decompress_one_array(
354 data: &mut [u8],
355 data_offset: usize,
356 buffer: &[u8],
357 cur: &mut NbitCursor,
358 parms: &[u32],
359 parms_index: &mut usize,
360) -> FormatResult<()> {
361 if *parms_index + 2 > parms.len() {
362 return Err(FormatError::InvalidData(
363 "nbit: parameter list truncated".into(),
364 ));
365 }
366 let total_size = parms[*parms_index];
367 let base_class = parms[*parms_index + 1];
368 *parms_index += 2;
369
370 match base_class {
371 NBIT_ATOMIC => {
372 let p = read_atomic(parms, parms_index)?;
373 let n = total_size / p.size;
374 for i in 0..n as usize {
375 nbit_decompress_one_atomic(
376 data,
377 data_offset + i * p.size as usize,
378 buffer,
379 cur,
380 &p,
381 )?;
382 }
383 }
384 NBIT_ARRAY => {
385 let base_size = parms[*parms_index];
386 let n = total_size / base_size;
387 let begin_index = *parms_index;
388 for i in 0..n as usize {
389 *parms_index = begin_index;
390 nbit_decompress_one_array(
391 data,
392 data_offset + i * base_size as usize,
393 buffer,
394 cur,
395 parms,
396 parms_index,
397 )?;
398 }
399 }
400 NBIT_COMPOUND => {
401 let base_size = parms[*parms_index];
402 let n = total_size / base_size;
403 let begin_index = *parms_index;
404 for i in 0..n as usize {
405 *parms_index = begin_index;
406 nbit_decompress_one_compound(
407 data,
408 data_offset + i * base_size as usize,
409 buffer,
410 cur,
411 parms,
412 parms_index,
413 )?;
414 }
415 }
416 NBIT_NOOPTYPE => {
417 *parms_index += 1; nbit_decompress_one_nooptype(data, data_offset, buffer, cur, total_size)?;
419 }
420 _ => {
421 return Err(FormatError::InvalidData(format!(
422 "nbit: bad base class {}",
423 base_class
424 )))
425 }
426 }
427 Ok(())
428}
429
430fn nbit_decompress_one_compound(
432 data: &mut [u8],
433 data_offset: usize,
434 buffer: &[u8],
435 cur: &mut NbitCursor,
436 parms: &[u32],
437 parms_index: &mut usize,
438) -> FormatResult<()> {
439 if *parms_index + 2 > parms.len() {
440 return Err(FormatError::InvalidData(
441 "nbit: parameter list truncated".into(),
442 ));
443 }
444 *parms_index += 1; let nmembers = parms[*parms_index];
446 *parms_index += 1;
447
448 for _ in 0..nmembers {
449 if *parms_index + 2 > parms.len() {
450 return Err(FormatError::InvalidData(
451 "nbit: parameter list truncated".into(),
452 ));
453 }
454 let member_offset = parms[*parms_index] as usize;
455 let member_class = parms[*parms_index + 1];
456 *parms_index += 2;
457
458 match member_class {
459 NBIT_ATOMIC => {
460 let p = read_atomic(parms, parms_index)?;
461 nbit_decompress_one_atomic(data, data_offset + member_offset, buffer, cur, &p)?;
462 }
463 NBIT_ARRAY => {
464 nbit_decompress_one_array(
465 data,
466 data_offset + member_offset,
467 buffer,
468 cur,
469 parms,
470 parms_index,
471 )?;
472 }
473 NBIT_COMPOUND => {
474 nbit_decompress_one_compound(
475 data,
476 data_offset + member_offset,
477 buffer,
478 cur,
479 parms,
480 parms_index,
481 )?;
482 }
483 NBIT_NOOPTYPE => {
484 let size = parms[*parms_index];
485 *parms_index += 1;
486 nbit_decompress_one_nooptype(data, data_offset + member_offset, buffer, cur, size)?;
487 }
488 _ => {
489 return Err(FormatError::InvalidData(format!(
490 "nbit: bad member class {}",
491 member_class
492 )))
493 }
494 }
495 }
496 Ok(())
497}
498
499fn nbit_compress_one_array(
501 data: &[u8],
502 data_offset: usize,
503 buffer: &mut [u8],
504 cur: &mut NbitCursor,
505 parms: &[u32],
506 parms_index: &mut usize,
507) -> FormatResult<()> {
508 if *parms_index + 2 > parms.len() {
509 return Err(FormatError::InvalidData(
510 "nbit: parameter list truncated".into(),
511 ));
512 }
513 let total_size = parms[*parms_index];
514 let base_class = parms[*parms_index + 1];
515 *parms_index += 2;
516
517 match base_class {
518 NBIT_ATOMIC => {
519 let p = read_atomic(parms, parms_index)?;
520 let n = total_size / p.size;
521 for i in 0..n as usize {
522 nbit_compress_one_atomic(data, data_offset + i * p.size as usize, buffer, cur, &p);
523 }
524 }
525 NBIT_ARRAY => {
526 let base_size = parms[*parms_index];
527 let n = total_size / base_size;
528 let begin_index = *parms_index;
529 for i in 0..n as usize {
530 *parms_index = begin_index;
531 nbit_compress_one_array(
532 data,
533 data_offset + i * base_size as usize,
534 buffer,
535 cur,
536 parms,
537 parms_index,
538 )?;
539 }
540 }
541 NBIT_COMPOUND => {
542 let base_size = parms[*parms_index];
543 let n = total_size / base_size;
544 let begin_index = *parms_index;
545 for i in 0..n as usize {
546 *parms_index = begin_index;
547 nbit_compress_one_compound(
548 data,
549 data_offset + i * base_size as usize,
550 buffer,
551 cur,
552 parms,
553 parms_index,
554 )?;
555 }
556 }
557 NBIT_NOOPTYPE => {
558 *parms_index += 1;
559 nbit_compress_one_nooptype(data, data_offset, buffer, cur, total_size);
560 }
561 _ => {
562 return Err(FormatError::InvalidData(format!(
563 "nbit: bad base class {}",
564 base_class
565 )))
566 }
567 }
568 Ok(())
569}
570
571fn nbit_compress_one_compound(
573 data: &[u8],
574 data_offset: usize,
575 buffer: &mut [u8],
576 cur: &mut NbitCursor,
577 parms: &[u32],
578 parms_index: &mut usize,
579) -> FormatResult<()> {
580 if *parms_index + 2 > parms.len() {
581 return Err(FormatError::InvalidData(
582 "nbit: parameter list truncated".into(),
583 ));
584 }
585 *parms_index += 1;
586 let nmembers = parms[*parms_index];
587 *parms_index += 1;
588
589 for _ in 0..nmembers {
590 if *parms_index + 2 > parms.len() {
591 return Err(FormatError::InvalidData(
592 "nbit: parameter list truncated".into(),
593 ));
594 }
595 let member_offset = parms[*parms_index] as usize;
596 let member_class = parms[*parms_index + 1];
597 *parms_index += 2;
598
599 match member_class {
600 NBIT_ATOMIC => {
601 let p = read_atomic(parms, parms_index)?;
602 nbit_compress_one_atomic(data, data_offset + member_offset, buffer, cur, &p);
603 }
604 NBIT_ARRAY => {
605 nbit_compress_one_array(
606 data,
607 data_offset + member_offset,
608 buffer,
609 cur,
610 parms,
611 parms_index,
612 )?;
613 }
614 NBIT_COMPOUND => {
615 nbit_compress_one_compound(
616 data,
617 data_offset + member_offset,
618 buffer,
619 cur,
620 parms,
621 parms_index,
622 )?;
623 }
624 NBIT_NOOPTYPE => {
625 let size = parms[*parms_index];
626 *parms_index += 1;
627 nbit_compress_one_nooptype(data, data_offset + member_offset, buffer, cur, size);
628 }
629 _ => {
630 return Err(FormatError::InvalidData(format!(
631 "nbit: bad member class {}",
632 member_class
633 )))
634 }
635 }
636 }
637 Ok(())
638}
639
640pub fn apply_nbit(data: &[u8], cd_values: &[u32], compress: bool) -> FormatResult<Vec<u8>> {
649 if cd_values.len() < 4 {
650 return Err(FormatError::InvalidData("nbit: cd_values too short".into()));
651 }
652 if cd_values[1] != 0 {
654 return Ok(data.to_vec());
655 }
656
657 let d_nelmts = cd_values[2] as usize;
658 let dtype_size = cd_values[4] as usize;
659 if dtype_size == 0 {
660 return Err(FormatError::InvalidData("nbit: zero datatype size".into()));
661 }
662 let unpacked_size = d_nelmts * dtype_size;
663
664 if compress {
665 if data.len() != unpacked_size {
666 return Err(FormatError::InvalidData(format!(
667 "nbit: input size {} != expected {}",
668 data.len(),
669 unpacked_size
670 )));
671 }
672 let mut buffer = vec![0u8; unpacked_size + 1];
674 let mut cur = NbitCursor { j: 0, buf_len: 8 };
675 match cd_values[3] {
676 NBIT_ATOMIC => {
677 let mut idx = 4;
678 let p = read_atomic(cd_values, &mut idx)?;
679 for i in 0..d_nelmts {
680 nbit_compress_one_atomic(data, i * p.size as usize, &mut buffer, &mut cur, &p);
681 }
682 }
683 NBIT_ARRAY => {
684 let size = cd_values[4] as usize;
685 for i in 0..d_nelmts {
686 let mut idx = 4;
687 nbit_compress_one_array(
688 data,
689 i * size,
690 &mut buffer,
691 &mut cur,
692 cd_values,
693 &mut idx,
694 )?;
695 }
696 }
697 NBIT_COMPOUND => {
698 let size = cd_values[4] as usize;
699 for i in 0..d_nelmts {
700 let mut idx = 4;
701 nbit_compress_one_compound(
702 data,
703 i * size,
704 &mut buffer,
705 &mut cur,
706 cd_values,
707 &mut idx,
708 )?;
709 }
710 }
711 other => {
712 return Err(FormatError::InvalidData(format!(
713 "nbit: unsupported top class {}",
714 other
715 )))
716 }
717 }
718 buffer.truncate(cur.j + 1);
720 Ok(buffer)
721 } else {
722 let mut out = vec![0u8; unpacked_size];
723 let mut cur = NbitCursor { j: 0, buf_len: 8 };
724 match cd_values[3] {
725 NBIT_ATOMIC => {
726 let mut idx = 4;
727 let p = read_atomic(cd_values, &mut idx)?;
728 if p.precision > p.size * 8 || p.precision + p.offset > p.size * 8 {
729 return Err(FormatError::InvalidData(
730 "nbit: invalid precision/offset".into(),
731 ));
732 }
733 for i in 0..d_nelmts {
734 nbit_decompress_one_atomic(&mut out, i * p.size as usize, data, &mut cur, &p)?;
735 }
736 }
737 NBIT_ARRAY => {
738 let size = cd_values[4] as usize;
739 for i in 0..d_nelmts {
740 let mut idx = 4;
741 nbit_decompress_one_array(
742 &mut out,
743 i * size,
744 data,
745 &mut cur,
746 cd_values,
747 &mut idx,
748 )?;
749 }
750 }
751 NBIT_COMPOUND => {
752 let size = cd_values[4] as usize;
753 for i in 0..d_nelmts {
754 let mut idx = 4;
755 nbit_decompress_one_compound(
756 &mut out,
757 i * size,
758 data,
759 &mut cur,
760 cd_values,
761 &mut idx,
762 )?;
763 }
764 }
765 other => {
766 return Err(FormatError::InvalidData(format!(
767 "nbit: unsupported top class {}",
768 other
769 )))
770 }
771 }
772 Ok(out)
773 }
774}
775
776const SO_PARM_SCALETYPE: usize = 0;
782const SO_PARM_SCALEFACTOR: usize = 1;
783const SO_PARM_NELMTS: usize = 2;
784const SO_PARM_CLASS: usize = 3;
785const SO_PARM_SIZE: usize = 4;
786const SO_PARM_SIGN: usize = 5;
787const SO_PARM_ORDER: usize = 6;
788const SO_PARM_FILAVAIL: usize = 7;
789const SO_PARM_FILVAL: usize = 8;
791
792const SO_CLS_INTEGER: u32 = 0;
793const SO_CLS_FLOAT: u32 = 1;
794const SO_ORDER_LE: u32 = 0;
795const SO_FILL_DEFINED: u32 = 1;
796const SO_FLOAT_DSCALE: u32 = 0;
798
799const SO_BUF_OFFSET: usize = 21;
801
802#[allow(clippy::too_many_arguments)]
805fn so_decompress_one_byte(
806 data: &mut [u8],
807 data_offset: usize,
808 k: u32,
809 begin_i: u32,
810 buffer: &[u8],
811 cur: &mut NbitCursor,
812 minbits: u32,
813 dtype_len: u32,
814) -> FormatResult<()> {
815 if cur.j >= buffer.len() {
816 return Err(FormatError::InvalidData(
817 "scaleoffset: buffer too short".into(),
818 ));
819 }
820 let mut val = buffer[cur.j];
821 let mut bits_to_copy: usize = if k == begin_i {
822 8 - ((dtype_len - minbits) % 8) as usize
823 } else {
824 8
825 };
826
827 let idx = data_offset + k as usize;
828 if cur.buf_len > bits_to_copy {
829 data[idx] = ((val >> (cur.buf_len - bits_to_copy)) as u32 & mask_u32(bits_to_copy)) as u8;
830 cur.buf_len -= bits_to_copy;
831 } else {
832 data[idx] = ((val as u32 & mask_u32(cur.buf_len)) << (bits_to_copy - cur.buf_len)) as u8;
833 bits_to_copy -= cur.buf_len;
834 cur.next_byte();
835 if bits_to_copy == 0 {
836 return Ok(());
837 }
838 if cur.j >= buffer.len() {
839 return Err(FormatError::InvalidData(
840 "scaleoffset: buffer too short".into(),
841 ));
842 }
843 val = buffer[cur.j];
844 data[idx] |= ((val >> (cur.buf_len - bits_to_copy)) as u32 & mask_u32(bits_to_copy)) as u8;
845 cur.buf_len -= bits_to_copy;
846 }
847 Ok(())
848}
849
850fn so_decompress_one_atomic(
853 data: &mut [u8],
854 data_offset: usize,
855 buffer: &[u8],
856 cur: &mut NbitCursor,
857 size: u32,
858 minbits: u32,
859 order: u32,
860) -> FormatResult<()> {
861 let dtype_len = size * 8;
862 if order == SO_ORDER_LE {
863 let begin_i = size - 1 - (dtype_len - minbits) / 8;
864 let mut k = begin_i as i64;
865 while k >= 0 {
866 so_decompress_one_byte(
867 data,
868 data_offset,
869 k as u32,
870 begin_i,
871 buffer,
872 cur,
873 minbits,
874 dtype_len,
875 )?;
876 k -= 1;
877 }
878 } else {
879 let begin_i = (dtype_len - minbits) / 8;
880 for k in begin_i..=(size - 1) {
881 so_decompress_one_byte(
882 data,
883 data_offset,
884 k,
885 begin_i,
886 buffer,
887 cur,
888 minbits,
889 dtype_len,
890 )?;
891 }
892 }
893 Ok(())
894}
895
896fn read_uint(data: &[u8], offset: usize, size: usize, order: u32) -> u64 {
898 let mut v: u64 = 0;
899 if order == SO_ORDER_LE {
900 for i in 0..size {
901 v |= (data[offset + i] as u64) << (i * 8);
902 }
903 } else {
904 for i in 0..size {
905 v = (v << 8) | data[offset + i] as u64;
906 }
907 }
908 v
909}
910
911fn write_uint(data: &mut [u8], offset: usize, size: usize, order: u32, v: u64) {
913 if order == SO_ORDER_LE {
914 for i in 0..size {
915 data[offset + i] = (v >> (i * 8)) as u8;
916 }
917 } else {
918 for i in 0..size {
919 data[offset + i] = (v >> ((size - 1 - i) * 8)) as u8;
920 }
921 }
922}
923
924pub fn reverse_scaleoffset(data: &[u8], cd_values: &[u32]) -> FormatResult<Vec<u8>> {
929 if cd_values.len() < 8 {
930 return Err(FormatError::InvalidData(
931 "scaleoffset: cd_values too short".into(),
932 ));
933 }
934 let scale_type = cd_values[SO_PARM_SCALETYPE];
935 let scale_factor = cd_values[SO_PARM_SCALEFACTOR] as i32;
936 let d_nelmts = cd_values[SO_PARM_NELMTS] as usize;
937 let dtype_class = cd_values[SO_PARM_CLASS];
938 let size = cd_values[SO_PARM_SIZE] as usize;
939 let dtype_sign = cd_values[SO_PARM_SIGN];
940 let order = cd_values[SO_PARM_ORDER];
941 let filavail = cd_values[SO_PARM_FILAVAIL];
942
943 if size == 0 || size > 8 {
944 return Err(FormatError::InvalidData(format!(
945 "scaleoffset: unsupported datatype size {}",
946 size
947 )));
948 }
949 let filval: u64 = if filavail == SO_FILL_DEFINED {
955 let mut v: u64 = 0;
956 let n_cd = size.div_ceil(4);
957 if cd_values.len() < SO_PARM_FILVAL + n_cd {
958 return Err(FormatError::InvalidData(
959 "scaleoffset: cd_values missing fill value".into(),
960 ));
961 }
962 for (w, cd) in cd_values[SO_PARM_FILVAL..SO_PARM_FILVAL + n_cd]
963 .iter()
964 .enumerate()
965 {
966 v |= (*cd as u64) << (w * 32);
967 }
968 if size < 8 {
969 v &= (1u64 << (size * 8)) - 1;
970 }
971 v
972 } else {
973 0
974 };
975 if dtype_class == SO_CLS_FLOAT && scale_type != SO_FLOAT_DSCALE {
976 return Err(FormatError::UnsupportedFeature(
977 "scaleoffset E-scaling method is not supported".into(),
978 ));
979 }
980
981 let size_out = d_nelmts * size;
982
983 let int_scalefactor = if scale_factor < 0 { 0 } else { scale_factor };
985 if dtype_class == SO_CLS_INTEGER && int_scalefactor as usize == size * 8 {
986 if data.len() < SO_BUF_OFFSET + size_out {
988 return Err(FormatError::InvalidData(
989 "scaleoffset: buffer too short".into(),
990 ));
991 }
992 return Ok(data[SO_BUF_OFFSET..SO_BUF_OFFSET + size_out].to_vec());
993 }
994
995 if data.len() < SO_BUF_OFFSET {
997 return Err(FormatError::InvalidData(
998 "scaleoffset: buffer too short for header".into(),
999 ));
1000 }
1001 let mut minbits: u32 = 0;
1002 for (i, &b) in data[..4].iter().enumerate() {
1003 minbits |= (b as u32) << (i * 8);
1004 }
1005 if minbits as usize > size * 8 {
1006 return Err(FormatError::InvalidData(
1007 "scaleoffset: minbits exceeds datatype size".into(),
1008 ));
1009 }
1010 let minval_size = std::cmp::min(8usize, data[4] as usize);
1011 let mut minval: u64 = 0;
1012 for i in 0..minval_size {
1013 minval |= (data[5 + i] as u64) << (i * 8);
1014 }
1015
1016 if minbits as usize == size * 8 {
1018 if data.len() < SO_BUF_OFFSET + size_out {
1019 return Err(FormatError::InvalidData(
1020 "scaleoffset: buffer too short".into(),
1021 ));
1022 }
1023 return Ok(data[SO_BUF_OFFSET..SO_BUF_OFFSET + size_out].to_vec());
1024 }
1025
1026 let mut out = vec![0u8; size_out];
1027
1028 if minbits != 0 {
1029 if data.len() < SO_BUF_OFFSET {
1030 return Err(FormatError::InvalidData(
1031 "scaleoffset: buffer too short".into(),
1032 ));
1033 }
1034 let payload = &data[SO_BUF_OFFSET..];
1035 let mut cur = NbitCursor { j: 0, buf_len: 8 };
1036 for i in 0..d_nelmts {
1037 so_decompress_one_atomic(
1038 &mut out,
1039 i * size,
1040 payload,
1041 &mut cur,
1042 size as u32,
1043 minbits,
1044 order,
1045 )?;
1046 }
1047 }
1048 postdecompress(
1052 &mut out,
1053 d_nelmts,
1054 size,
1055 order,
1056 dtype_class,
1057 dtype_sign,
1058 minbits,
1059 minval,
1060 scale_factor,
1061 filavail == SO_FILL_DEFINED,
1062 filval,
1063 );
1064
1065 Ok(out)
1066}
1067
1068fn sign_extend(v: u64, size: usize) -> i64 {
1070 if size >= 8 {
1071 return v as i64;
1072 }
1073 let bits = size * 8;
1074 let shift = 64 - bits;
1075 ((v << shift) as i64) >> shift
1076}
1077
1078#[allow(clippy::too_many_arguments)]
1080fn postdecompress(
1081 out: &mut [u8],
1082 d_nelmts: usize,
1083 size: usize,
1084 order: u32,
1085 dtype_class: u32,
1086 dtype_sign: u32,
1087 minbits: u32,
1088 minval: u64,
1089 scale_factor: i32,
1090 fill_defined: bool,
1091 filval: u64,
1092) {
1093 let sentinel: u64 = if (minbits as usize) >= 64 {
1096 u64::MAX
1097 } else {
1098 (1u64 << minbits) - 1
1099 };
1100 let width_mask: u64 = if size >= 8 {
1101 u64::MAX
1102 } else {
1103 (1u64 << (size * 8)) - 1
1104 };
1105
1106 if dtype_class == SO_CLS_INTEGER {
1107 for i in 0..d_nelmts {
1109 let off = i * size;
1110 let v = read_uint(out, off, size, order);
1111 let result = if fill_defined && v == sentinel {
1112 filval
1113 } else {
1114 v.wrapping_add(minval) & width_mask
1115 };
1116 write_uint(out, off, size, order, result);
1117 }
1118 let _ = dtype_sign;
1119 } else {
1120 let d_val = scale_factor as f64;
1123 let divisor = 10f64.powf(d_val);
1124 if size == 4 {
1125 let min = f32::from_bits(minval as u32);
1126 let filval_f = f32::from_bits(filval as u32);
1127 for i in 0..d_nelmts {
1128 let off = i * size;
1129 let raw = read_uint(out, off, size, order);
1130 let val = if fill_defined && raw == sentinel {
1131 filval_f
1132 } else {
1133 (sign_extend(raw, size) as f32) / (divisor as f32) + min
1134 };
1135 write_uint(out, off, size, order, val.to_bits() as u64);
1136 }
1137 } else if size == 8 {
1138 let min = f64::from_bits(minval);
1139 let filval_f = f64::from_bits(filval);
1140 for i in 0..d_nelmts {
1141 let off = i * size;
1142 let raw = read_uint(out, off, size, order);
1143 if fill_defined && raw == sentinel {
1144 write_uint(out, off, size, order, filval_f.to_bits());
1145 continue;
1146 }
1147 let val = (sign_extend(raw, size) as f64) / divisor + min;
1148 write_uint(out, off, size, order, val.to_bits());
1149 }
1150 }
1151 }
1152}
1153
1154use crate::format::messages::datatype::{ByteOrder, DatatypeMessage};
1159
1160fn is_standard_ieee_float(dt: &DatatypeMessage) -> bool {
1163 match dt {
1164 DatatypeMessage::FloatingPoint {
1165 size,
1166 sign_location,
1167 bit_offset,
1168 bit_precision,
1169 exponent_location,
1170 exponent_size,
1171 mantissa_location,
1172 mantissa_size,
1173 exponent_bias,
1174 ..
1175 } => {
1176 let bits = *size * 8;
1177 let is_ieee32 = bits == 32
1178 && *bit_offset == 0
1179 && *bit_precision == 32
1180 && *sign_location == 31
1181 && *exponent_location == 23
1182 && *exponent_size == 8
1183 && *mantissa_location == 0
1184 && *mantissa_size == 23
1185 && *exponent_bias == 127;
1186 let is_ieee64 = bits == 64
1187 && *bit_offset == 0
1188 && *bit_precision == 64
1189 && *sign_location == 63
1190 && *exponent_location == 52
1191 && *exponent_size == 11
1192 && *mantissa_location == 0
1193 && *mantissa_size == 52
1194 && *exponent_bias == 1023;
1195 is_ieee32 || is_ieee64
1196 }
1197 _ => false,
1198 }
1199}
1200
1201pub fn datatype_needs_bit_conversion(dt: &DatatypeMessage) -> bool {
1215 match dt {
1216 DatatypeMessage::FixedPoint {
1217 size,
1218 bit_offset,
1219 bit_precision,
1220 ..
1221 } => *bit_offset != 0 || (*bit_precision as u32) < *size * 8,
1222 DatatypeMessage::FloatingPoint { .. } => !is_standard_ieee_float(dt),
1223 _ => false,
1224 }
1225}
1226
1227pub fn apply_datatype_conversion(buffer: &mut [u8], dt: &DatatypeMessage) -> FormatResult<()> {
1247 match dt {
1248 DatatypeMessage::FixedPoint {
1249 size,
1250 byte_order,
1251 signed,
1252 bit_offset,
1253 bit_precision,
1254 } => {
1255 let size = *size as usize;
1256 let precision = *bit_precision as usize;
1257 let offset = *bit_offset as usize;
1258
1259 if offset == 0 && precision == size * 8 {
1261 return Ok(());
1262 }
1263 if size == 0 || size > 8 {
1264 return Err(FormatError::InvalidData(format!(
1265 "datatype conversion: unsupported FixedPoint size {size}"
1266 )));
1267 }
1268 if precision == 0 || offset + precision > size * 8 {
1269 return Err(FormatError::InvalidData(format!(
1270 "datatype conversion: invalid bit layout (offset {offset}, \
1271 precision {precision}, size {size})"
1272 )));
1273 }
1274 if !buffer.len().is_multiple_of(size) {
1275 return Err(FormatError::InvalidData(format!(
1276 "datatype conversion: buffer length {} not a multiple of \
1277 element size {size}",
1278 buffer.len()
1279 )));
1280 }
1281
1282 let big_endian = matches!(byte_order, ByteOrder::BigEndian);
1283 let precision_mask: u64 = if precision == 64 {
1284 u64::MAX
1285 } else {
1286 (1u64 << precision) - 1
1287 };
1288 let sign_bit: u64 = 1u64 << (precision - 1);
1289
1290 for elem in buffer.chunks_exact_mut(size) {
1291 let mut raw: u64 = 0;
1293 if big_endian {
1294 for &b in elem.iter() {
1295 raw = (raw << 8) | b as u64;
1296 }
1297 } else {
1298 for (i, &b) in elem.iter().enumerate() {
1299 raw |= (b as u64) << (8 * i);
1300 }
1301 }
1302
1303 let mut value = (raw >> offset) & precision_mask;
1305
1306 if *signed && (value & sign_bit) != 0 {
1308 value |= !precision_mask;
1309 }
1310
1311 if big_endian {
1313 for i in 0..size {
1314 elem[size - 1 - i] = (value >> (8 * i)) as u8;
1315 }
1316 } else {
1317 for (i, b) in elem.iter_mut().enumerate() {
1318 *b = (value >> (8 * i)) as u8;
1319 }
1320 }
1321 }
1322 Ok(())
1323 }
1324 DatatypeMessage::FloatingPoint { .. } => {
1325 if is_standard_ieee_float(dt) {
1328 Ok(())
1329 } else {
1330 Err(FormatError::InvalidData(
1331 "datatype conversion: non-standard floating-point bit \
1332 layout cannot be converted"
1333 .into(),
1334 ))
1335 }
1336 }
1337 _ => Ok(()),
1338 }
1339}
1340
1341#[cfg(test)]
1345mod tests {
1346 use super::*;
1347
1348 fn nbit_atomic_cd(d_nelmts: u32, size: u32, precision: u32, offset: u32) -> Vec<u32> {
1350 let need_not_compress = if offset == 0 && precision == size * 8 {
1353 1
1354 } else {
1355 0
1356 };
1357 vec![
1358 8,
1359 need_not_compress,
1360 d_nelmts,
1361 NBIT_ATOMIC,
1362 size,
1363 NBIT_ORDER_LE,
1364 precision,
1365 offset,
1366 ]
1367 }
1368
1369 #[test]
1370 fn nbit_roundtrip_u16_precision12() {
1371 let values: Vec<u16> = (0..40u16).map(|i| (i * 71) & 0x0FFF).collect();
1373 let mut raw = Vec::new();
1374 for &v in &values {
1375 raw.extend_from_slice(&v.to_le_bytes());
1376 }
1377 let cd = nbit_atomic_cd(values.len() as u32, 2, 12, 0);
1378 let packed = apply_nbit(&raw, &cd, true).unwrap();
1379 assert!(packed.len() <= raw.len());
1380 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1381 assert_eq!(unpacked, raw);
1382 }
1383
1384 #[test]
1385 fn nbit_roundtrip_u32_precision20_offset4() {
1386 let values: Vec<u32> = (0..32u32).map(|i| ((i * 9999) & 0xFFFFF) << 4).collect();
1387 let mut raw = Vec::new();
1388 for &v in &values {
1389 raw.extend_from_slice(&v.to_le_bytes());
1390 }
1391 let cd = nbit_atomic_cd(values.len() as u32, 4, 20, 4);
1392 let packed = apply_nbit(&raw, &cd, true).unwrap();
1393 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1394 assert_eq!(unpacked, raw);
1395 }
1396
1397 #[test]
1398 fn nbit_passthrough_full_precision() {
1399 let raw: Vec<u8> = (0..64).collect();
1400 let cd = nbit_atomic_cd(16, 4, 32, 0); let packed = apply_nbit(&raw, &cd, true).unwrap();
1402 assert_eq!(packed, raw);
1403 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1404 assert_eq!(unpacked, raw);
1405 }
1406
1407 #[test]
1408 fn nbit_roundtrip_big_endian() {
1409 let values: Vec<u16> = (0..24u16).map(|i| (i * 53) & 0x03FF).collect();
1410 let mut raw = Vec::new();
1411 for &v in &values {
1412 raw.extend_from_slice(&v.to_be_bytes());
1413 }
1414 let mut cd = nbit_atomic_cd(values.len() as u32, 2, 10, 0);
1415 cd[5] = NBIT_ORDER_BE;
1416 let packed = apply_nbit(&raw, &cd, true).unwrap();
1417 let unpacked = apply_nbit(&packed, &cd, false).unwrap();
1418 assert_eq!(unpacked, raw);
1419 }
1420
1421 fn fixed(size: u32, signed: bool, offset: u16, precision: u16) -> DatatypeMessage {
1426 DatatypeMessage::FixedPoint {
1427 size,
1428 byte_order: ByteOrder::LittleEndian,
1429 signed,
1430 bit_offset: offset,
1431 bit_precision: precision,
1432 }
1433 }
1434
1435 #[test]
1436 fn conversion_noop_for_full_width_types() {
1437 let dt = fixed(4, false, 0, 32);
1439 assert!(!datatype_needs_bit_conversion(&dt));
1440 let mut buf = vec![0x78, 0x56, 0x34, 0x12, 0xFF, 0xFF, 0xFF, 0xFF];
1441 let before = buf.clone();
1442 apply_datatype_conversion(&mut buf, &dt).unwrap();
1443 assert_eq!(buf, before);
1444 }
1445
1446 #[test]
1447 fn conversion_noop_for_non_numeric_types() {
1448 let dt = DatatypeMessage::fixed_string(8);
1449 assert!(!datatype_needs_bit_conversion(&dt));
1450 let mut buf = b"hello!!\0".to_vec();
1451 let before = buf.clone();
1452 apply_datatype_conversion(&mut buf, &dt).unwrap();
1453 assert_eq!(buf, before);
1454 }
1455
1456 #[test]
1457 fn conversion_unsigned_offset_shifts_right() {
1458 let dt = fixed(2, false, 3, 10);
1462 assert!(datatype_needs_bit_conversion(&dt));
1463 let mut buf = (0x1528u16).to_le_bytes().to_vec();
1464 apply_datatype_conversion(&mut buf, &dt).unwrap();
1465 assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0x2A5);
1466 }
1467
1468 #[test]
1469 fn conversion_signed_negative_sign_extends() {
1470 let dt = fixed(2, true, 4, 8);
1473 let mut buf = (0x0FD0u16).to_le_bytes().to_vec();
1474 apply_datatype_conversion(&mut buf, &dt).unwrap();
1475 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), -3);
1476 }
1477
1478 #[test]
1479 fn conversion_signed_positive_stays_positive() {
1480 let dt = fixed(2, true, 4, 8);
1482 let mut buf = (0x0050u16).to_le_bytes().to_vec();
1483 apply_datatype_conversion(&mut buf, &dt).unwrap();
1484 assert_eq!(i16::from_le_bytes([buf[0], buf[1]]), 5);
1485 }
1486
1487 #[test]
1488 fn conversion_reduced_precision_offset_zero() {
1489 let dt = fixed(4, true, 0, 20);
1492 assert!(datatype_needs_bit_conversion(&dt));
1493 let mut buf = (0x000FFFFFu32).to_le_bytes().to_vec();
1494 apply_datatype_conversion(&mut buf, &dt).unwrap();
1495 assert_eq!(i32::from_le_bytes(buf.clone().try_into().unwrap()), -1);
1496 }
1497
1498 #[test]
1499 fn conversion_big_endian_signed() {
1500 let dt = DatatypeMessage::FixedPoint {
1502 size: 2,
1503 byte_order: ByteOrder::BigEndian,
1504 signed: true,
1505 bit_offset: 4,
1506 bit_precision: 8,
1507 };
1508 let mut buf = (0x0FD0u16).to_be_bytes().to_vec();
1509 apply_datatype_conversion(&mut buf, &dt).unwrap();
1510 assert_eq!(i16::from_be_bytes([buf[0], buf[1]]), -3);
1511 }
1512
1513 #[test]
1514 fn conversion_multiple_elements() {
1515 let dt = fixed(4, false, 5, 16);
1517 let vals: [u32; 3] = [0x1234, 0xABCD, 0x0001];
1518 let mut buf = Vec::new();
1519 for v in vals {
1520 buf.extend_from_slice(&(v << 5).to_le_bytes());
1521 }
1522 apply_datatype_conversion(&mut buf, &dt).unwrap();
1523 for (i, v) in vals.iter().enumerate() {
1524 let e = u32::from_le_bytes(buf[i * 4..i * 4 + 4].try_into().unwrap());
1525 assert_eq!(e, *v);
1526 }
1527 }
1528
1529 #[test]
1530 fn conversion_rejects_non_standard_float() {
1531 let dt = DatatypeMessage::FloatingPoint {
1533 size: 4,
1534 byte_order: ByteOrder::LittleEndian,
1535 sign_location: 30,
1536 bit_offset: 1,
1537 bit_precision: 31,
1538 exponent_location: 22,
1539 exponent_size: 8,
1540 mantissa_location: 0,
1541 mantissa_size: 22,
1542 exponent_bias: 127,
1543 };
1544 assert!(datatype_needs_bit_conversion(&dt));
1545 let mut buf = vec![0u8; 4];
1546 assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1547 }
1548
1549 #[test]
1550 fn conversion_standard_float_is_noop() {
1551 let dt = DatatypeMessage::f64_type();
1552 assert!(!datatype_needs_bit_conversion(&dt));
1553 let mut buf = 12.5f64.to_le_bytes().to_vec();
1554 let before = buf.clone();
1555 apply_datatype_conversion(&mut buf, &dt).unwrap();
1556 assert_eq!(buf, before);
1557 }
1558
1559 #[test]
1560 fn conversion_rejects_bad_buffer_length() {
1561 let dt = fixed(4, false, 3, 16);
1562 let mut buf = vec![0u8; 5]; assert!(apply_datatype_conversion(&mut buf, &dt).is_err());
1564 }
1565}