Skip to main content

grib_reader/
data.rs

1//! Data Representation Section (Section 5) and Data Section (Section 7) decoding.
2
3use crate::error::{Error, Result};
4use crate::util::grib_i16;
5
6/// Data representation template number and parameters.
7#[derive(Debug, Clone, PartialEq)]
8pub enum DataRepresentation {
9    /// Template 5.0: Simple packing.
10    SimplePacking(SimplePackingParams),
11    /// Unsupported template.
12    Unsupported(u16),
13}
14
15/// Parameters for simple packing (Template 5.0).
16#[derive(Debug, Clone, PartialEq)]
17pub struct SimplePackingParams {
18    pub encoded_values: usize,
19    pub reference_value: f32,
20    pub binary_scale: i16,
21    pub decimal_scale: i16,
22    pub bits_per_value: u8,
23    pub original_field_type: u8,
24}
25
26impl DataRepresentation {
27    pub fn parse(section_bytes: &[u8]) -> Result<Self> {
28        if section_bytes.len() < 11 {
29            return Err(Error::InvalidSection {
30                section: 5,
31                reason: format!("expected at least 11 bytes, got {}", section_bytes.len()),
32            });
33        }
34        if section_bytes[4] != 5 {
35            return Err(Error::InvalidSection {
36                section: section_bytes[4],
37                reason: "not a data representation section".into(),
38            });
39        }
40
41        let template = u16::from_be_bytes(section_bytes[9..11].try_into().unwrap());
42        match template {
43            0 => parse_simple_packing(section_bytes),
44            _ => Ok(Self::Unsupported(template)),
45        }
46    }
47
48    pub fn encoded_values(&self) -> Option<usize> {
49        match self {
50            Self::SimplePacking(params) => Some(params.encoded_values),
51            Self::Unsupported(_) => None,
52        }
53    }
54}
55
56/// Decode Section 7 payload into field values, applying Section 6 bitmap when present.
57pub fn decode_field(
58    data_section: &[u8],
59    representation: &DataRepresentation,
60    bitmap_section: Option<&[u8]>,
61    num_grid_points: usize,
62) -> Result<Vec<f64>> {
63    if data_section.len() < 5 || data_section[4] != 7 {
64        return Err(Error::InvalidSection {
65            section: data_section.get(4).copied().unwrap_or(7),
66            reason: "not a data section".into(),
67        });
68    }
69
70    decode_payload(
71        &data_section[5..],
72        representation,
73        bitmap_section,
74        num_grid_points,
75    )
76}
77
78pub(crate) fn decode_payload(
79    payload: &[u8],
80    representation: &DataRepresentation,
81    bitmap_section: Option<&[u8]>,
82    num_grid_points: usize,
83) -> Result<Vec<f64>> {
84    match representation {
85        DataRepresentation::SimplePacking(params) => {
86            let unpacked = unpack_simple(payload, params, params.encoded_values)?;
87            match bitmap_section {
88                Some(bitmap) => apply_bitmap(bitmap, unpacked, num_grid_points),
89                None => {
90                    if params.encoded_values != num_grid_points {
91                        return Err(Error::DataLengthMismatch {
92                            expected: num_grid_points,
93                            actual: params.encoded_values,
94                        });
95                    }
96                    Ok(unpacked)
97                }
98            }
99        }
100        DataRepresentation::Unsupported(template) => Err(Error::UnsupportedDataTemplate(*template)),
101    }
102}
103
104/// Parse bitmap presence from Section 6.
105pub fn bitmap_payload(section_bytes: &[u8]) -> Result<Option<&[u8]>> {
106    if section_bytes.len() < 6 {
107        return Err(Error::InvalidSection {
108            section: 6,
109            reason: format!("expected at least 6 bytes, got {}", section_bytes.len()),
110        });
111    }
112    if section_bytes[4] != 6 {
113        return Err(Error::InvalidSection {
114            section: section_bytes[4],
115            reason: "not a bitmap section".into(),
116        });
117    }
118
119    match section_bytes[5] {
120        255 => Ok(None),
121        0 => Ok(Some(&section_bytes[6..])),
122        indicator => Err(Error::UnsupportedBitmapIndicator(indicator)),
123    }
124}
125
126fn parse_simple_packing(data: &[u8]) -> Result<DataRepresentation> {
127    if data.len() < 21 {
128        return Err(Error::InvalidSection {
129            section: 5,
130            reason: format!("template 5.0 requires 21 bytes, got {}", data.len()),
131        });
132    }
133
134    let encoded_values = u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize;
135    let reference_value = f32::from_be_bytes(data[11..15].try_into().unwrap());
136    let binary_scale = grib_i16(&data[15..17]).unwrap();
137    let decimal_scale = grib_i16(&data[17..19]).unwrap();
138    let bits_per_value = data[19];
139    let original_field_type = data[20];
140
141    Ok(DataRepresentation::SimplePacking(SimplePackingParams {
142        encoded_values,
143        reference_value,
144        binary_scale,
145        decimal_scale,
146        bits_per_value,
147        original_field_type,
148    }))
149}
150
151/// Unpack simple-packed values.
152pub fn unpack_simple(
153    data_bytes: &[u8],
154    params: &SimplePackingParams,
155    num_values: usize,
156) -> Result<Vec<f64>> {
157    let bits = params.bits_per_value as usize;
158    if bits == 0 {
159        return Ok(vec![params.reference_value as f64; num_values]);
160    }
161    if bits > u64::BITS as usize {
162        return Err(Error::UnsupportedPackingWidth(params.bits_per_value));
163    }
164
165    let required_bits = bits
166        .checked_mul(num_values)
167        .ok_or_else(|| Error::Other("bit count overflow during unpacking".into()))?;
168    let required_bytes = required_bits.div_ceil(8);
169    if data_bytes.len() < required_bytes {
170        return Err(Error::Truncated {
171            offset: data_bytes.len() as u64,
172        });
173    }
174
175    let binary_factor = 2.0_f64.powi(params.binary_scale as i32);
176    let decimal_factor = 10.0_f64.powi(-(params.decimal_scale as i32));
177    let reference = params.reference_value as f64;
178    let mut reader = BitReader::new(data_bytes);
179    let mut values = Vec::with_capacity(num_values);
180
181    for _ in 0..num_values {
182        let packed = reader.read(bits)?;
183        values.push(reference + (packed as f64) * binary_factor * decimal_factor);
184    }
185
186    Ok(values)
187}
188
189fn apply_bitmap(
190    bitmap_payload: &[u8],
191    packed_values: Vec<f64>,
192    num_grid_points: usize,
193) -> Result<Vec<f64>> {
194    let mut decoded = Vec::with_capacity(num_grid_points);
195    let mut packed_iter = packed_values.into_iter();
196    let mut present_points = 0usize;
197
198    for bit_index in 0..num_grid_points {
199        if bitmap_bit(bitmap_payload, bit_index)? {
200            present_points += 1;
201            decoded.push(packed_iter.next().ok_or(Error::MissingBitmap)?);
202        } else {
203            decoded.push(f64::NAN);
204        }
205    }
206
207    let extra_values = packed_iter.count();
208    if extra_values > 0 {
209        return Err(Error::DataLengthMismatch {
210            expected: present_points,
211            actual: present_points + extra_values,
212        });
213    }
214
215    Ok(decoded)
216}
217
218fn bitmap_bit(bitmap_payload: &[u8], index: usize) -> Result<bool> {
219    let byte_index = index / 8;
220    let bit_index = index % 8;
221    let byte = bitmap_payload
222        .get(byte_index)
223        .copied()
224        .ok_or(Error::MissingBitmap)?;
225    Ok(((byte >> (7 - bit_index)) & 1) != 0)
226}
227
228struct BitReader<'a> {
229    data: &'a [u8],
230    bit_offset: usize,
231}
232
233impl<'a> BitReader<'a> {
234    fn new(data: &'a [u8]) -> Self {
235        Self {
236            data,
237            bit_offset: 0,
238        }
239    }
240
241    fn read(&mut self, bit_count: usize) -> Result<u64> {
242        let mut remaining = bit_count;
243        let mut value = 0u64;
244
245        while remaining > 0 {
246            let byte_index = self.bit_offset / 8;
247            let bit_index = self.bit_offset % 8;
248            let byte = *self.data.get(byte_index).ok_or(Error::Truncated {
249                offset: byte_index as u64,
250            })?;
251            let available = 8 - bit_index;
252            let take = remaining.min(available);
253            let mask = ((1u16 << take) - 1) as u8;
254            let shift = available - take;
255            let bits = (byte >> shift) & mask;
256
257            value = (value << take) | bits as u64;
258            self.bit_offset += take;
259            remaining -= take;
260        }
261
262        Ok(value)
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::{
269        bitmap_payload, decode_field, unpack_simple, DataRepresentation, SimplePackingParams,
270    };
271    use crate::error::Error;
272
273    #[test]
274    fn unpack_simple_constant() {
275        let params = SimplePackingParams {
276            encoded_values: 5,
277            reference_value: 42.0,
278            binary_scale: 0,
279            decimal_scale: 0,
280            bits_per_value: 0,
281            original_field_type: 0,
282        };
283        let values = unpack_simple(&[], &params, 5).unwrap();
284        assert_eq!(values, vec![42.0; 5]);
285    }
286
287    #[test]
288    fn unpack_simple_basic() {
289        let params = SimplePackingParams {
290            encoded_values: 5,
291            reference_value: 0.0,
292            binary_scale: 0,
293            decimal_scale: 0,
294            bits_per_value: 8,
295            original_field_type: 0,
296        };
297        let values = unpack_simple(&[0, 1, 2, 3, 4], &params, 5).unwrap();
298        assert_eq!(values, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
299    }
300
301    #[test]
302    fn decodes_bitmap_masked_field() {
303        let data_section = [0, 0, 0, 8, 7, 10, 20, 30];
304        let bitmap_section = [0, 0, 0, 7, 6, 0, 0b1011_0000];
305        let representation = DataRepresentation::SimplePacking(SimplePackingParams {
306            encoded_values: 3,
307            reference_value: 0.0,
308            binary_scale: 0,
309            decimal_scale: 0,
310            bits_per_value: 8,
311            original_field_type: 0,
312        });
313
314        let bitmap = bitmap_payload(&bitmap_section).unwrap();
315        let decoded = decode_field(&data_section, &representation, bitmap, 4).unwrap();
316        assert_eq!(decoded[0], 10.0);
317        assert!(decoded[1].is_nan());
318        assert_eq!(decoded[2], 20.0);
319        assert_eq!(decoded[3], 30.0);
320    }
321
322    #[test]
323    fn rejects_simple_packing_wider_than_u64() {
324        let params = SimplePackingParams {
325            encoded_values: 1,
326            reference_value: 0.0,
327            binary_scale: 0,
328            decimal_scale: 0,
329            bits_per_value: 65,
330            original_field_type: 0,
331        };
332        let err = unpack_simple(&[0; 9], &params, 1).unwrap_err();
333        assert!(matches!(err, Error::UnsupportedPackingWidth(65)));
334    }
335
336    #[test]
337    fn rejects_encoded_value_count_mismatch_without_bitmap() {
338        let data_section = [0, 0, 0, 8, 7, 10, 20, 30];
339        let representation = DataRepresentation::SimplePacking(SimplePackingParams {
340            encoded_values: 3,
341            reference_value: 0.0,
342            binary_scale: 0,
343            decimal_scale: 0,
344            bits_per_value: 8,
345            original_field_type: 0,
346        });
347
348        let err = decode_field(&data_section, &representation, None, 4).unwrap_err();
349        assert!(matches!(
350            err,
351            Error::DataLengthMismatch {
352                expected: 4,
353                actual: 3,
354            }
355        ));
356    }
357}