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