Skip to main content

grib_reader/
data.rs

1//! Data Section (Section 7) decoding.
2
3use 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
17/// Numeric target type for decoded field values.
18pub 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
43/// Decode Section 7 payload into field values, applying Section 6 bitmap when present.
44pub 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
65/// Decode Section 7 payload into a caller-provided buffer, applying Section 6
66/// bitmap when present.
67pub 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
222/// Returns the value of a constant field encoded with zero bits per value.
223///
224/// ecCodes and NCEP g2c store the constant directly in the reference value and
225/// ignore both scale factors when bits-per-value is zero, so the reference is
226/// returned unscaled. Image templates are resolved here without invoking a
227/// codec because their Section 7 payload is empty for constant fields.
228fn zero_width_constant(representation: &DataRepresentation, payload: &[u8]) -> Result<Option<f64>> {
229    let packing = match representation {
230        DataRepresentation::SimplePacking(params) => params,
231        DataRepresentation::Jpeg2000Packing(params) => &params.packing,
232        DataRepresentation::PngPacking(params) => &params.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
518/// Parse bitmap presence from Section 6.
519pub 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(&section_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
566/// Unpack simple-packed values.
567pub 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        // SIGNED controls the AEC preprocessor. GRIB template 5.42 still
765        // stores non-negative scaled differences, so reconstruction uses the
766        // declared low bits as an unsigned value.
767        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    // MSB and THREE_BYTE describe the caller's decoded sample containers, not
775    // the CCSDS bitstream. Request one canonical, compact big-endian layout so
776    // the reader never depends on host endianness or four-byte padding.
777    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    // Parse the SIZ marker in safe Rust before OpenJPEG allocates or decodes
822    // sample data. jpeg2k's header-only DumpImage path currently frees a null
823    // OpenJPEG code-block pointer on some valid codestreams, so it cannot be
824    // used as the validation boundary.
825    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, &params.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            &params.packing,
1127            output,
1128        ),
1129        (png::ColorType::Grayscale, png::BitDepth::Eight, 8) => {
1130            unpack_png_bytes(data, 1, num_values, &params.packing, output)
1131        }
1132        (png::ColorType::Grayscale, png::BitDepth::Sixteen, 16) => {
1133            unpack_png_u16(data, num_values, &params.packing, output)
1134        }
1135        (png::ColorType::Rgb, png::BitDepth::Eight, 24) => {
1136            unpack_png_bytes(data, 3, num_values, &params.packing, output)
1137        }
1138        (png::ColorType::Rgba, png::BitDepth::Eight, 32) => {
1139            unpack_png_bytes(data, 4, num_values, &params.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
1674/// Bounds-checked cursor over one group of fixed-width packed values.
1675///
1676/// The whole group is validated against the payload up front so each value is
1677/// extracted from a single unaligned big-endian word without per-value
1678/// truncation checks.
1679struct 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    /// Returns the next value. Callers must not read more than the `count`
1712    /// values validated by [`PackedValues::new`].
1713    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            // Widths above 57 bits can straddle nine bytes.
1721            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
1730/// Loads up to eight bytes starting at `byte` as a big-endian word, padding
1731/// past the end of `data` with zeros.
1732fn 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
1744/// Writes `count` decoded values, filling a contiguous output slice when no
1745/// bitmap applies.
1746fn 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/// Embedded missing-value codes for one complex-packing bit width, resolved
1764/// once per group instead of once per value.
1765#[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    /// Restores a value once the order's initial values have been emitted.
2085    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(&[], &params, 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], &params, 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(), &params, 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], &params, 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        // With range 5, digits 2 and 1 encode seven repetitions after the
2327        // first level value: 1 + 2 + 1*5 = 8 total values.
2328        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], &params, 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], &params).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], &params).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], &params).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], &params).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}