1use crate::error::{Error, Result};
4use grib_core::binary::decode_ieee_binary128;
5use grib_core::bit::BitReader;
6pub use grib_core::data::{
7 CcsdsBlockSize, CcsdsFlags, CcsdsPackingParams, ComplexPackingParams, DataRepresentation,
8 Grib1SecondOrderLayout, Grib1SecondOrderPackingParams, IeeePackingParams, IeeePrecision,
9 Jpeg2000PackingParams, PngPackingParams, RunLengthPackingParams, ScaledPackingParams,
10 SpatialDifferencingParams,
11};
12use grib_core::filled_vec;
13
14#[cfg(feature = "ccsds")]
15use grib_aec::{decode as aec_decode, AecFlags, AecParams};
16
17pub trait DecodeSample: Copy + Sized {
19 fn from_f64(value: f64) -> Self;
20 fn nan() -> Self;
21}
22
23impl DecodeSample for f32 {
24 fn from_f64(value: f64) -> Self {
25 value as f32
26 }
27
28 fn nan() -> Self {
29 f32::NAN
30 }
31}
32
33impl DecodeSample for f64 {
34 fn from_f64(value: f64) -> Self {
35 value
36 }
37
38 fn nan() -> Self {
39 f64::NAN
40 }
41}
42
43pub fn decode_field(
45 data_section: &[u8],
46 representation: &DataRepresentation,
47 bitmap_section: Option<&[u8]>,
48 num_grid_points: usize,
49) -> Result<Vec<f64>> {
50 if data_section.len() < 5 || data_section[4] != 7 {
51 return Err(Error::InvalidSection {
52 section: data_section.get(4).copied().unwrap_or(7),
53 reason: "not a data section".into(),
54 });
55 }
56
57 decode_payload(
58 &data_section[5..],
59 representation,
60 bitmap_section,
61 num_grid_points,
62 )
63}
64
65pub fn decode_field_into<T: DecodeSample>(
68 data_section: &[u8],
69 representation: &DataRepresentation,
70 bitmap_section: Option<&[u8]>,
71 num_grid_points: usize,
72 out: &mut [T],
73) -> Result<()> {
74 if data_section.len() < 5 || data_section[4] != 7 {
75 return Err(Error::InvalidSection {
76 section: data_section.get(4).copied().unwrap_or(7),
77 reason: "not a data section".into(),
78 });
79 }
80
81 decode_payload_into_with_group_lengths(
82 &data_section[5..],
83 representation,
84 bitmap_section,
85 num_grid_points,
86 out,
87 None,
88 )
89}
90
91pub(crate) fn decode_field_into_with_group_lengths<T: DecodeSample>(
92 data_section: &[u8],
93 representation: &DataRepresentation,
94 bitmap_section: Option<&[u8]>,
95 num_grid_points: usize,
96 out: &mut [T],
97 row_group_lengths: Option<&[usize]>,
98) -> Result<()> {
99 if data_section.len() < 5 || data_section[4] != 7 {
100 return Err(Error::InvalidSection {
101 section: data_section.get(4).copied().unwrap_or(7),
102 reason: "not a data section".into(),
103 });
104 }
105
106 decode_payload_into_with_group_lengths(
107 &data_section[5..],
108 representation,
109 bitmap_section,
110 num_grid_points,
111 out,
112 row_group_lengths,
113 )
114}
115
116pub(crate) fn decode_payload(
117 payload: &[u8],
118 representation: &DataRepresentation,
119 bitmap_section: Option<&[u8]>,
120 num_grid_points: usize,
121) -> Result<Vec<f64>> {
122 let mut values = filled_vec(num_grid_points, 0.0, "decoded field")?;
123 decode_payload_into_with_group_lengths(
124 payload,
125 representation,
126 bitmap_section,
127 num_grid_points,
128 &mut values,
129 None,
130 )?;
131 Ok(values)
132}
133
134pub(crate) fn decode_payload_into_with_group_lengths<T: DecodeSample>(
135 payload: &[u8],
136 representation: &DataRepresentation,
137 bitmap_section: Option<&[u8]>,
138 num_grid_points: usize,
139 out: &mut [T],
140 row_group_lengths: Option<&[usize]>,
141) -> Result<()> {
142 if out.len() != num_grid_points {
143 return Err(Error::DataLengthMismatch {
144 expected: num_grid_points,
145 actual: out.len(),
146 });
147 }
148
149 let expected_values = match representation {
150 DataRepresentation::SimplePacking(params) => params.encoded_values,
151 DataRepresentation::Grib1SecondOrderPacking(params) => params.packing().encoded_values,
152 DataRepresentation::ComplexPacking(params) => params.encoded_values,
153 DataRepresentation::IeeePacking(params) => params.encoded_values,
154 DataRepresentation::Jpeg2000Packing(params) => params.packing.encoded_values,
155 DataRepresentation::PngPacking(params) => params.packing.encoded_values,
156 DataRepresentation::CcsdsPacking(params) => params.packing().encoded_values,
157 DataRepresentation::RunLengthPacking(params) => params.encoded_values,
158 DataRepresentation::Unsupported(template) => {
159 return Err(Error::UnsupportedDataTemplate(*template));
160 }
161 _ => {
162 return Err(Error::InvalidMessage(
163 "data representation variant is not supported by this reader".into(),
164 ));
165 }
166 };
167 match bitmap_section {
168 Some(bitmap_payload) => {
169 let present_values = count_bitmap_present_points(bitmap_payload, num_grid_points)?;
170 if expected_values != present_values {
171 return Err(Error::DataLengthMismatch {
172 expected: present_values,
173 actual: expected_values,
174 });
175 }
176 }
177 None if expected_values != num_grid_points => {
178 return Err(Error::DataLengthMismatch {
179 expected: num_grid_points,
180 actual: expected_values,
181 });
182 }
183 None => {}
184 }
185
186 let mut output = OutputCursor::new(out, bitmap_section);
187 if let Some(constant) = zero_width_constant(representation, payload)? {
188 fill_present(&mut output, T::from_f64(constant), expected_values)?;
189 return output.finish();
190 }
191 match representation {
192 DataRepresentation::SimplePacking(params) => {
193 unpack_simple_into(payload, params, expected_values, &mut output)?
194 }
195 DataRepresentation::Grib1SecondOrderPacking(params) => {
196 unpack_grib1_second_order_into(payload, params, row_group_lengths, &mut output)?
197 }
198 DataRepresentation::ComplexPacking(params) => {
199 unpack_complex_into(payload, params, row_group_lengths, &mut output)?
200 }
201 DataRepresentation::IeeePacking(params) => {
202 unpack_ieee_into(payload, params, expected_values, &mut output)?
203 }
204 DataRepresentation::Jpeg2000Packing(params) => {
205 unpack_jpeg2000_into(payload, params, expected_values, &mut output)?
206 }
207 DataRepresentation::PngPacking(params) => {
208 unpack_png_into(payload, params, expected_values, &mut output)?
209 }
210 DataRepresentation::CcsdsPacking(params) => {
211 unpack_ccsds_into(payload, params, expected_values, &mut output)?
212 }
213 DataRepresentation::RunLengthPacking(params) => {
214 unpack_run_length_into(payload, params, expected_values, &mut output)?
215 }
216 DataRepresentation::Unsupported(_) => unreachable!(),
217 _ => unreachable!("unsupported representation rejected before decoding"),
218 }
219 output.finish()
220}
221
222fn zero_width_constant(representation: &DataRepresentation, payload: &[u8]) -> Result<Option<f64>> {
229 let packing = match representation {
230 DataRepresentation::SimplePacking(params) => params,
231 DataRepresentation::Jpeg2000Packing(params) => ¶ms.packing,
232 DataRepresentation::PngPacking(params) => ¶ms.packing,
233 DataRepresentation::CcsdsPacking(params) => {
234 let packing = params.packing();
235 if packing.bits_per_value == 0 && !payload.is_empty() {
236 return Err(Error::DataLengthMismatch {
237 expected: 0,
238 actual: payload.len(),
239 });
240 }
241 packing
242 }
243 _ => return Ok(None),
244 };
245 Ok((packing.bits_per_value == 0).then(|| f64::from(packing.reference_value)))
246}
247
248fn fill_present<T: DecodeSample>(
249 output: &mut OutputCursor<'_, T>,
250 value: T,
251 count: usize,
252) -> Result<()> {
253 if let Some(dense) = output.take_dense(count)? {
254 dense.fill(value);
255 } else {
256 for _ in 0..count {
257 output.push_present(value)?;
258 }
259 }
260 Ok(())
261}
262
263fn unpack_ieee_into<T: DecodeSample>(
264 data_bytes: &[u8],
265 params: &IeeePackingParams,
266 num_values: usize,
267 output: &mut OutputCursor<'_, T>,
268) -> Result<()> {
269 let expected_bytes = num_values
270 .checked_mul(params.precision.bytes_per_value())
271 .ok_or(Error::ArithmeticOverflow {
272 operation: "computing IEEE-packed byte count",
273 })?;
274 if data_bytes.len() != expected_bytes {
275 return Err(Error::DataLengthMismatch {
276 expected: expected_bytes,
277 actual: data_bytes.len(),
278 });
279 }
280
281 macro_rules! decode_values {
282 ($chunks:expr, $decode:expr) => {{
283 if let Some(dense) = output.take_dense(num_values)? {
284 for (target, bytes) in dense.iter_mut().zip($chunks) {
285 *target = T::from_f64($decode(bytes));
286 }
287 } else {
288 for bytes in $chunks {
289 output.push_present(T::from_f64($decode(bytes)))?;
290 }
291 }
292 }};
293 }
294
295 match params.precision {
296 IeeePrecision::Single => decode_values!(data_bytes.chunks_exact(4), |bytes: &[u8]| {
297 f64::from(f32::from_be_bytes(
298 bytes.try_into().expect("four-byte IEEE chunk"),
299 ))
300 }),
301 IeeePrecision::Double => decode_values!(data_bytes.chunks_exact(8), |bytes: &[u8]| {
302 f64::from_be_bytes(bytes.try_into().expect("eight-byte IEEE chunk"))
303 }),
304 IeeePrecision::Quadruple => {
305 decode_values!(data_bytes.chunks_exact(16), |bytes: &[u8]| {
306 decode_ieee_binary128(bytes.try_into().expect("sixteen-byte IEEE chunk"))
307 })
308 }
309 _ => {
310 return Err(Error::UnsupportedPackingWidth(
311 params.precision.bits_per_value(),
312 ));
313 }
314 }
315
316 Ok(())
317}
318
319fn unpack_run_length_into<T: DecodeSample>(
320 data_bytes: &[u8],
321 params: &RunLengthPackingParams,
322 num_values: usize,
323 output: &mut OutputCursor<'_, T>,
324) -> Result<()> {
325 if params.bits_per_value > 64 {
326 return Err(Error::InvalidSection {
327 section: 5,
328 reason: format!(
329 "template 5.200 bits per value must not exceed 64, got {}",
330 params.bits_per_value
331 ),
332 });
333 }
334 if params.max_level_value == 0 {
335 if !params.level_values.is_empty() {
336 return Err(Error::InvalidSection {
337 section: 5,
338 reason: format!(
339 "template 5.200 zero maximum level requires an empty level table, got {} entries",
340 params.level_values.len()
341 ),
342 });
343 }
344 if !remaining_bits_are_zero(data_bytes, 0)? {
345 return Err(invalid_run_length_data(
346 "all-missing field contains nonzero packed data",
347 ));
348 }
349 let missing = T::nan();
350 if let Some(dense) = output.take_dense(num_values)? {
351 dense.fill(missing);
352 } else {
353 for _ in 0..num_values {
354 output.push_present(missing)?;
355 }
356 }
357 return Ok(());
358 }
359
360 let bits = usize::from(params.bits_per_value);
361 if !(1..=64).contains(&bits) {
362 return Err(Error::InvalidSection {
363 section: 5,
364 reason: format!(
365 "template 5.200 bits per value must be between 1 and 64, got {}",
366 params.bits_per_value
367 ),
368 });
369 }
370 if usize::from(params.max_level_value) > params.level_values.len() {
371 return Err(Error::InvalidSection {
372 section: 5,
373 reason: format!(
374 "template 5.200 maximum level {} exceeds the {}-entry level table",
375 params.max_level_value,
376 params.level_values.len()
377 ),
378 });
379 }
380 let max_code = if bits == 64 {
381 u64::MAX
382 } else {
383 (1_u64 << bits) - 1
384 };
385 let range = max_code
386 .checked_sub(u64::from(params.max_level_value))
387 .filter(|range| *range > 0)
388 .ok_or_else(|| Error::InvalidSection {
389 section: 5,
390 reason: format!(
391 "template 5.200 maximum level {} leaves no run-length codes at {bits} bits",
392 params.max_level_value
393 ),
394 })?;
395 let data_bits = data_bytes
396 .len()
397 .checked_mul(8)
398 .ok_or(Error::BitOffsetOverflow)?;
399 let decimal_factor = 10.0_f64.powi(-i32::from(params.decimal_scale));
400 let mut reader = BitReader::new(data_bytes);
401 let mut decoded_values = 0usize;
402
403 while decoded_values < num_values {
404 if reader
405 .bit_offset()
406 .checked_add(bits)
407 .ok_or(Error::BitOffsetOverflow)?
408 > data_bits
409 {
410 return Err(Error::DataLengthMismatch {
411 expected: num_values,
412 actual: decoded_values,
413 });
414 }
415
416 let level = reader.read(bits)?;
417 if level > u64::from(params.max_level_value) {
418 return Err(invalid_run_length_data(format!(
419 "run starts with code {level}, above maximum level {}",
420 params.max_level_value
421 )));
422 }
423
424 let remaining_values = num_values - decoded_values;
425 let run_limit = remaining_values as u128;
426 let mut run_length = 1_u128;
427 let mut factor = 1_u128;
428 loop {
429 if reader
430 .bit_offset()
431 .checked_add(bits)
432 .ok_or(Error::BitOffsetOverflow)?
433 > data_bits
434 {
435 break;
436 }
437
438 let mut lookahead = reader;
439 let code = lookahead.read(bits)?;
440 if code <= u64::from(params.max_level_value) {
441 break;
442 }
443 reader = lookahead;
444
445 let digit = u128::from(code - u64::from(params.max_level_value) - 1);
446 let addition = factor.checked_mul(digit).ok_or(Error::ArithmeticOverflow {
447 operation: "decoding a template 5.200 run length",
448 })?;
449 run_length = run_length
450 .checked_add(addition)
451 .ok_or(Error::ArithmeticOverflow {
452 operation: "decoding a template 5.200 run length",
453 })?;
454 if run_length > run_limit {
455 return Err(Error::DataLengthMismatch {
456 expected: num_values,
457 actual: num_values.saturating_add(1),
458 });
459 }
460 factor = factor.saturating_mul(u128::from(range)).min(run_limit + 1);
461 }
462
463 let run_length = usize::try_from(run_length).map_err(|_| Error::ArithmeticOverflow {
464 operation: "converting a template 5.200 run length",
465 })?;
466 let value = if level == 0 {
467 T::nan()
468 } else {
469 let scaled = params.level_values[usize::try_from(level - 1).map_err(|_| {
470 Error::ArithmeticOverflow {
471 operation: "indexing a template 5.200 level",
472 }
473 })?];
474 T::from_f64(f64::from(scaled) * decimal_factor)
475 };
476 if let Some(dense) = output.take_dense(run_length)? {
477 dense.fill(value);
478 } else {
479 for _ in 0..run_length {
480 output.push_present(value)?;
481 }
482 }
483 decoded_values += run_length;
484 }
485
486 if !remaining_bits_are_zero(data_bytes, reader.bit_offset())? {
487 return Err(invalid_run_length_data(
488 "nonzero bits remain after the declared number of values",
489 ));
490 }
491 Ok(())
492}
493
494fn remaining_bits_are_zero(data: &[u8], start_bit: usize) -> Result<bool> {
495 let data_bits = data.len().checked_mul(8).ok_or(Error::BitOffsetOverflow)?;
496 if start_bit > data_bits {
497 return Err(Error::BitOffsetOverflow);
498 }
499 let mut byte_index = start_bit / 8;
500 let bit_index = start_bit % 8;
501 if bit_index != 0 {
502 let remaining_mask = u8::MAX >> bit_index;
503 if data[byte_index] & remaining_mask != 0 {
504 return Ok(false);
505 }
506 byte_index += 1;
507 }
508 Ok(data[byte_index..].iter().all(|byte| *byte == 0))
509}
510
511fn invalid_run_length_data(reason: impl Into<String>) -> Error {
512 Error::InvalidSection {
513 section: 7,
514 reason: format!("invalid template 5.200 data: {}", reason.into()),
515 }
516}
517
518pub fn bitmap_payload(section_bytes: &[u8]) -> Result<Option<&[u8]>> {
520 if section_bytes.len() < 6 {
521 return Err(Error::InvalidSection {
522 section: 6,
523 reason: format!("expected at least 6 bytes, got {}", section_bytes.len()),
524 });
525 }
526 if section_bytes[4] != 6 {
527 return Err(Error::InvalidSection {
528 section: section_bytes[4],
529 reason: "not a bitmap section".into(),
530 });
531 }
532
533 match section_bytes[5] {
534 255 => Ok(None),
535 0 => Ok(Some(§ion_bytes[6..])),
536 indicator => Err(Error::UnsupportedBitmapIndicator(u16::from(indicator))),
537 }
538}
539
540pub(crate) fn count_bitmap_present_points(
541 bitmap_payload: &[u8],
542 num_grid_points: usize,
543) -> Result<usize> {
544 let full_bytes = num_grid_points / 8;
545 let remaining_bits = num_grid_points % 8;
546 let required_bytes = full_bytes + usize::from(remaining_bits > 0);
547 if bitmap_payload.len() < required_bytes {
548 return Err(Error::DataLengthMismatch {
549 expected: required_bytes,
550 actual: bitmap_payload.len(),
551 });
552 }
553
554 let mut present = bitmap_payload[..full_bytes]
555 .iter()
556 .map(|byte| byte.count_ones() as usize)
557 .sum();
558 if remaining_bits > 0 {
559 let mask = u8::MAX << (8 - remaining_bits);
560 present += (bitmap_payload[full_bytes] & mask).count_ones() as usize;
561 }
562
563 Ok(present)
564}
565
566pub fn unpack_simple(
568 data_bytes: &[u8],
569 params: &ScaledPackingParams,
570 num_values: usize,
571) -> Result<Vec<f64>> {
572 let mut values = filled_vec(num_values, 0.0, "simple-packed field")?;
573 let mut output = OutputCursor::new(&mut values, None);
574 unpack_simple_into(data_bytes, params, num_values, &mut output)?;
575 output.finish()?;
576 Ok(values)
577}
578
579fn unpack_simple_into<T: DecodeSample>(
580 data_bytes: &[u8],
581 params: &ScaledPackingParams,
582 num_values: usize,
583 output: &mut OutputCursor<'_, T>,
584) -> Result<()> {
585 let bits = params.bits_per_value as usize;
586 let binary_factor = 2.0_f64.powi(params.binary_scale as i32);
587 let decimal_factor = 10.0_f64.powi(-(params.decimal_scale as i32));
588 let reference = params.reference_value as f64;
589 if bits == 0 {
590 return fill_present(output, T::from_f64(reference), num_values);
591 }
592 if bits > u64::BITS as usize {
593 return Err(Error::UnsupportedPackingWidth(params.bits_per_value));
594 }
595
596 let required_bits = bits
597 .checked_mul(num_values)
598 .ok_or(Error::ArithmeticOverflow {
599 operation: "computing packed bit count",
600 })?;
601 let required_bytes = required_bits.div_ceil(8);
602 if data_bytes.len() < required_bytes {
603 return Err(Error::Truncated {
604 offset: data_bytes.len() as u64,
605 });
606 }
607
608 if let Some(dense) = output.take_dense(num_values)? {
609 return unpack_simple_dense(
610 data_bytes,
611 bits,
612 reference,
613 binary_factor,
614 decimal_factor,
615 dense,
616 );
617 }
618
619 let mut reader = BitReader::new(data_bytes);
620 for _ in 0..num_values {
621 let packed = reader.read(bits)?;
622 output.push_present(T::from_f64(scale_decoded_value(
623 reference,
624 packed as f64,
625 binary_factor,
626 decimal_factor,
627 )))?;
628 }
629
630 Ok(())
631}
632
633fn unpack_simple_dense<T: DecodeSample>(
634 data_bytes: &[u8],
635 bits: usize,
636 reference: f64,
637 binary_factor: f64,
638 decimal_factor: f64,
639 output: &mut [T],
640) -> Result<()> {
641 let scale = |packed: u64| {
642 T::from_f64(scale_decoded_value(
643 reference,
644 packed as f64,
645 binary_factor,
646 decimal_factor,
647 ))
648 };
649
650 match bits {
651 8 => {
652 for (target, &packed) in output.iter_mut().zip(data_bytes) {
653 *target = scale(u64::from(packed));
654 }
655 }
656 16 => {
657 for (target, bytes) in output.iter_mut().zip(data_bytes.chunks_exact(2)) {
658 *target = scale(u64::from(u16::from_be_bytes(
659 bytes.try_into().expect("two-byte chunk"),
660 )));
661 }
662 }
663 32 => {
664 for (target, bytes) in output.iter_mut().zip(data_bytes.chunks_exact(4)) {
665 *target = scale(u64::from(u32::from_be_bytes(
666 bytes.try_into().expect("four-byte chunk"),
667 )));
668 }
669 }
670 64 => {
671 for (target, bytes) in output.iter_mut().zip(data_bytes.chunks_exact(8)) {
672 *target = scale(u64::from_be_bytes(
673 bytes.try_into().expect("eight-byte chunk"),
674 ));
675 }
676 }
677 _ => {
678 let mut reader = BitReader::new(data_bytes);
679 for target in output {
680 *target = scale(reader.read(bits)?);
681 }
682 }
683 }
684 Ok(())
685}
686
687#[cfg(feature = "ccsds")]
688fn unpack_ccsds_into<T: DecodeSample>(
689 data_bytes: &[u8],
690 params: &CcsdsPackingParams,
691 num_values: usize,
692 output: &mut OutputCursor<'_, T>,
693) -> Result<()> {
694 let packing = params.packing();
695 let bits = packing.bits_per_value;
696
697 if num_values == 0 {
698 if data_bytes.is_empty() {
699 return Ok(());
700 }
701 return Err(Error::DataLengthMismatch {
702 expected: 0,
703 actual: data_bytes.len(),
704 });
705 }
706
707 let bytes_per_sample = usize::from(bits).div_ceil(8);
708 let decoded_size =
709 num_values
710 .checked_mul(bytes_per_sample)
711 .ok_or(Error::ArithmeticOverflow {
712 operation: "computing CCSDS decoded byte count",
713 })?;
714 let codec_params = AecParams::new(
715 bits,
716 params.block_size().samples(),
717 params.reference_sample_interval(),
718 ccsds_decoder_flags(params.flags()),
719 )
720 .map_err(|error| Error::Codec {
721 codec: "CCSDS/AEC",
722 operation: "configure",
723 reason: error.to_string(),
724 })?;
725 let decoded =
726 aec_decode(data_bytes, num_values, codec_params).map_err(|error| Error::Codec {
727 codec: "CCSDS/AEC",
728 operation: "decode",
729 reason: error.to_string(),
730 })?;
731 if decoded.len() != decoded_size {
732 return Err(Error::DataLengthMismatch {
733 expected: decoded_size,
734 actual: decoded.len(),
735 });
736 }
737
738 let value_mask = if bits == 32 {
739 u32::MAX
740 } else {
741 (1u32 << bits) - 1
742 };
743 for bytes in decoded.chunks_exact(bytes_per_sample) {
744 let container = bytes
745 .iter()
746 .fold(0u32, |value, byte| (value << 8) | u32::from(*byte));
747 let raw = container & value_mask;
748 let signed = params.flags().contains(CcsdsFlags::SIGNED);
749 let high_mask = if bytes_per_sample == 4 {
750 !value_mask
751 } else {
752 ((1u32 << (bytes_per_sample * 8)) - 1) & !value_mask
753 };
754 let expected_high = if signed && raw & (1u32 << (bits - 1)) != 0 {
755 high_mask
756 } else {
757 0
758 };
759 if container & high_mask != expected_high {
760 return Err(Error::ValueOutOfRange(format!(
761 "CCSDS sample container 0x{container:08x} has invalid extension bits for its {bits}-bit width"
762 )));
763 }
764 output.push_present(scale_ccsds_value(packing, f64::from(raw)))?;
768 }
769 Ok(())
770}
771
772#[cfg(feature = "ccsds")]
773fn ccsds_decoder_flags(flags: CcsdsFlags) -> AecFlags {
774 let mut codec_flags = AecFlags::DATA_MSB | AecFlags::DATA_3BYTE;
778 for (wire_flag, codec_flag) in [
779 (CcsdsFlags::SIGNED, AecFlags::DATA_SIGNED),
780 (CcsdsFlags::PREPROCESS, AecFlags::DATA_PREPROCESS),
781 (CcsdsFlags::RESTRICTED, AecFlags::RESTRICTED),
782 (CcsdsFlags::PAD_RSI, AecFlags::PAD_RSI),
783 (CcsdsFlags::NOT_ENFORCE, AecFlags::NOT_ENFORCE),
784 ] {
785 if flags.contains(wire_flag) {
786 codec_flags |= codec_flag;
787 }
788 }
789 codec_flags
790}
791
792#[cfg(feature = "ccsds")]
793fn scale_ccsds_value<T: DecodeSample>(params: &ScaledPackingParams, packed: f64) -> T {
794 T::from_f64(scale_decoded_value(
795 f64::from(params.reference_value),
796 packed,
797 2.0_f64.powi(i32::from(params.binary_scale)),
798 10.0_f64.powi(-i32::from(params.decimal_scale)),
799 ))
800}
801
802#[cfg(not(feature = "ccsds"))]
803fn unpack_ccsds_into<T: DecodeSample>(
804 _data_bytes: &[u8],
805 _params: &CcsdsPackingParams,
806 _num_values: usize,
807 _output: &mut OutputCursor<'_, T>,
808) -> Result<()> {
809 Err(Error::UnsupportedDataTemplate(42))
810}
811
812#[cfg(feature = "jpeg2000")]
813fn unpack_jpeg2000_into<T: DecodeSample>(
814 data_bytes: &[u8],
815 params: &Jpeg2000PackingParams,
816 num_values: usize,
817 output: &mut OutputCursor<'_, T>,
818) -> Result<()> {
819 validate_jpeg2000_bits(params.packing.bits_per_value)?;
820
821 let header = parse_jpeg2000_header(data_bytes)?;
826 if header.components != 1 {
827 return Err(invalid_data_section(format!(
828 "JPEG 2000 GRIB packing requires one component, got {}",
829 header.components
830 )));
831 }
832 if header.is_signed {
833 return Err(invalid_data_section(
834 "JPEG 2000 GRIB packing requires unsigned component data",
835 ));
836 }
837 if header.precision > params.packing.bits_per_value {
838 return Err(invalid_data_section(format!(
839 "JPEG 2000 component precision {} exceeds GRIB bits-per-value {}",
840 header.precision, params.packing.bits_per_value
841 )));
842 }
843
844 let sample_count = image_sample_count(header.width, header.height)?;
845 if sample_count != num_values {
846 return Err(Error::DataLengthMismatch {
847 expected: num_values,
848 actual: sample_count,
849 });
850 }
851
852 let image = jpeg2k::Image::from_bytes(data_bytes).map_err(|err| Error::Codec {
853 codec: "JPEG 2000",
854 operation: "decode",
855 reason: err.to_string(),
856 })?;
857 if image.num_components() != 1 {
858 return Err(invalid_data_section(format!(
859 "JPEG 2000 GRIB packing requires one component, got {}",
860 image.num_components()
861 )));
862 }
863 let component = image
864 .components()
865 .first()
866 .ok_or_else(|| invalid_data_section("JPEG 2000 image has no components"))?;
867 if component.is_signed() {
868 return Err(invalid_data_section(
869 "JPEG 2000 GRIB packing requires unsigned component data",
870 ));
871 }
872 if component.precision() > u32::from(params.packing.bits_per_value) {
873 return Err(invalid_data_section(format!(
874 "JPEG 2000 component precision {} exceeds GRIB bits-per-value {}",
875 component.precision(),
876 params.packing.bits_per_value
877 )));
878 }
879 let decoded_sample_count = image_sample_count(component.width(), component.height())?;
880 if decoded_sample_count != num_values {
881 return Err(Error::DataLengthMismatch {
882 expected: num_values,
883 actual: decoded_sample_count,
884 });
885 }
886
887 for &sample in component.data() {
888 let raw = jpeg2000_raw_value(sample, params.packing.bits_per_value)?;
889 push_image_value(output, ¶ms.packing, raw)?;
890 }
891
892 Ok(())
893}
894
895#[cfg(feature = "jpeg2000")]
896#[derive(Debug, Clone, Copy, PartialEq, Eq)]
897struct Jpeg2000Header {
898 width: u32,
899 height: u32,
900 components: u16,
901 precision: u8,
902 is_signed: bool,
903}
904
905#[cfg(feature = "jpeg2000")]
906fn parse_jpeg2000_header(data: &[u8]) -> Result<Jpeg2000Header> {
907 let codestream = jpeg2000_codestream(data)?;
908 if codestream.get(..4) != Some(&[0xff, 0x4f, 0xff, 0x51]) {
909 return Err(invalid_jpeg2000_header(
910 "codestream does not begin with SOC followed by SIZ",
911 ));
912 }
913
914 let segment_length = usize::from(u16::from_be_bytes(codestream[4..6].try_into().unwrap()));
915 let segment_end = 4usize
916 .checked_add(segment_length)
917 .ok_or_else(|| invalid_jpeg2000_header("SIZ segment length overflow"))?;
918 let segment = codestream
919 .get(4..segment_end)
920 .ok_or_else(|| invalid_jpeg2000_header("truncated SIZ segment"))?;
921 if segment.len() < 38 {
922 return Err(invalid_jpeg2000_header("SIZ segment is too short"));
923 }
924
925 let components = u16::from_be_bytes(segment[36..38].try_into().unwrap());
926 let required_length = usize::from(components)
927 .checked_mul(3)
928 .and_then(|component_bytes| 38usize.checked_add(component_bytes))
929 .ok_or_else(|| invalid_jpeg2000_header("SIZ component table length overflow"))?;
930 if segment_length != required_length {
931 return Err(invalid_jpeg2000_header(format!(
932 "SIZ length {segment_length} does not match {components} components"
933 )));
934 }
935 if components == 0 {
936 return Err(invalid_jpeg2000_header("SIZ declares zero components"));
937 }
938
939 let xsiz = u32::from_be_bytes(segment[4..8].try_into().unwrap());
940 let ysiz = u32::from_be_bytes(segment[8..12].try_into().unwrap());
941 let xosiz = u32::from_be_bytes(segment[12..16].try_into().unwrap());
942 let yosiz = u32::from_be_bytes(segment[16..20].try_into().unwrap());
943 let xrsiz = u32::from(segment[39]);
944 let yrsiz = u32::from(segment[40]);
945 if xrsiz == 0 || yrsiz == 0 {
946 return Err(invalid_jpeg2000_header(
947 "SIZ component subsampling factors must be nonzero",
948 ));
949 }
950 let width = ceil_div_u32(xsiz, xrsiz)
951 .checked_sub(ceil_div_u32(xosiz, xrsiz))
952 .filter(|width| *width > 0)
953 .ok_or_else(|| invalid_jpeg2000_header("SIZ has an invalid horizontal extent"))?;
954 let height = ceil_div_u32(ysiz, yrsiz)
955 .checked_sub(ceil_div_u32(yosiz, yrsiz))
956 .filter(|height| *height > 0)
957 .ok_or_else(|| invalid_jpeg2000_header("SIZ has an invalid vertical extent"))?;
958 let sample_format = segment[38];
959
960 Ok(Jpeg2000Header {
961 width,
962 height,
963 components,
964 precision: (sample_format & 0x7f) + 1,
965 is_signed: sample_format & 0x80 != 0,
966 })
967}
968
969#[cfg(feature = "jpeg2000")]
970fn jpeg2000_codestream(data: &[u8]) -> Result<&[u8]> {
971 if data.starts_with(&[0xff, 0x4f]) {
972 return Ok(data);
973 }
974
975 let mut offset = 0usize;
976 while offset
977 .checked_add(8)
978 .is_some_and(|header_end| header_end <= data.len())
979 {
980 let short_length = u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap());
981 let box_type = &data[offset + 4..offset + 8];
982 let (header_length, box_length) = match short_length {
983 0 => (8usize, data.len() - offset),
984 1 => {
985 let extended_end = offset
986 .checked_add(16)
987 .ok_or_else(|| invalid_jpeg2000_header("JP2 box header overflow"))?;
988 let extended = data
989 .get(offset + 8..extended_end)
990 .ok_or_else(|| invalid_jpeg2000_header("truncated extended JP2 box"))?;
991 let length = usize::try_from(u64::from_be_bytes(extended.try_into().unwrap()))
992 .map_err(|_| invalid_jpeg2000_header("JP2 box length exceeds usize"))?;
993 (16usize, length)
994 }
995 length => (8usize, length as usize),
996 };
997 if box_length < header_length {
998 return Err(invalid_jpeg2000_header(
999 "JP2 box is shorter than its header",
1000 ));
1001 }
1002 let box_end = offset
1003 .checked_add(box_length)
1004 .filter(|end| *end <= data.len())
1005 .ok_or_else(|| invalid_jpeg2000_header("JP2 box extends past the payload"))?;
1006 if box_type == b"jp2c" {
1007 return data
1008 .get(offset + header_length..box_end)
1009 .ok_or_else(|| invalid_jpeg2000_header("truncated JP2 codestream box"));
1010 }
1011 if short_length == 0 {
1012 break;
1013 }
1014 offset = box_end;
1015 }
1016
1017 Err(invalid_jpeg2000_header(
1018 "payload is neither a raw codestream nor a JP2 file with a codestream box",
1019 ))
1020}
1021
1022#[cfg(feature = "jpeg2000")]
1023const fn ceil_div_u32(value: u32, divisor: u32) -> u32 {
1024 value / divisor + (!value.is_multiple_of(divisor)) as u32
1025}
1026
1027#[cfg(feature = "jpeg2000")]
1028fn invalid_jpeg2000_header(reason: impl Into<String>) -> Error {
1029 invalid_data_section(format!("invalid JPEG 2000 header: {}", reason.into()))
1030}
1031
1032#[cfg(any(feature = "jpeg2000", feature = "png"))]
1033fn invalid_data_section(reason: impl Into<String>) -> Error {
1034 Error::InvalidSection {
1035 section: 7,
1036 reason: reason.into(),
1037 }
1038}
1039
1040#[cfg(not(feature = "jpeg2000"))]
1041fn unpack_jpeg2000_into<T: DecodeSample>(
1042 _data_bytes: &[u8],
1043 _params: &Jpeg2000PackingParams,
1044 _num_values: usize,
1045 _output: &mut OutputCursor<'_, T>,
1046) -> Result<()> {
1047 Err(Error::UnsupportedDataTemplate(40))
1048}
1049
1050#[cfg(feature = "jpeg2000")]
1051fn validate_jpeg2000_bits(bits_per_value: u8) -> Result<()> {
1052 if !(1..=32).contains(&bits_per_value) {
1053 return Err(Error::UnsupportedPackingWidth(bits_per_value));
1054 }
1055 Ok(())
1056}
1057
1058#[cfg(feature = "jpeg2000")]
1059fn jpeg2000_raw_value(sample: i32, bits_per_value: u8) -> Result<u64> {
1060 let raw = if bits_per_value == 32 {
1061 u64::from(u32::from_ne_bytes(sample.to_ne_bytes()))
1062 } else {
1063 u64::try_from(sample).map_err(|_| {
1064 Error::ValueOutOfRange("JPEG 2000 unsigned component yielded a negative sample".into())
1065 })?
1066 };
1067 validate_raw_value_fits(raw, bits_per_value)?;
1068 Ok(raw)
1069}
1070
1071#[cfg(feature = "png")]
1072fn unpack_png_into<T: DecodeSample>(
1073 data_bytes: &[u8],
1074 params: &PngPackingParams,
1075 num_values: usize,
1076 output: &mut OutputCursor<'_, T>,
1077) -> Result<()> {
1078 validate_png_bits(params.packing.bits_per_value)?;
1079
1080 let decoder = png::Decoder::new(std::io::Cursor::new(data_bytes));
1081 let mut reader = decoder.read_info().map_err(|err| Error::Codec {
1082 codec: "PNG",
1083 operation: "read header",
1084 reason: err.to_string(),
1085 })?;
1086 let header_sample_count = image_sample_count(reader.info().width, reader.info().height)?;
1087 if header_sample_count != num_values {
1088 return Err(Error::DataLengthMismatch {
1089 expected: num_values,
1090 actual: header_sample_count,
1091 });
1092 }
1093 let buffer_size = reader
1094 .output_buffer_size()
1095 .ok_or(Error::ArithmeticOverflow {
1096 operation: "computing PNG output buffer size",
1097 })?;
1098 let mut buffer = filled_vec(buffer_size, 0, "PNG output buffer")?;
1099 let info = reader.next_frame(&mut buffer).map_err(|err| Error::Codec {
1100 codec: "PNG",
1101 operation: "decode",
1102 reason: err.to_string(),
1103 })?;
1104 let data = &buffer[..info.buffer_size()];
1105
1106 let sample_count = image_sample_count(info.width, info.height)?;
1107 if sample_count != num_values {
1108 return Err(Error::DataLengthMismatch {
1109 expected: num_values,
1110 actual: sample_count,
1111 });
1112 }
1113
1114 match (
1115 info.color_type,
1116 info.bit_depth,
1117 params.packing.bits_per_value,
1118 ) {
1119 (png::ColorType::Grayscale, png::BitDepth::One, 1)
1120 | (png::ColorType::Grayscale, png::BitDepth::Two, 2)
1121 | (png::ColorType::Grayscale, png::BitDepth::Four, 4) => unpack_png_subbyte_grayscale(
1122 data,
1123 info.width,
1124 info.height,
1125 params.packing.bits_per_value,
1126 ¶ms.packing,
1127 output,
1128 ),
1129 (png::ColorType::Grayscale, png::BitDepth::Eight, 8) => {
1130 unpack_png_bytes(data, 1, num_values, ¶ms.packing, output)
1131 }
1132 (png::ColorType::Grayscale, png::BitDepth::Sixteen, 16) => {
1133 unpack_png_u16(data, num_values, ¶ms.packing, output)
1134 }
1135 (png::ColorType::Rgb, png::BitDepth::Eight, 24) => {
1136 unpack_png_bytes(data, 3, num_values, ¶ms.packing, output)
1137 }
1138 (png::ColorType::Rgba, png::BitDepth::Eight, 32) => {
1139 unpack_png_bytes(data, 4, num_values, ¶ms.packing, output)
1140 }
1141 (color_type, bit_depth, bits_per_value) => Err(invalid_data_section(format!(
1142 "PNG image layout {color_type:?}/{bit_depth:?} is incompatible with GRIB bits-per-value {bits_per_value}"
1143 ))),
1144 }
1145}
1146
1147#[cfg(not(feature = "png"))]
1148fn unpack_png_into<T: DecodeSample>(
1149 _data_bytes: &[u8],
1150 _params: &PngPackingParams,
1151 _num_values: usize,
1152 _output: &mut OutputCursor<'_, T>,
1153) -> Result<()> {
1154 Err(Error::UnsupportedDataTemplate(41))
1155}
1156
1157#[cfg(feature = "png")]
1158fn validate_png_bits(bits_per_value: u8) -> Result<()> {
1159 if !matches!(bits_per_value, 1 | 2 | 4 | 8 | 16 | 24 | 32) {
1160 return Err(Error::UnsupportedPackingWidth(bits_per_value));
1161 }
1162 Ok(())
1163}
1164
1165#[cfg(any(feature = "jpeg2000", feature = "png"))]
1166fn image_sample_count(width: u32, height: u32) -> Result<usize> {
1167 let width = usize::try_from(width)
1168 .map_err(|_| Error::ValueOutOfRange("image width exceeds usize".into()))?;
1169 let height = usize::try_from(height)
1170 .map_err(|_| Error::ValueOutOfRange("image height exceeds usize".into()))?;
1171 width.checked_mul(height).ok_or(Error::ArithmeticOverflow {
1172 operation: "computing image sample count",
1173 })
1174}
1175
1176#[cfg(feature = "png")]
1177fn unpack_png_subbyte_grayscale<T: DecodeSample>(
1178 data: &[u8],
1179 width: u32,
1180 height: u32,
1181 bits_per_sample: u8,
1182 params: &ScaledPackingParams,
1183 output: &mut OutputCursor<'_, T>,
1184) -> Result<()> {
1185 let width = usize::try_from(width)
1186 .map_err(|_| Error::ValueOutOfRange("PNG width exceeds usize".into()))?;
1187 let height = usize::try_from(height)
1188 .map_err(|_| Error::ValueOutOfRange("PNG height exceeds usize".into()))?;
1189 let bits = usize::from(bits_per_sample);
1190 let row_bytes = width
1191 .checked_mul(bits)
1192 .ok_or(Error::ArithmeticOverflow {
1193 operation: "computing PNG row width",
1194 })?
1195 .div_ceil(8);
1196 let expected_bytes = row_bytes
1197 .checked_mul(height)
1198 .ok_or(Error::ArithmeticOverflow {
1199 operation: "computing PNG data length",
1200 })?;
1201 if data.len() < expected_bytes {
1202 return Err(Error::DataLengthMismatch {
1203 expected: expected_bytes,
1204 actual: data.len(),
1205 });
1206 }
1207
1208 let mask = (1u8 << bits) - 1;
1209 for row in data[..expected_bytes].chunks_exact(row_bytes) {
1210 for x in 0..width {
1211 let bit_offset = x.checked_mul(bits).ok_or(Error::BitOffsetOverflow)?;
1212 let shift = 8 - bits - (bit_offset % 8);
1213 let raw = u64::from((row[bit_offset / 8] >> shift) & mask);
1214 push_image_value(output, params, raw)?;
1215 }
1216 }
1217
1218 Ok(())
1219}
1220
1221#[cfg(feature = "png")]
1222fn unpack_png_bytes<T: DecodeSample>(
1223 data: &[u8],
1224 bytes_per_sample: usize,
1225 num_values: usize,
1226 params: &ScaledPackingParams,
1227 output: &mut OutputCursor<'_, T>,
1228) -> Result<()> {
1229 let expected_bytes =
1230 num_values
1231 .checked_mul(bytes_per_sample)
1232 .ok_or(Error::ArithmeticOverflow {
1233 operation: "computing PNG data length",
1234 })?;
1235 if data.len() < expected_bytes {
1236 return Err(Error::DataLengthMismatch {
1237 expected: expected_bytes,
1238 actual: data.len(),
1239 });
1240 }
1241
1242 for sample in data[..expected_bytes].chunks_exact(bytes_per_sample) {
1243 let raw = sample
1244 .iter()
1245 .fold(0u64, |acc, byte| (acc << 8) | u64::from(*byte));
1246 push_image_value(output, params, raw)?;
1247 }
1248
1249 Ok(())
1250}
1251
1252#[cfg(feature = "png")]
1253fn unpack_png_u16<T: DecodeSample>(
1254 data: &[u8],
1255 num_values: usize,
1256 params: &ScaledPackingParams,
1257 output: &mut OutputCursor<'_, T>,
1258) -> Result<()> {
1259 let expected_bytes = num_values.checked_mul(2).ok_or(Error::ArithmeticOverflow {
1260 operation: "computing PNG data length",
1261 })?;
1262 if data.len() < expected_bytes {
1263 return Err(Error::DataLengthMismatch {
1264 expected: expected_bytes,
1265 actual: data.len(),
1266 });
1267 }
1268
1269 for sample in data[..expected_bytes].chunks_exact(2) {
1270 let raw = u64::from(u16::from_be_bytes(sample.try_into().unwrap()));
1271 push_image_value(output, params, raw)?;
1272 }
1273
1274 Ok(())
1275}
1276
1277#[cfg(any(feature = "jpeg2000", feature = "png"))]
1278fn push_image_value<T: DecodeSample>(
1279 output: &mut OutputCursor<'_, T>,
1280 params: &ScaledPackingParams,
1281 raw: u64,
1282) -> Result<()> {
1283 validate_raw_value_fits(raw, params.bits_per_value)?;
1284 output.push_present(scale_image_value(params, raw))
1285}
1286
1287#[cfg(any(feature = "jpeg2000", feature = "png"))]
1288fn validate_raw_value_fits(raw: u64, bits_per_value: u8) -> Result<()> {
1289 let max_value = if bits_per_value == u64::BITS as u8 {
1290 u64::MAX
1291 } else {
1292 (1u64 << bits_per_value) - 1
1293 };
1294 if raw > max_value {
1295 return Err(Error::ValueOutOfRange(format!(
1296 "decoded image sample {raw} exceeds GRIB bits-per-value {bits_per_value}"
1297 )));
1298 }
1299 Ok(())
1300}
1301
1302#[cfg(any(feature = "jpeg2000", feature = "png"))]
1303fn scale_image_value<T: DecodeSample>(params: &ScaledPackingParams, raw: u64) -> T {
1304 let binary_factor = 2.0_f64.powi(params.binary_scale as i32);
1305 let decimal_factor = 10.0_f64.powi(-(params.decimal_scale as i32));
1306 T::from_f64(scale_decoded_value(
1307 params.reference_value as f64,
1308 raw as f64,
1309 binary_factor,
1310 decimal_factor,
1311 ))
1312}
1313
1314#[cfg(test)]
1315fn unpack_complex(data_bytes: &[u8], params: &ComplexPackingParams) -> Result<Vec<f64>> {
1316 let mut values = filled_vec(params.encoded_values, 0.0, "complex-packed field")?;
1317 let mut output = OutputCursor::new(&mut values, None);
1318 unpack_complex_into(data_bytes, params, None, &mut output)?;
1319 output.finish()?;
1320 Ok(values)
1321}
1322
1323fn unpack_grib1_second_order_into<T: DecodeSample>(
1324 data_bytes: &[u8],
1325 params: &Grib1SecondOrderPackingParams,
1326 row_group_lengths: Option<&[usize]>,
1327 output: &mut OutputCursor<'_, T>,
1328) -> Result<()> {
1329 let group_lengths = match params.layout() {
1330 Grib1SecondOrderLayout::RowByRow => {
1331 row_group_lengths.ok_or_else(|| Error::InvalidSection {
1332 section: 4,
1333 reason: "GRIB1 row-by-row second-order packing requires grid-derived group lengths"
1334 .into(),
1335 })?
1336 }
1337 Grib1SecondOrderLayout::ConstantWidth | Grib1SecondOrderLayout::VariableWidth => {
1338 params.group_lengths()
1339 }
1340 _ => {
1341 return Err(Error::InvalidSection {
1342 section: 4,
1343 reason: "unsupported GRIB1 second-order group layout".into(),
1344 });
1345 }
1346 };
1347 validate_external_group_lengths(
1348 group_lengths,
1349 params.group_count(),
1350 params.packing().encoded_values,
1351 "GRIB1 second-order",
1352 4,
1353 )?;
1354
1355 let first_order_offset = params
1356 .first_order_values_offset()
1357 .checked_mul(8)
1358 .ok_or(Error::BitOffsetOverflow)?;
1359 let second_order_offset = params
1360 .second_order_values_offset()
1361 .checked_mul(8)
1362 .ok_or(Error::BitOffsetOverflow)?;
1363 let mut first_order_reader = BitReader::with_offset(data_bytes, first_order_offset);
1364 let mut second_order_reader = BitReader::with_offset(data_bytes, second_order_offset);
1365 let packing = params.packing();
1366 let binary_factor = 2.0_f64.powi(i32::from(packing.binary_scale));
1367 let decimal_factor = 10.0_f64.powi(-i32::from(packing.decimal_scale));
1368 let reference = f64::from(packing.reference_value);
1369
1370 for (group_index, group_length) in group_lengths.iter().copied().enumerate() {
1371 let first_order = first_order_reader.read(usize::from(packing.bits_per_value))?;
1372 let group_width = params
1373 .group_width(group_index)
1374 .ok_or_else(|| Error::InvalidSection {
1375 section: 4,
1376 reason: format!("missing width for GRIB1 second-order group {group_index}"),
1377 })?;
1378 for _ in 0..group_length {
1379 let second_order = second_order_reader.read(usize::from(group_width))?;
1380 let packed =
1381 first_order
1382 .checked_add(second_order)
1383 .ok_or(Error::ArithmeticOverflow {
1384 operation: "reconstructing a GRIB1 second-order value",
1385 })?;
1386 output.push_present(T::from_f64(scale_decoded_value(
1387 reference,
1388 packed as f64,
1389 binary_factor,
1390 decimal_factor,
1391 )))?;
1392 }
1393 }
1394
1395 let payload_bits = data_bytes
1396 .len()
1397 .checked_mul(8)
1398 .ok_or(Error::BitOffsetOverflow)?;
1399 let usable_bits = payload_bits
1400 .checked_sub(usize::from(params.unused_bits()))
1401 .ok_or_else(|| Error::InvalidSection {
1402 section: 4,
1403 reason: "GRIB1 BDS unused-bit count exceeds its payload".into(),
1404 })?;
1405 if second_order_reader.bit_offset() != usable_bits {
1406 return Err(Error::InvalidSection {
1407 section: 4,
1408 reason: format!(
1409 "GRIB1 second-order values end at bit {}, expected {usable_bits}",
1410 second_order_reader.bit_offset()
1411 ),
1412 });
1413 }
1414 if output.values_written() != packing.encoded_values {
1415 return Err(Error::DataLengthMismatch {
1416 expected: packing.encoded_values,
1417 actual: output.values_written(),
1418 });
1419 }
1420 Ok(())
1421}
1422
1423fn unpack_complex_into<T: DecodeSample>(
1424 data_bytes: &[u8],
1425 params: &ComplexPackingParams,
1426 row_group_lengths: Option<&[usize]>,
1427 output: &mut OutputCursor<'_, T>,
1428) -> Result<()> {
1429 if params.num_groups == 0 {
1430 return Err(Error::InvalidSection {
1431 section: 5,
1432 reason: "complex packing requires at least one group".into(),
1433 });
1434 }
1435
1436 let mut reader = BitReader::new(data_bytes);
1437 let mut spatial = params
1438 .spatial_differencing
1439 .map(|spatial| read_spatial_descriptors(&mut reader, spatial))
1440 .transpose()?
1441 .map(SpatialRestoreState::new);
1442
1443 let layout = GroupReaderLayout::new(reader.bit_offset(), params)?;
1444 let mut reference_reader = BitReader::with_offset(data_bytes, layout.reference_offset);
1445 let mut width_reader = BitReader::with_offset(data_bytes, layout.width_offset);
1446 let mut length_reader = BitReader::with_offset(data_bytes, layout.length_offset);
1447 let mut value_offset = layout.value_offset;
1448 let scale = ComplexScale {
1449 reference: f64::from(params.reference_value),
1450 binary_factor: 2.0_f64.powi(i32::from(params.binary_scale)),
1451 decimal_factor: 10.0_f64.powi(-i32::from(params.decimal_scale)),
1452 };
1453 let group_reference_bits = usize::from(params.group_reference_bits);
1454 let mut actual_total = 0usize;
1455 let row_group_lengths = match params.group_splitting_method {
1456 0 => {
1457 let lengths = row_group_lengths.ok_or_else(|| Error::InvalidSection {
1458 section: 5,
1459 reason:
1460 "complex packing group splitting method 0 requires grid-derived group lengths"
1461 .into(),
1462 })?;
1463 validate_external_group_lengths(
1464 lengths,
1465 params.num_groups,
1466 params.encoded_values,
1467 "complex-packing row",
1468 5,
1469 )?;
1470 Some(lengths)
1471 }
1472 1 => None,
1473 method => return Err(Error::UnsupportedGroupSplittingMethod(method)),
1474 };
1475
1476 for group_index in 0..params.num_groups {
1477 let group_reference = reference_reader.read(group_reference_bits)?;
1478 let width_delta = width_reader.read(params.group_width_bits as usize)?;
1479 let group_width = usize::from(params.group_width_reference)
1480 .checked_add(width_delta as usize)
1481 .ok_or(Error::ArithmeticOverflow {
1482 operation: "computing complex-packing group width",
1483 })?;
1484 let group_length = match row_group_lengths {
1485 Some(lengths) => lengths[group_index],
1486 None => read_group_length(&mut length_reader, params, group_index)?,
1487 };
1488
1489 actual_total = actual_total
1490 .checked_add(group_length)
1491 .ok_or(Error::ArithmeticOverflow {
1492 operation: "summing complex-packing group lengths",
1493 })?;
1494
1495 if actual_total > params.encoded_values {
1496 return Err(Error::DataLengthMismatch {
1497 expected: params.encoded_values,
1498 actual: actual_total,
1499 });
1500 }
1501
1502 if group_width == 0 {
1503 let missing = MissingCodes::new(group_reference_bits, params.missing_value_management)?;
1504 let raw_value =
1505 if missing.contains(group_reference) {
1506 None
1507 } else {
1508 Some(i64::try_from(group_reference).map_err(|_| {
1509 Error::ValueOutOfRange("group reference exceeds i64".into())
1510 })?)
1511 };
1512 match spatial.as_mut() {
1513 Some(state) => emit_group(output, group_length, || {
1514 Ok(scale.apply(state.restore(raw_value)?))
1515 })?,
1516 None => {
1517 let value = scale.apply(raw_value);
1518 emit_group(output, group_length, || Ok(value))?
1519 }
1520 }
1521 continue;
1522 }
1523
1524 if group_width > u64::BITS as usize {
1525 return Err(Error::UnsupportedPackingWidth(group_width as u8));
1526 }
1527
1528 let group_reference = i64::try_from(group_reference)
1529 .map_err(|_| Error::ValueOutOfRange("group reference exceeds i64".into()))?;
1530 let missing = MissingCodes::new(group_width, params.missing_value_management)?;
1531 let mut packed_values =
1532 PackedValues::new(data_bytes, value_offset, group_width, group_length)?;
1533 value_offset = packed_values.end_bit_offset();
1534 let mut next_value = || -> Result<Option<i64>> {
1535 let packed = packed_values.next_value();
1536 if missing.contains(packed) {
1537 return Ok(None);
1538 }
1539 i64::try_from(packed)
1540 .ok()
1541 .and_then(|packed| group_reference.checked_add(packed))
1542 .map(Some)
1543 .ok_or(Error::ArithmeticOverflow {
1544 operation: "reconstructing complex-packing value",
1545 })
1546 };
1547 match spatial.as_mut() {
1548 Some(state) => emit_group(output, group_length, || {
1549 Ok(scale.apply(state.restore(next_value()?)?))
1550 })?,
1551 None => emit_group(output, group_length, || Ok(scale.apply(next_value()?)))?,
1552 }
1553 }
1554
1555 if actual_total != params.encoded_values {
1556 return Err(Error::DataLengthMismatch {
1557 expected: params.encoded_values,
1558 actual: actual_total,
1559 });
1560 }
1561
1562 if output.values_written() != params.encoded_values {
1563 return Err(Error::DataLengthMismatch {
1564 expected: params.encoded_values,
1565 actual: output.values_written(),
1566 });
1567 }
1568
1569 if let Some(spatial) = spatial {
1570 spatial.finish()?;
1571 }
1572
1573 Ok(())
1574}
1575
1576fn validate_external_group_lengths(
1577 lengths: &[usize],
1578 expected_groups: usize,
1579 expected_values: usize,
1580 packing_name: &str,
1581 section: u8,
1582) -> Result<()> {
1583 if lengths.len() != expected_groups {
1584 return Err(Error::InvalidSection {
1585 section,
1586 reason: format!(
1587 "{packing_name} packing declares {expected_groups} groups, but the grid defines {} scan lines",
1588 lengths.len()
1589 ),
1590 });
1591 }
1592 let actual_values = lengths.iter().try_fold(0usize, |total, length| {
1593 total.checked_add(*length).ok_or(Error::ArithmeticOverflow {
1594 operation: "summing row-wise packing group lengths",
1595 })
1596 })?;
1597 if actual_values != expected_values {
1598 return Err(Error::DataLengthMismatch {
1599 expected: expected_values,
1600 actual: actual_values,
1601 });
1602 }
1603 Ok(())
1604}
1605
1606fn read_group_length(
1607 reader: &mut BitReader<'_>,
1608 params: &ComplexPackingParams,
1609 group_index: usize,
1610) -> Result<usize> {
1611 if params.scaled_group_length_bits as usize > u64::BITS as usize {
1612 return Err(Error::UnsupportedPackingWidth(
1613 params.scaled_group_length_bits,
1614 ));
1615 }
1616
1617 if group_index + 1 == params.num_groups {
1618 return Ok(params.true_length_last_group as usize);
1619 }
1620
1621 let scaled = reader
1622 .read(params.scaled_group_length_bits as usize)?
1623 .checked_mul(u64::from(params.group_length_increment))
1624 .ok_or(Error::ArithmeticOverflow {
1625 operation: "scaling complex-packing group length",
1626 })?;
1627 let length = u64::from(params.group_length_reference)
1628 .checked_add(scaled)
1629 .ok_or(Error::ArithmeticOverflow {
1630 operation: "computing complex-packing group length",
1631 })?;
1632 usize::try_from(length)
1633 .map_err(|_| Error::ValueOutOfRange("complex-packing group length exceeds usize".into()))
1634}
1635
1636fn read_spatial_descriptors(
1637 reader: &mut BitReader<'_>,
1638 params: SpatialDifferencingParams,
1639) -> Result<SpatialDescriptors> {
1640 if params.descriptor_octets == 0 {
1641 return Err(Error::InvalidSection {
1642 section: 5,
1643 reason: "spatial differencing requires at least one descriptor octet".into(),
1644 });
1645 }
1646
1647 let bit_count = usize::from(params.descriptor_octets) * 8;
1648 if bit_count > u64::BITS as usize {
1649 return Err(Error::InvalidSection {
1650 section: 5,
1651 reason: "spatial differencing descriptors wider than 8 octets are not supported".into(),
1652 });
1653 }
1654
1655 let first_value = reader.read_signed(bit_count)?;
1656 let second_value = if params.order == 2 {
1657 Some(reader.read_signed(bit_count)?)
1658 } else {
1659 None
1660 };
1661 for _ in usize::from(params.order.min(2))..params.order as usize {
1662 let _ = reader.read_signed(bit_count)?;
1663 }
1664 let overall_minimum = reader.read_signed(bit_count)?;
1665
1666 Ok(SpatialDescriptors {
1667 order: params.order,
1668 first_value,
1669 second_value,
1670 overall_minimum,
1671 })
1672}
1673
1674struct PackedValues<'a> {
1680 data: &'a [u8],
1681 bit_offset: usize,
1682 end_bit_offset: usize,
1683 width: usize,
1684}
1685
1686impl<'a> PackedValues<'a> {
1687 fn new(data: &'a [u8], bit_offset: usize, width: usize, count: usize) -> Result<Self> {
1688 debug_assert!((1..=u64::BITS as usize).contains(&width));
1689 let end_bit_offset = width
1690 .checked_mul(count)
1691 .and_then(|bits| bit_offset.checked_add(bits))
1692 .ok_or(Error::BitOffsetOverflow)?;
1693 let data_bits = data.len().checked_mul(8).ok_or(Error::BitOffsetOverflow)?;
1694 if end_bit_offset > data_bits {
1695 return Err(Error::Truncated {
1696 offset: data.len() as u64,
1697 });
1698 }
1699 Ok(Self {
1700 data,
1701 bit_offset,
1702 end_bit_offset,
1703 width,
1704 })
1705 }
1706
1707 fn end_bit_offset(&self) -> usize {
1708 self.end_bit_offset
1709 }
1710
1711 fn next_value(&mut self) -> u64 {
1714 debug_assert!(self.bit_offset + self.width <= self.end_bit_offset);
1715 let byte = self.bit_offset / 8;
1716 let shift = self.bit_offset % 8;
1717 let value = if shift + self.width <= u64::BITS as usize {
1718 (load_be_u64(self.data, byte) << shift) >> (u64::BITS as usize - self.width)
1719 } else {
1720 let high = load_be_u64(self.data, byte) << shift;
1722 let low = u64::from(self.data[byte + 8]) >> (8 - shift);
1723 (high | low) >> (u64::BITS as usize - self.width)
1724 };
1725 self.bit_offset += self.width;
1726 value
1727 }
1728}
1729
1730fn load_be_u64(data: &[u8], byte: usize) -> u64 {
1733 match data.get(byte..byte + 8) {
1734 Some(bytes) => u64::from_be_bytes(bytes.try_into().expect("eight-byte window")),
1735 None => {
1736 let mut bytes = [0u8; 8];
1737 let available = data.get(byte..).unwrap_or_default();
1738 bytes[..available.len()].copy_from_slice(available);
1739 u64::from_be_bytes(bytes)
1740 }
1741 }
1742}
1743
1744fn emit_group<T: DecodeSample>(
1747 output: &mut OutputCursor<'_, T>,
1748 count: usize,
1749 mut next: impl FnMut() -> Result<T>,
1750) -> Result<()> {
1751 if let Some(dense) = output.take_dense(count)? {
1752 for target in dense {
1753 *target = next()?;
1754 }
1755 } else {
1756 for _ in 0..count {
1757 output.push_present(next()?)?;
1758 }
1759 }
1760 Ok(())
1761}
1762
1763#[derive(Debug, Clone, Copy)]
1766struct MissingCodes {
1767 primary: Option<u64>,
1768 secondary: Option<u64>,
1769}
1770
1771impl MissingCodes {
1772 fn new(bit_width: usize, missing_value_management: u8) -> Result<Self> {
1773 let primary = if missing_value_management >= 1 {
1774 missing_code(bit_width, MissingKind::Primary)?
1775 } else {
1776 None
1777 };
1778 let secondary = if missing_value_management >= 2 {
1779 missing_code(bit_width, MissingKind::Secondary)?
1780 } else {
1781 None
1782 };
1783 Ok(Self { primary, secondary })
1784 }
1785
1786 fn contains(self, value: u64) -> bool {
1787 self.primary == Some(value) || self.secondary == Some(value)
1788 }
1789}
1790
1791fn missing_code(bit_width: usize, kind: MissingKind) -> Result<Option<u64>> {
1792 if bit_width == 0 {
1793 return Ok(None);
1794 }
1795 if bit_width > u64::BITS as usize {
1796 return Err(Error::UnsupportedPackingWidth(bit_width as u8));
1797 }
1798
1799 let max_value = if bit_width == u64::BITS as usize {
1800 u64::MAX
1801 } else {
1802 (1u64 << bit_width) - 1
1803 };
1804
1805 let code = match kind {
1806 MissingKind::Primary => max_value,
1807 MissingKind::Secondary => max_value.saturating_sub(1),
1808 };
1809 Ok(Some(code))
1810}
1811
1812#[derive(Debug, Clone, Copy)]
1813struct ComplexScale {
1814 reference: f64,
1815 binary_factor: f64,
1816 decimal_factor: f64,
1817}
1818
1819impl ComplexScale {
1820 fn apply<T: DecodeSample>(self, value: Option<i64>) -> T {
1821 match value {
1822 Some(value) => T::from_f64(scale_decoded_value(
1823 self.reference,
1824 value as f64,
1825 self.binary_factor,
1826 self.decimal_factor,
1827 )),
1828 None => T::nan(),
1829 }
1830 }
1831}
1832
1833fn scale_decoded_value(
1834 reference: f64,
1835 packed_delta: f64,
1836 binary_factor: f64,
1837 decimal_factor: f64,
1838) -> f64 {
1839 (reference + packed_delta * binary_factor) * decimal_factor
1840}
1841
1842struct GroupReaderLayout {
1843 reference_offset: usize,
1844 width_offset: usize,
1845 length_offset: usize,
1846 value_offset: usize,
1847}
1848
1849impl GroupReaderLayout {
1850 fn new(start_bit_offset: usize, params: &ComplexPackingParams) -> Result<Self> {
1851 if params.group_reference_bits as usize > u64::BITS as usize {
1852 return Err(Error::UnsupportedPackingWidth(params.group_reference_bits));
1853 }
1854 if params.group_width_bits as usize > u64::BITS as usize {
1855 return Err(Error::UnsupportedPackingWidth(params.group_width_bits));
1856 }
1857 if params.scaled_group_length_bits as usize > u64::BITS as usize {
1858 return Err(Error::UnsupportedPackingWidth(
1859 params.scaled_group_length_bits,
1860 ));
1861 }
1862
1863 let reference_offset = start_bit_offset;
1864 let width_offset = align_bit_offset(add_group_bits(
1865 reference_offset,
1866 params.num_groups,
1867 params.group_reference_bits as usize,
1868 )?)?;
1869 let length_offset = align_bit_offset(add_group_bits(
1870 width_offset,
1871 params.num_groups,
1872 params.group_width_bits as usize,
1873 )?)?;
1874 let value_offset = align_bit_offset(add_group_bits(
1875 length_offset,
1876 params.num_groups,
1877 params.scaled_group_length_bits as usize,
1878 )?)?;
1879
1880 Ok(Self {
1881 reference_offset,
1882 width_offset,
1883 length_offset,
1884 value_offset,
1885 })
1886 }
1887}
1888
1889fn add_group_bits(start_bit_offset: usize, count: usize, bits_per_group: usize) -> Result<usize> {
1890 count
1891 .checked_mul(bits_per_group)
1892 .and_then(|total_bits| start_bit_offset.checked_add(total_bits))
1893 .ok_or(Error::BitOffsetOverflow)
1894}
1895
1896fn align_bit_offset(bit_offset: usize) -> Result<usize> {
1897 let remainder = bit_offset % 8;
1898 if remainder == 0 {
1899 Ok(bit_offset)
1900 } else {
1901 bit_offset
1902 .checked_add(8 - remainder)
1903 .ok_or(Error::BitOffsetOverflow)
1904 }
1905}
1906
1907struct OutputCursor<'a, T> {
1908 output: &'a mut [T],
1909 bitmap: Option<&'a [u8]>,
1910 next_index: usize,
1911 values_written: usize,
1912}
1913
1914impl<'a, T: DecodeSample> OutputCursor<'a, T> {
1915 fn new(output: &'a mut [T], bitmap: Option<&'a [u8]>) -> Self {
1916 Self {
1917 output,
1918 bitmap,
1919 next_index: 0,
1920 values_written: 0,
1921 }
1922 }
1923
1924 fn push_present(&mut self, value: T) -> Result<()> {
1925 match self.bitmap {
1926 Some(bitmap) => {
1927 if self.advance_to_present(bitmap)? {
1928 self.output[self.next_index] = value;
1929 self.next_index += 1;
1930 self.values_written += 1;
1931 return Ok(());
1932 }
1933
1934 let expected = count_bitmap_present_points(bitmap, self.output.len())?;
1935 Err(Error::DataLengthMismatch {
1936 expected,
1937 actual: self.values_written + 1,
1938 })
1939 }
1940 None => {
1941 if self.next_index >= self.output.len() {
1942 return Err(Error::DataLengthMismatch {
1943 expected: self.output.len(),
1944 actual: self.next_index + 1,
1945 });
1946 }
1947
1948 self.output[self.next_index] = value;
1949 self.next_index += 1;
1950 self.values_written += 1;
1951 Ok(())
1952 }
1953 }
1954 }
1955
1956 fn take_dense(&mut self, count: usize) -> Result<Option<&mut [T]>> {
1957 if self.bitmap.is_some() {
1958 return Ok(None);
1959 }
1960 let start = self.next_index;
1961 let end = start.checked_add(count).ok_or(Error::ArithmeticOverflow {
1962 operation: "advancing dense decoded output",
1963 })?;
1964 if end > self.output.len() {
1965 return Err(Error::DataLengthMismatch {
1966 expected: self.output.len(),
1967 actual: end,
1968 });
1969 }
1970 self.next_index = end;
1971 self.values_written =
1972 self.values_written
1973 .checked_add(count)
1974 .ok_or(Error::ArithmeticOverflow {
1975 operation: "counting dense decoded values",
1976 })?;
1977 Ok(Some(&mut self.output[start..end]))
1978 }
1979
1980 fn advance_to_present(&mut self, bitmap: &[u8]) -> Result<bool> {
1981 while self.next_index < self.output.len() {
1982 let byte_index = self.next_index / 8;
1983 let bit_index = self.next_index % 8;
1984 let byte = *bitmap.get(byte_index).ok_or(Error::DataLengthMismatch {
1985 expected: byte_index + 1,
1986 actual: bitmap.len(),
1987 })?;
1988 let remaining = byte & (u8::MAX >> bit_index);
1989 if remaining != 0 {
1990 let present_index = byte_index
1991 .checked_mul(8)
1992 .and_then(|base| base.checked_add(remaining.leading_zeros() as usize))
1993 .ok_or(Error::BitOffsetOverflow)?;
1994 if present_index < self.output.len() {
1995 self.output[self.next_index..present_index].fill(T::nan());
1996 self.next_index = present_index;
1997 return Ok(true);
1998 }
1999 }
2000
2001 let next_byte = byte_index
2002 .checked_add(1)
2003 .and_then(|index| index.checked_mul(8))
2004 .ok_or(Error::BitOffsetOverflow)?
2005 .min(self.output.len());
2006 self.output[self.next_index..next_byte].fill(T::nan());
2007 self.next_index = next_byte;
2008 }
2009 Ok(false)
2010 }
2011
2012 fn finish(mut self) -> Result<()> {
2013 if let Some(bitmap) = self.bitmap {
2014 if self.advance_to_present(bitmap)? {
2015 return Err(Error::DataLengthMismatch {
2016 expected: count_bitmap_present_points(bitmap, self.output.len())?,
2017 actual: self.values_written,
2018 });
2019 }
2020 }
2021
2022 if self.next_index != self.output.len() {
2023 return Err(Error::DataLengthMismatch {
2024 expected: self.output.len(),
2025 actual: self.next_index,
2026 });
2027 }
2028
2029 Ok(())
2030 }
2031 fn values_written(&self) -> usize {
2032 self.values_written
2033 }
2034}
2035
2036#[derive(Debug, Clone)]
2037struct SpatialDescriptors {
2038 order: u8,
2039 first_value: i64,
2040 second_value: Option<i64>,
2041 overall_minimum: i64,
2042}
2043
2044#[derive(Debug, Clone)]
2045struct SpatialRestoreState {
2046 order: u8,
2047 first_value: i64,
2048 second_value: Option<i64>,
2049 overall_minimum: i64,
2050 previous: i64,
2051 previous_difference: i64,
2052 non_missing_seen: usize,
2053}
2054
2055impl SpatialRestoreState {
2056 fn new(descriptors: SpatialDescriptors) -> Self {
2057 Self {
2058 order: descriptors.order,
2059 first_value: descriptors.first_value,
2060 second_value: descriptors.second_value,
2061 overall_minimum: descriptors.overall_minimum,
2062 previous: 0,
2063 previous_difference: 0,
2064 non_missing_seen: 0,
2065 }
2066 }
2067
2068 fn restore(&mut self, value: Option<i64>) -> Result<Option<i64>> {
2069 let Some(value) = value else {
2070 return Ok(None);
2071 };
2072
2073 let restored = if self.non_missing_seen >= usize::from(self.order) {
2074 self.restore_steady_state(value)
2075 } else {
2076 self.restore_initial_value()
2077 }?;
2078
2079 self.previous = restored;
2080 self.non_missing_seen += 1;
2081 Ok(Some(restored))
2082 }
2083
2084 fn restore_steady_state(&mut self, value: i64) -> Result<i64> {
2086 let overflow = || Error::ArithmeticOverflow {
2087 operation: "restoring a spatial difference",
2088 };
2089 let difference = value
2090 .checked_add(self.overall_minimum)
2091 .ok_or_else(overflow)?;
2092 match self.order {
2093 1 => self.previous.checked_add(difference).ok_or_else(overflow),
2094 2 => {
2095 self.previous_difference = self
2096 .previous_difference
2097 .checked_add(difference)
2098 .ok_or_else(overflow)?;
2099 self.previous
2100 .checked_add(self.previous_difference)
2101 .ok_or_else(overflow)
2102 }
2103 other => Err(Error::UnsupportedSpatialDifferencingOrder(other)),
2104 }
2105 }
2106
2107 fn restore_initial_value(&mut self) -> Result<i64> {
2108 match (self.order, self.non_missing_seen) {
2109 (1 | 2, 0) => Ok(self.first_value),
2110 (2, 1) => {
2111 let second_value = self.second_value.ok_or(Error::InvalidSection {
2112 section: 5,
2113 reason: "missing second-order spatial differencing descriptors".into(),
2114 })?;
2115 self.previous_difference = second_value.checked_sub(self.first_value).ok_or(
2116 Error::ArithmeticOverflow {
2117 operation: "initializing second-order spatial difference",
2118 },
2119 )?;
2120 Ok(second_value)
2121 }
2122 (other, _) => Err(Error::UnsupportedSpatialDifferencingOrder(other)),
2123 }
2124 }
2125
2126 fn finish(self) -> Result<()> {
2127 if !matches!(self.order, 1 | 2) {
2128 return Err(Error::UnsupportedSpatialDifferencingOrder(self.order));
2129 }
2130 let expected = usize::from(self.order);
2131 if self.non_missing_seen < expected {
2132 return Err(Error::DataLengthMismatch {
2133 expected,
2134 actual: self.non_missing_seen,
2135 });
2136 }
2137 Ok(())
2138 }
2139}
2140
2141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2142enum MissingKind {
2143 Primary,
2144 Secondary,
2145}
2146
2147#[cfg(test)]
2148mod tests {
2149 use super::{
2150 bitmap_payload, count_bitmap_present_points, decode_field, decode_payload, unpack_complex,
2151 unpack_simple, CcsdsBlockSize, CcsdsFlags, CcsdsPackingParams, ComplexPackingParams,
2152 DataRepresentation, IeeePackingParams, IeeePrecision, Jpeg2000PackingParams,
2153 PngPackingParams, RunLengthPackingParams, ScaledPackingParams, SpatialDifferencingParams,
2154 };
2155 use crate::error::Error;
2156 use grib_core::binary::encode_ieee_binary128;
2157 use grib_core::bit::BitWriter;
2158
2159 #[test]
2160 fn packed_values_match_bit_reader_for_every_width_and_alignment() {
2161 for width in 1..=64usize {
2162 for lead in 0..8usize {
2163 let count = 5usize;
2164 let mut writer = BitWriter::with_capacity_bits(lead + width * count);
2165 writer.write(0, lead).unwrap();
2166 let max = if width == 64 {
2167 u64::MAX
2168 } else {
2169 (1u64 << width) - 1
2170 };
2171 let expected = (0..count as u64)
2172 .map(|index| max.wrapping_sub(index.wrapping_mul(0x9e37_79b9_7f4a_7c15)) & max)
2173 .collect::<Vec<_>>();
2174 for value in &expected {
2175 writer.write(*value, width).unwrap();
2176 }
2177 writer.align_to_byte().unwrap();
2178 let bytes = writer.into_bytes();
2179
2180 let mut values = super::PackedValues::new(&bytes, lead, width, count).unwrap();
2181 let actual = (0..count).map(|_| values.next_value()).collect::<Vec<_>>();
2182 assert_eq!(actual, expected, "width {width}, lead {lead}");
2183 assert_eq!(values.end_bit_offset(), lead + width * count);
2184 assert!(super::PackedValues::new(&bytes, lead, width, count + 8).is_err());
2185 }
2186 }
2187 }
2188
2189 #[test]
2190 fn unpack_simple_constant() {
2191 let params = ScaledPackingParams {
2192 encoded_values: 5,
2193 reference_value: 42.0,
2194 binary_scale: 0,
2195 decimal_scale: 0,
2196 bits_per_value: 0,
2197 original_field_type: 0,
2198 };
2199 let values = unpack_simple(&[], ¶ms, 5).unwrap();
2200 assert_eq!(values, vec![42.0; 5]);
2201 }
2202
2203 #[test]
2204 fn unpack_simple_basic() {
2205 let params = ScaledPackingParams {
2206 encoded_values: 5,
2207 reference_value: 0.0,
2208 binary_scale: 0,
2209 decimal_scale: 0,
2210 bits_per_value: 8,
2211 original_field_type: 0,
2212 };
2213 let values = unpack_simple(&[0, 1, 2, 3, 4], ¶ms, 5).unwrap();
2214 assert_eq!(values, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
2215 }
2216
2217 #[test]
2218 fn unpack_simple_dense_matches_byte_aligned_and_bit_packed_values() {
2219 let cases: &[(usize, &[u64])] = &[
2220 (7, &[1, 2, 3, 127]),
2221 (8, &[1, 17, 255]),
2222 (16, &[1, 0x1234, u16::MAX as u64]),
2223 (32, &[1, 0x1234_5678, u32::MAX as u64]),
2224 (64, &[1, 1 << 40, u32::MAX as u64 + 1]),
2225 ];
2226
2227 for &(bits, packed_values) in cases {
2228 let mut packed = BitWriter::new();
2229 for &value in packed_values {
2230 packed.write(value, bits).unwrap();
2231 }
2232 let params = ScaledPackingParams {
2233 encoded_values: packed_values.len(),
2234 reference_value: 0.0,
2235 binary_scale: 0,
2236 decimal_scale: 0,
2237 bits_per_value: bits as u8,
2238 original_field_type: 0,
2239 };
2240
2241 let decoded = unpack_simple(packed.as_bytes(), ¶ms, packed_values.len()).unwrap();
2242 let expected: Vec<_> = packed_values.iter().map(|&value| value as f64).collect();
2243 assert_eq!(decoded, expected, "bits_per_value={bits}");
2244 }
2245 }
2246
2247 #[test]
2248 fn unpack_simple_applies_decimal_scale_to_reference_and_values() {
2249 let params = ScaledPackingParams {
2250 encoded_values: 2,
2251 reference_value: 10.0,
2252 binary_scale: 0,
2253 decimal_scale: 1,
2254 bits_per_value: 8,
2255 original_field_type: 0,
2256 };
2257
2258 let values = unpack_simple(&[0, 20], ¶ms, 2).unwrap();
2259 assert!((values[0] - 1.0).abs() < 1e-12);
2260 assert!((values[1] - 3.0).abs() < 1e-12);
2261 }
2262
2263 #[test]
2264 fn decodes_all_ieee_packing_precisions() {
2265 let values = [1.0, -2.5, 65_536.125];
2266
2267 for precision in [
2268 IeeePrecision::Single,
2269 IeeePrecision::Double,
2270 IeeePrecision::Quadruple,
2271 ] {
2272 let payload = ieee_payload(&values, precision);
2273 let representation = DataRepresentation::IeeePacking(IeeePackingParams {
2274 encoded_values: values.len(),
2275 precision,
2276 });
2277 assert_eq!(
2278 decode_payload(&payload, &representation, None, values.len()).unwrap(),
2279 values
2280 );
2281 }
2282 }
2283
2284 #[test]
2285 fn decodes_ieee_values_through_a_bitmap() {
2286 let values = [1.0, -2.5, 65_536.125];
2287 let payload = ieee_payload(&values, IeeePrecision::Double);
2288 let representation = DataRepresentation::IeeePacking(IeeePackingParams {
2289 encoded_values: values.len(),
2290 precision: IeeePrecision::Double,
2291 });
2292
2293 let decoded = decode_payload(&payload, &representation, Some(&[0b1011_0000]), 4).unwrap();
2294 assert_eq!(decoded[0], 1.0);
2295 assert!(decoded[1].is_nan());
2296 assert_eq!(decoded[2..], values[1..]);
2297 }
2298
2299 #[test]
2300 fn rejects_truncated_and_trailing_ieee_payload_bytes() {
2301 let representation = DataRepresentation::IeeePacking(IeeePackingParams {
2302 encoded_values: 1,
2303 precision: IeeePrecision::Double,
2304 });
2305
2306 for payload in [&[0_u8; 7][..], &[0_u8; 9][..]] {
2307 assert!(matches!(
2308 decode_payload(payload, &representation, None, 1),
2309 Err(Error::DataLengthMismatch {
2310 expected: 8,
2311 actual
2312 }) if actual == payload.len()
2313 ));
2314 }
2315 }
2316
2317 #[test]
2318 fn decodes_run_length_levels_and_multidigit_runs() {
2319 let params = RunLengthPackingParams {
2320 encoded_values: 9,
2321 bits_per_value: 3,
2322 max_level_value: 2,
2323 decimal_scale: 1,
2324 level_values: vec![10, 20],
2325 };
2326 let payload = packed_symbols(3, &[1, 5, 4, 2]);
2329 let representation = DataRepresentation::RunLengthPacking(params);
2330
2331 assert_eq!(
2332 decode_payload(&payload, &representation, None, 9).unwrap(),
2333 [vec![1.0; 8], vec![2.0]].concat()
2334 );
2335 }
2336
2337 #[test]
2338 fn decodes_run_length_level_zero_as_missing() {
2339 let params = RunLengthPackingParams {
2340 encoded_values: 4,
2341 bits_per_value: 3,
2342 max_level_value: 2,
2343 decimal_scale: 0,
2344 level_values: vec![10, 20],
2345 };
2346 let payload = packed_symbols(3, &[0, 5, 1]);
2347 let representation = DataRepresentation::RunLengthPacking(params);
2348 let decoded = decode_payload(&payload, &representation, None, 4).unwrap();
2349
2350 assert!(decoded[..3].iter().all(|value| value.is_nan()));
2351 assert_eq!(decoded[3], 10.0);
2352 }
2353
2354 #[test]
2355 fn decodes_all_missing_run_length_field_through_bitmap() {
2356 let params = RunLengthPackingParams {
2357 encoded_values: 2,
2358 bits_per_value: 0,
2359 max_level_value: 0,
2360 decimal_scale: 0,
2361 level_values: Vec::new(),
2362 };
2363 let representation = DataRepresentation::RunLengthPacking(params);
2364 let decoded = decode_payload(&[], &representation, Some(&[0b1010_0000]), 4).unwrap();
2365
2366 assert!(decoded.iter().all(|value| value.is_nan()));
2367 }
2368
2369 #[test]
2370 fn rejects_malformed_run_length_streams() {
2371 let representation = DataRepresentation::RunLengthPacking(RunLengthPackingParams {
2372 encoded_values: 2,
2373 bits_per_value: 8,
2374 max_level_value: 2,
2375 decimal_scale: 0,
2376 level_values: vec![10, 20],
2377 });
2378
2379 let error = decode_payload(&[3], &representation, None, 2).unwrap_err();
2380 assert!(error.to_string().contains("run starts with code 3"));
2381
2382 let error = decode_payload(&[1], &representation, None, 2).unwrap_err();
2383 assert!(matches!(
2384 error,
2385 Error::DataLengthMismatch {
2386 expected: 2,
2387 actual: 1
2388 }
2389 ));
2390
2391 let error = decode_payload(&[1, 255], &representation, None, 2).unwrap_err();
2392 assert!(matches!(
2393 error,
2394 Error::DataLengthMismatch { expected: 2, .. }
2395 ));
2396
2397 for (params, reason) in [
2398 (
2399 RunLengthPackingParams {
2400 encoded_values: 1,
2401 bits_per_value: 65,
2402 max_level_value: 0,
2403 decimal_scale: 0,
2404 level_values: Vec::new(),
2405 },
2406 "bits per value must not exceed 64",
2407 ),
2408 (
2409 RunLengthPackingParams {
2410 encoded_values: 1,
2411 bits_per_value: 0,
2412 max_level_value: 0,
2413 decimal_scale: 0,
2414 level_values: vec![10],
2415 },
2416 "zero maximum level requires an empty level table",
2417 ),
2418 ] {
2419 let error = decode_payload(&[], &DataRepresentation::RunLengthPacking(params), None, 1)
2420 .unwrap_err();
2421 assert!(error.to_string().contains(reason), "{error}");
2422 }
2423
2424 let one_value = DataRepresentation::RunLengthPacking(RunLengthPackingParams {
2425 encoded_values: 1,
2426 bits_per_value: 3,
2427 max_level_value: 2,
2428 decimal_scale: 0,
2429 level_values: vec![10, 20],
2430 });
2431 let error = decode_payload(&[0b0010_0001], &one_value, None, 1).unwrap_err();
2432 assert!(error.to_string().contains("nonzero bits remain"));
2433 }
2434
2435 #[test]
2436 fn decodes_bitmap_masked_field() {
2437 let data_section = [0, 0, 0, 8, 7, 10, 20, 30];
2438 let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1011_0000];
2439 let representation = DataRepresentation::SimplePacking(ScaledPackingParams {
2440 encoded_values: 3,
2441 reference_value: 0.0,
2442 binary_scale: 0,
2443 decimal_scale: 0,
2444 bits_per_value: 8,
2445 original_field_type: 0,
2446 });
2447
2448 let bitmap = bitmap_payload(&bitmap_section).unwrap();
2449 let decoded = decode_field(&data_section, &representation, bitmap, 4).unwrap();
2450 assert_eq!(decoded[0], 10.0);
2451 assert!(decoded[1].is_nan());
2452 assert_eq!(decoded[2], 20.0);
2453 assert_eq!(decoded[3], 30.0);
2454 }
2455
2456 fn ieee_payload(values: &[f64], precision: IeeePrecision) -> Vec<u8> {
2457 values
2458 .iter()
2459 .flat_map(|value| match precision {
2460 IeeePrecision::Single => Vec::from((*value as f32).to_be_bytes()),
2461 IeeePrecision::Double => Vec::from(value.to_be_bytes()),
2462 IeeePrecision::Quadruple => Vec::from(encode_ieee_binary128(*value)),
2463 _ => unreachable!("test covers all supported IEEE precisions"),
2464 })
2465 .collect()
2466 }
2467
2468 fn packed_symbols(bits: usize, symbols: &[u64]) -> Vec<u8> {
2469 let mut writer = BitWriter::new();
2470 for symbol in symbols {
2471 writer.write(*symbol, bits).unwrap();
2472 }
2473 writer.align_to_byte().unwrap();
2474 writer.into_bytes()
2475 }
2476
2477 #[test]
2478 fn decodes_bitmap_across_empty_bytes_and_a_partial_final_byte() {
2479 let data_section = [0, 0, 0, 7, 7, 11, 22];
2480 let bitmap_section = [0, 0, 0, 9, 6, 0, 0b0000_0000, 0b0010_0000, 0b1000_0000];
2481 let representation = DataRepresentation::SimplePacking(ScaledPackingParams {
2482 encoded_values: 2,
2483 reference_value: 0.0,
2484 binary_scale: 0,
2485 decimal_scale: 0,
2486 bits_per_value: 8,
2487 original_field_type: 0,
2488 });
2489
2490 let bitmap = bitmap_payload(&bitmap_section).unwrap();
2491 let decoded = decode_field(&data_section, &representation, bitmap, 19).unwrap();
2492 assert!(decoded[..10].iter().all(|value| value.is_nan()));
2493 assert_eq!(decoded[10], 11.0);
2494 assert!(decoded[11..16].iter().all(|value| value.is_nan()));
2495 assert_eq!(decoded[16], 22.0);
2496 assert!(decoded[17..].iter().all(|value| value.is_nan()));
2497 }
2498
2499 #[cfg(feature = "ccsds")]
2500 #[test]
2501 fn decodes_ccsds_unsigned_samples_across_container_widths() {
2502 for (bits, values) in [
2503 (8, vec![0, 1, 17, 255]),
2504 (16, vec![0, 1, 0x1234, 0xffff]),
2505 (20, vec![0, 1, 0x54321, 0xfffff]),
2506 (24, vec![0, 1, 0x654321, 0xffffff]),
2507 (32, vec![0, 1, 0x76543210, u32::MAX as i64]),
2508 ] {
2509 let flags = CcsdsFlags::PREPROCESS;
2510 let (payload, representation) = encode_ccsds_test_values(&values, bits, flags, 16);
2511 let decoded = decode_payload(&payload, &representation, None, values.len()).unwrap();
2512 let expected = values.iter().map(|value| *value as f64).collect::<Vec<_>>();
2513 assert_eq!(decoded, expected, "bits_per_value={bits}");
2514 }
2515 }
2516
2517 #[cfg(feature = "ccsds")]
2518 #[test]
2519 fn decodes_ccsds_signed_preprocessing_as_unsigned_grib_differences() {
2520 let values = [0, 1, 2047, 2048, 4095];
2521 let flags = CcsdsFlags::SIGNED | CcsdsFlags::PREPROCESS;
2522 let (payload, mut representation) = encode_ccsds_test_values(&values, 12, flags, 16);
2523 let DataRepresentation::CcsdsPacking(params) = &mut representation else {
2524 unreachable!();
2525 };
2526 *params = CcsdsPackingParams::new(
2527 ScaledPackingParams {
2528 encoded_values: values.len(),
2529 reference_value: 10.0,
2530 binary_scale: 1,
2531 decimal_scale: 1,
2532 bits_per_value: 12,
2533 original_field_type: 0,
2534 },
2535 flags,
2536 CcsdsBlockSize::SIXTEEN,
2537 128,
2538 )
2539 .unwrap();
2540
2541 let decoded = decode_payload(&payload, &representation, None, values.len()).unwrap();
2542 let expected = values
2543 .iter()
2544 .map(|value| (10.0 + *value as f64 * 2.0) / 10.0)
2545 .collect::<Vec<_>>();
2546 assert_float_values(&decoded, &expected);
2547 }
2548
2549 #[cfg(feature = "ccsds")]
2550 #[test]
2551 fn decodes_ccsds_restricted_stream_with_wire_to_codec_flag_mapping() {
2552 let values = [0, 1, 2, 3, 15, 7, 4, 0];
2553 let flags = CcsdsFlags::RESTRICTED | CcsdsFlags::PREPROCESS;
2554 let (payload, representation) = encode_ccsds_test_values(&values, 4, flags, 16);
2555
2556 let decoded = decode_payload(&payload, &representation, None, values.len()).unwrap();
2557 assert_eq!(
2558 decoded,
2559 values.iter().map(|value| *value as f64).collect::<Vec<_>>()
2560 );
2561 assert!(super::ccsds_decoder_flags(flags).contains(grib_aec::AecFlags::RESTRICTED));
2562 }
2563
2564 #[cfg(feature = "ccsds")]
2565 #[test]
2566 fn decodes_ccsds_nonstandard_even_block_when_not_enforced() {
2567 let values = (0..24).collect::<Vec<_>>();
2568 let flags = CcsdsFlags::PREPROCESS | CcsdsFlags::NOT_ENFORCE;
2569 let (payload, representation) = encode_ccsds_test_values(&values, 8, flags, 12);
2570
2571 let decoded = decode_payload(&payload, &representation, None, values.len()).unwrap();
2572 assert_eq!(
2573 decoded,
2574 values.iter().map(|value| *value as f64).collect::<Vec<_>>()
2575 );
2576 }
2577
2578 #[test]
2579 fn zero_width_simple_packing_ignores_scale_factors() {
2580 let params = ScaledPackingParams {
2581 encoded_values: 3,
2582 reference_value: 273.5,
2583 binary_scale: 3,
2584 decimal_scale: 2,
2585 bits_per_value: 0,
2586 original_field_type: 0,
2587 };
2588 let decoded =
2589 decode_payload(&[], &DataRepresentation::SimplePacking(params), None, 3).unwrap();
2590 assert_eq!(decoded, vec![273.5; 3]);
2591 }
2592
2593 #[test]
2594 fn zero_width_image_packings_decode_without_a_codestream() {
2595 let packing = ScaledPackingParams {
2596 encoded_values: 3,
2597 reference_value: 7.25,
2598 binary_scale: 0,
2599 decimal_scale: 1,
2600 bits_per_value: 0,
2601 original_field_type: 0,
2602 };
2603 let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1101_0000];
2604 for representation in [
2605 DataRepresentation::Jpeg2000Packing(Jpeg2000PackingParams {
2606 packing: packing.clone(),
2607 compression_type: 0,
2608 target_compression_ratio: 255,
2609 }),
2610 DataRepresentation::PngPacking(PngPackingParams { packing }),
2611 ] {
2612 let decoded = decode_payload(
2613 &[],
2614 &representation,
2615 bitmap_payload(&bitmap_section).unwrap(),
2616 4,
2617 )
2618 .unwrap();
2619 assert_eq!(decoded[0], 7.25);
2620 assert_eq!(decoded[1], 7.25);
2621 assert!(decoded[2].is_nan());
2622 assert_eq!(decoded[3], 7.25);
2623 }
2624 }
2625
2626 #[cfg(feature = "ccsds")]
2627 #[test]
2628 fn decodes_constant_ccsds_field_with_bitmap() {
2629 let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1011_0000];
2630 let representation = DataRepresentation::CcsdsPacking(
2631 CcsdsPackingParams::new(
2632 ScaledPackingParams {
2633 encoded_values: 3,
2634 reference_value: 42.0,
2635 binary_scale: 0,
2636 decimal_scale: 1,
2637 bits_per_value: 0,
2638 original_field_type: 0,
2639 },
2640 CcsdsFlags::DEFAULT,
2641 CcsdsBlockSize::THIRTY_TWO,
2642 128,
2643 )
2644 .unwrap(),
2645 );
2646
2647 let decoded = decode_payload(
2648 &[],
2649 &representation,
2650 bitmap_payload(&bitmap_section).unwrap(),
2651 4,
2652 )
2653 .unwrap();
2654 assert_eq!(decoded[0], 42.0);
2655 assert!(decoded[1].is_nan());
2656 assert_eq!(decoded[2], 42.0);
2657 assert_eq!(decoded[3], 42.0);
2658 }
2659
2660 #[cfg(not(feature = "ccsds"))]
2661 #[test]
2662 fn ccsds_packing_requires_ccsds_feature() {
2663 let representation = DataRepresentation::CcsdsPacking(
2664 CcsdsPackingParams::new(
2665 ScaledPackingParams {
2666 encoded_values: 1,
2667 reference_value: 0.0,
2668 binary_scale: 0,
2669 decimal_scale: 0,
2670 bits_per_value: 8,
2671 original_field_type: 0,
2672 },
2673 CcsdsFlags::DEFAULT,
2674 CcsdsBlockSize::THIRTY_TWO,
2675 128,
2676 )
2677 .unwrap(),
2678 );
2679
2680 let error = decode_payload(&[], &representation, None, 1).unwrap_err();
2681 assert!(matches!(error, Error::UnsupportedDataTemplate(42)));
2682 }
2683
2684 #[cfg(feature = "png")]
2685 #[test]
2686 fn decodes_png_packing_grayscale8() {
2687 let payload = encode_png(
2688 2,
2689 2,
2690 png::ColorType::Grayscale,
2691 png::BitDepth::Eight,
2692 &[1, 2, 3, 4],
2693 );
2694 let representation = DataRepresentation::PngPacking(PngPackingParams {
2695 packing: ScaledPackingParams {
2696 encoded_values: 4,
2697 reference_value: 10.0,
2698 binary_scale: 1,
2699 decimal_scale: 1,
2700 bits_per_value: 8,
2701 original_field_type: 0,
2702 },
2703 });
2704
2705 let decoded = decode_payload(&payload, &representation, None, 4).unwrap();
2706 assert_float_values(&decoded, &[1.2, 1.4, 1.6, 1.8]);
2707 }
2708
2709 #[cfg(feature = "png")]
2710 #[test]
2711 fn decodes_png_packing_grayscale16() {
2712 let payload = encode_png(
2713 2,
2714 1,
2715 png::ColorType::Grayscale,
2716 png::BitDepth::Sixteen,
2717 &[0x01, 0x00, 0x02, 0x00],
2718 );
2719 let representation = DataRepresentation::PngPacking(PngPackingParams {
2720 packing: ScaledPackingParams {
2721 encoded_values: 2,
2722 reference_value: 0.0,
2723 binary_scale: 0,
2724 decimal_scale: 0,
2725 bits_per_value: 16,
2726 original_field_type: 0,
2727 },
2728 });
2729
2730 let decoded = decode_payload(&payload, &representation, None, 2).unwrap();
2731 assert_eq!(decoded, vec![256.0, 512.0]);
2732 }
2733
2734 #[cfg(feature = "png")]
2735 #[test]
2736 fn decodes_png_packing_grayscale_subbyte() {
2737 let payload = encode_png(
2738 4,
2739 1,
2740 png::ColorType::Grayscale,
2741 png::BitDepth::Four,
2742 &[0x12, 0x34],
2743 );
2744 let representation = DataRepresentation::PngPacking(PngPackingParams {
2745 packing: ScaledPackingParams {
2746 encoded_values: 4,
2747 reference_value: 0.0,
2748 binary_scale: 0,
2749 decimal_scale: 0,
2750 bits_per_value: 4,
2751 original_field_type: 0,
2752 },
2753 });
2754
2755 let decoded = decode_payload(&payload, &representation, None, 4).unwrap();
2756 assert_eq!(decoded, vec![1.0, 2.0, 3.0, 4.0]);
2757 }
2758
2759 #[cfg(feature = "png")]
2760 #[test]
2761 fn decodes_png_packing_rgb8() {
2762 let payload = encode_png(
2763 2,
2764 1,
2765 png::ColorType::Rgb,
2766 png::BitDepth::Eight,
2767 &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
2768 );
2769 let representation = DataRepresentation::PngPacking(PngPackingParams {
2770 packing: ScaledPackingParams {
2771 encoded_values: 2,
2772 reference_value: 0.0,
2773 binary_scale: 0,
2774 decimal_scale: 0,
2775 bits_per_value: 24,
2776 original_field_type: 0,
2777 },
2778 });
2779
2780 let decoded = decode_payload(&payload, &representation, None, 2).unwrap();
2781 assert_eq!(decoded, vec![0x01_02_03 as f64, 0x04_05_06 as f64]);
2782 }
2783
2784 #[cfg(feature = "png")]
2785 #[test]
2786 fn decodes_png_packing_rgba8() {
2787 let payload = encode_png(
2788 1,
2789 1,
2790 png::ColorType::Rgba,
2791 png::BitDepth::Eight,
2792 &[0x01, 0x02, 0x03, 0x04],
2793 );
2794 let representation = DataRepresentation::PngPacking(PngPackingParams {
2795 packing: ScaledPackingParams {
2796 encoded_values: 1,
2797 reference_value: 0.0,
2798 binary_scale: 0,
2799 decimal_scale: 0,
2800 bits_per_value: 32,
2801 original_field_type: 0,
2802 },
2803 });
2804
2805 let decoded = decode_payload(&payload, &representation, None, 1).unwrap();
2806 assert_eq!(decoded, vec![0x01_02_03_04 as f64]);
2807 }
2808
2809 #[cfg(feature = "png")]
2810 #[test]
2811 fn decodes_png_packing_with_bitmap() {
2812 let payload = encode_png(
2813 3,
2814 1,
2815 png::ColorType::Grayscale,
2816 png::BitDepth::Eight,
2817 &[10, 20, 30],
2818 );
2819 let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1011_0000];
2820 let representation = DataRepresentation::PngPacking(PngPackingParams {
2821 packing: ScaledPackingParams {
2822 encoded_values: 3,
2823 reference_value: 0.0,
2824 binary_scale: 0,
2825 decimal_scale: 0,
2826 bits_per_value: 8,
2827 original_field_type: 0,
2828 },
2829 });
2830
2831 let decoded = decode_payload(
2832 &payload,
2833 &representation,
2834 bitmap_payload(&bitmap_section).unwrap(),
2835 4,
2836 )
2837 .unwrap();
2838 assert_eq!(decoded[0], 10.0);
2839 assert!(decoded[1].is_nan());
2840 assert_eq!(decoded[2], 20.0);
2841 assert_eq!(decoded[3], 30.0);
2842 }
2843
2844 #[cfg(feature = "png")]
2845 #[test]
2846 fn rejects_png_header_dimension_mismatch_before_allocating_frame_buffer() {
2847 let mut payload = encode_png(1, 1, png::ColorType::Grayscale, png::BitDepth::Eight, &[1]);
2848 payload[16..20].copy_from_slice(&100_000u32.to_be_bytes());
2849 payload[20..24].copy_from_slice(&100_000u32.to_be_bytes());
2850 let crc = test_crc32(&payload[12..29]);
2851 payload[29..33].copy_from_slice(&crc.to_be_bytes());
2852
2853 let representation = DataRepresentation::PngPacking(PngPackingParams {
2854 packing: ScaledPackingParams {
2855 encoded_values: 1,
2856 reference_value: 0.0,
2857 binary_scale: 0,
2858 decimal_scale: 0,
2859 bits_per_value: 8,
2860 original_field_type: 0,
2861 },
2862 });
2863
2864 let err = decode_payload(&payload, &representation, None, 1).unwrap_err();
2865 assert!(matches!(
2866 err,
2867 Error::DataLengthMismatch {
2868 expected: 1,
2869 actual: 10_000_000_000,
2870 }
2871 ));
2872 }
2873
2874 #[cfg(feature = "jpeg2000")]
2875 #[test]
2876 fn rejects_jpeg2000_header_dimension_mismatch_before_decoding_codestream() {
2877 let mut payload = vec![0u8; 45];
2878 payload[..4].copy_from_slice(&[0xff, 0x4f, 0xff, 0x51]);
2879 payload[4..6].copy_from_slice(&41u16.to_be_bytes());
2880 payload[8..12].copy_from_slice(&100_000u32.to_be_bytes());
2881 payload[12..16].copy_from_slice(&100_000u32.to_be_bytes());
2882 payload[40..42].copy_from_slice(&1u16.to_be_bytes());
2883 payload[42] = 7;
2884 payload[43] = 1;
2885 payload[44] = 1;
2886
2887 let representation = DataRepresentation::Jpeg2000Packing(super::Jpeg2000PackingParams {
2888 packing: ScaledPackingParams {
2889 encoded_values: 1,
2890 reference_value: 0.0,
2891 binary_scale: 0,
2892 decimal_scale: 0,
2893 bits_per_value: 8,
2894 original_field_type: 0,
2895 },
2896 compression_type: 0,
2897 target_compression_ratio: 0,
2898 });
2899
2900 let err = decode_payload(&payload, &representation, None, 1).unwrap_err();
2901 assert!(matches!(
2902 err,
2903 Error::DataLengthMismatch {
2904 expected: 1,
2905 actual: 10_000_000_000,
2906 }
2907 ));
2908 }
2909
2910 #[cfg(feature = "jpeg2000")]
2911 #[test]
2912 fn reports_negative_unsigned_jpeg2000_samples_as_out_of_range() {
2913 let error = super::jpeg2000_raw_value(-1, 8).unwrap_err();
2914 assert!(matches!(error, Error::ValueOutOfRange(_)));
2915 }
2916
2917 #[cfg(feature = "png")]
2918 #[test]
2919 fn reports_incompatible_png_layout_as_invalid_section_seven() {
2920 let payload = encode_png(1, 1, png::ColorType::Rgb, png::BitDepth::Eight, &[1, 2, 3]);
2921 let representation = DataRepresentation::PngPacking(PngPackingParams {
2922 packing: ScaledPackingParams {
2923 encoded_values: 1,
2924 reference_value: 0.0,
2925 binary_scale: 0,
2926 decimal_scale: 0,
2927 bits_per_value: 8,
2928 original_field_type: 0,
2929 },
2930 });
2931
2932 let error = decode_payload(&payload, &representation, None, 1).unwrap_err();
2933 assert!(matches!(error, Error::InvalidSection { section: 7, .. }));
2934 }
2935
2936 #[cfg(not(feature = "png"))]
2937 #[test]
2938 fn png_packing_requires_png_feature() {
2939 let representation = DataRepresentation::PngPacking(PngPackingParams {
2940 packing: ScaledPackingParams {
2941 encoded_values: 1,
2942 reference_value: 0.0,
2943 binary_scale: 0,
2944 decimal_scale: 0,
2945 bits_per_value: 8,
2946 original_field_type: 0,
2947 },
2948 });
2949
2950 let err = decode_payload(&[], &representation, None, 1).unwrap_err();
2951 assert!(matches!(err, Error::UnsupportedDataTemplate(41)));
2952 }
2953
2954 #[cfg(not(feature = "jpeg2000"))]
2955 #[test]
2956 fn jpeg2000_packing_requires_jpeg2000_feature() {
2957 let representation = DataRepresentation::Jpeg2000Packing(super::Jpeg2000PackingParams {
2958 packing: ScaledPackingParams {
2959 encoded_values: 1,
2960 reference_value: 0.0,
2961 binary_scale: 0,
2962 decimal_scale: 0,
2963 bits_per_value: 8,
2964 original_field_type: 0,
2965 },
2966 compression_type: 1,
2967 target_compression_ratio: 255,
2968 });
2969
2970 let err = decode_payload(&[], &representation, None, 1).unwrap_err();
2971 assert!(matches!(err, Error::UnsupportedDataTemplate(40)));
2972 }
2973
2974 #[test]
2975 fn rejects_simple_packing_wider_than_u64() {
2976 let params = ScaledPackingParams {
2977 encoded_values: 1,
2978 reference_value: 0.0,
2979 binary_scale: 0,
2980 decimal_scale: 0,
2981 bits_per_value: 65,
2982 original_field_type: 0,
2983 };
2984 let err = unpack_simple(&[0; 9], ¶ms, 1).unwrap_err();
2985 assert!(matches!(err, Error::UnsupportedPackingWidth(65)));
2986 }
2987
2988 #[test]
2989 fn rejects_encoded_value_count_mismatch_without_bitmap() {
2990 let data_section = [0, 0, 0, 8, 7, 10, 20, 30];
2991 let representation = DataRepresentation::SimplePacking(ScaledPackingParams {
2992 encoded_values: 3,
2993 reference_value: 0.0,
2994 binary_scale: 0,
2995 decimal_scale: 0,
2996 bits_per_value: 8,
2997 original_field_type: 0,
2998 });
2999
3000 let err = decode_field(&data_section, &representation, None, 4).unwrap_err();
3001 assert!(matches!(
3002 err,
3003 Error::DataLengthMismatch {
3004 expected: 4,
3005 actual: 3,
3006 }
3007 ));
3008 }
3009
3010 #[test]
3011 fn rejects_bitmap_present_count_mismatch() {
3012 let data_section = [0, 0, 0, 7, 7, 10, 20];
3013 let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1011_0000];
3014 let representation = DataRepresentation::SimplePacking(ScaledPackingParams {
3015 encoded_values: 2,
3016 reference_value: 0.0,
3017 binary_scale: 0,
3018 decimal_scale: 0,
3019 bits_per_value: 8,
3020 original_field_type: 0,
3021 });
3022
3023 let bitmap = bitmap_payload(&bitmap_section).unwrap();
3024 let err = decode_field(&data_section, &representation, bitmap, 4).unwrap_err();
3025 assert!(matches!(
3026 err,
3027 Error::DataLengthMismatch {
3028 expected: 3,
3029 actual: 2,
3030 }
3031 ));
3032 }
3033
3034 #[test]
3035 fn counts_bitmap_present_points_with_partial_bytes() {
3036 let present = count_bitmap_present_points(&[0b1011_1111], 3).unwrap();
3037 assert_eq!(present, 2);
3038 }
3039
3040 #[cfg(feature = "ccsds")]
3041 fn encode_ccsds_test_values(
3042 values: &[i64],
3043 bits_per_value: u8,
3044 flags: CcsdsFlags,
3045 block_size: u8,
3046 ) -> (Vec<u8>, DataRepresentation) {
3047 let bytes_per_sample = usize::from(bits_per_value).div_ceil(8);
3048 let mask = if bits_per_value == 32 {
3049 u32::MAX
3050 } else {
3051 (1u32 << bits_per_value) - 1
3052 };
3053 let mut unpacked = Vec::with_capacity(values.len() * bytes_per_sample);
3054 for &value in values {
3055 let raw = (value as u32) & mask;
3056 let bytes = raw.to_be_bytes();
3057 unpacked.extend_from_slice(&bytes[4 - bytes_per_sample..]);
3058 }
3059
3060 let codec_params = grib_aec::AecParams::new(
3061 bits_per_value,
3062 block_size,
3063 128,
3064 super::ccsds_decoder_flags(flags),
3065 )
3066 .unwrap();
3067 let payload = grib_aec::encode(&unpacked, codec_params).unwrap();
3068 let representation = DataRepresentation::CcsdsPacking(
3069 CcsdsPackingParams::new(
3070 ScaledPackingParams {
3071 encoded_values: values.len(),
3072 reference_value: 0.0,
3073 binary_scale: 0,
3074 decimal_scale: 0,
3075 bits_per_value,
3076 original_field_type: 0,
3077 },
3078 flags,
3079 CcsdsBlockSize::new(block_size).unwrap(),
3080 128,
3081 )
3082 .unwrap(),
3083 );
3084 (payload, representation)
3085 }
3086
3087 #[test]
3088 fn unpacks_complex_packing_with_explicit_missing() {
3089 let params = ComplexPackingParams {
3090 encoded_values: 4,
3091 reference_value: 0.0,
3092 binary_scale: 0,
3093 decimal_scale: 0,
3094 group_reference_bits: 4,
3095 original_field_type: 0,
3096 group_splitting_method: 1,
3097 missing_value_management: 1,
3098 primary_missing_substitute: 0x6655_4433,
3099 secondary_missing_substitute: 0x2211_00ff,
3100 num_groups: 2,
3101 group_width_reference: 0,
3102 group_width_bits: 2,
3103 group_length_reference: 2,
3104 group_length_increment: 1,
3105 true_length_last_group: 2,
3106 scaled_group_length_bits: 0,
3107 spatial_differencing: None,
3108 };
3109
3110 assert_eq!(params.primary_missing_substitute(), Some(0x6655_4433));
3111 assert_eq!(params.secondary_missing_substitute(), None);
3112
3113 let values = unpack_complex(&[0x79, 0x90, 0x34], ¶ms).unwrap();
3114 assert_eq!(values[0], 7.0);
3115 assert!(values[1].is_nan());
3116 assert_eq!(values[2], 9.0);
3117 assert!(values[3].is_nan());
3118 }
3119
3120 #[test]
3121 fn unpacks_complex_packing_applies_decimal_scale_to_reference_and_values() {
3122 let params = ComplexPackingParams {
3123 encoded_values: 2,
3124 reference_value: 10.0,
3125 binary_scale: 0,
3126 decimal_scale: 1,
3127 group_reference_bits: 4,
3128 original_field_type: 0,
3129 group_splitting_method: 1,
3130 missing_value_management: 0,
3131 primary_missing_substitute: u32::MAX,
3132 secondary_missing_substitute: u32::MAX,
3133 num_groups: 1,
3134 group_width_reference: 4,
3135 group_width_bits: 0,
3136 group_length_reference: 2,
3137 group_length_increment: 1,
3138 true_length_last_group: 2,
3139 scaled_group_length_bits: 0,
3140 spatial_differencing: None,
3141 };
3142
3143 let values = unpack_complex(&[0x10, 0x02], ¶ms).unwrap();
3144 assert!((values[0] - 1.1).abs() < 1e-12);
3145 assert!((values[1] - 1.3).abs() < 1e-12);
3146 }
3147
3148 #[test]
3149 fn unpacks_complex_packing_with_second_order_spatial_differencing() {
3150 let params = ComplexPackingParams {
3151 encoded_values: 4,
3152 reference_value: 0.0,
3153 binary_scale: 0,
3154 decimal_scale: 0,
3155 group_reference_bits: 1,
3156 original_field_type: 0,
3157 group_splitting_method: 1,
3158 missing_value_management: 0,
3159 primary_missing_substitute: u32::MAX,
3160 secondary_missing_substitute: u32::MAX,
3161 num_groups: 1,
3162 group_width_reference: 1,
3163 group_width_bits: 0,
3164 group_length_reference: 4,
3165 group_length_increment: 1,
3166 true_length_last_group: 4,
3167 scaled_group_length_bits: 0,
3168 spatial_differencing: Some(SpatialDifferencingParams {
3169 order: 2,
3170 descriptor_octets: 2,
3171 }),
3172 };
3173
3174 let values =
3175 unpack_complex(&[0x00, 0x0A, 0x00, 0x0D, 0x00, 0x03, 0x00, 0x10], ¶ms).unwrap();
3176 assert_eq!(values, vec![10.0, 13.0, 19.0, 29.0]);
3177 }
3178
3179 #[test]
3180 fn unpacks_complex_packing_with_spatial_differencing_and_missing_values() {
3181 let params = ComplexPackingParams {
3182 encoded_values: 4,
3183 reference_value: 0.0,
3184 binary_scale: 0,
3185 decimal_scale: 0,
3186 group_reference_bits: 1,
3187 original_field_type: 0,
3188 group_splitting_method: 1,
3189 missing_value_management: 1,
3190 primary_missing_substitute: u32::MAX,
3191 secondary_missing_substitute: u32::MAX,
3192 num_groups: 1,
3193 group_width_reference: 2,
3194 group_width_bits: 0,
3195 group_length_reference: 4,
3196 group_length_increment: 1,
3197 true_length_last_group: 4,
3198 scaled_group_length_bits: 0,
3199 spatial_differencing: Some(SpatialDifferencingParams {
3200 order: 1,
3201 descriptor_octets: 2,
3202 }),
3203 };
3204
3205 let values = unpack_complex(&[0x00, 0x0A, 0x00, 0x03, 0x00, 0x32], ¶ms).unwrap();
3206 assert_eq!(values[0], 10.0);
3207 assert!(values[1].is_nan());
3208 assert_eq!(values[2], 13.0);
3209 assert_eq!(values[3], 18.0);
3210 }
3211
3212 #[cfg(feature = "png")]
3213 fn encode_png(
3214 width: u32,
3215 height: u32,
3216 color_type: png::ColorType,
3217 bit_depth: png::BitDepth,
3218 data: &[u8],
3219 ) -> Vec<u8> {
3220 let mut payload = Vec::new();
3221 {
3222 let mut encoder = png::Encoder::new(&mut payload, width, height);
3223 encoder.set_color(color_type);
3224 encoder.set_depth(bit_depth);
3225 let mut writer = encoder.write_header().unwrap();
3226 writer.write_image_data(data).unwrap();
3227 }
3228 payload
3229 }
3230
3231 #[cfg(feature = "png")]
3232 fn test_crc32(bytes: &[u8]) -> u32 {
3233 let mut crc = u32::MAX;
3234 for &byte in bytes {
3235 crc ^= u32::from(byte);
3236 for _ in 0..8 {
3237 let mask = 0u32.wrapping_sub(crc & 1);
3238 crc = (crc >> 1) ^ (0xedb8_8320 & mask);
3239 }
3240 }
3241 !crc
3242 }
3243
3244 #[cfg(feature = "png")]
3245 fn assert_float_values(actual: &[f64], expected: &[f64]) {
3246 assert_eq!(actual.len(), expected.len());
3247 for (actual, expected) in actual.iter().zip(expected) {
3248 assert!(
3249 (actual - expected).abs() < 1e-12,
3250 "expected {expected}, got {actual}"
3251 );
3252 }
3253 }
3254}