Skip to main content

grib_reader/
grib1.rs

1//! GRIB Edition 1 parsing.
2
3use crate::data::{decode_payload, DataRepresentation};
4use crate::error::{Error, Result};
5use crate::indicator::grib1_section4_length;
6use crate::sections::SectionRef;
7
8use grib_core::binary::read_u24_be;
9pub use grib_core::grib1::{BinaryDataSection, GridDescription, ProductDefinition};
10
11pub fn bitmap_payload(section_bytes: &[u8]) -> Result<Option<&[u8]>> {
12    let indicator = bitmap_table_reference(section_bytes)?;
13    if indicator == 0 {
14        Ok(Some(&section_bytes[6..]))
15    } else {
16        Err(Error::UnsupportedBitmapIndicator(indicator))
17    }
18}
19
20pub fn bitmap_table_reference(section_bytes: &[u8]) -> Result<u16> {
21    if section_bytes.len() < 6 {
22        return Err(Error::InvalidSection {
23            section: 3,
24            reason: format!("expected at least 6 bytes, got {}", section_bytes.len()),
25        });
26    }
27    Ok(u16::from_be_bytes(section_bytes[4..6].try_into().unwrap()))
28}
29
30pub fn decode_simple_field(
31    data_section: &[u8],
32    representation: &DataRepresentation,
33    bitmap_section: Option<&[u8]>,
34    num_grid_points: usize,
35) -> Result<Vec<f64>> {
36    decode_payload(
37        data_section,
38        representation,
39        bitmap_section,
40        num_grid_points,
41    )
42}
43
44pub fn parse_message_sections(message_bytes: &[u8]) -> Result<Grib1Sections> {
45    if message_bytes.len() < 8 + 28 + 11 + 4 {
46        return Err(Error::InvalidMessage(format!(
47            "GRIB1 message too short: {} bytes",
48            message_bytes.len()
49        )));
50    }
51
52    let payload_limit = message_bytes.len() - 4;
53    let pds = parse_section(message_bytes, 8, 1, payload_limit)?;
54    let pds_bytes = &message_bytes[pds.offset..pds.offset + pds.length];
55    let product = ProductDefinition::parse(pds_bytes)?;
56
57    let mut cursor = pds.offset + pds.length;
58    let grid = if product.has_grid_definition {
59        let section_ref = parse_section(message_bytes, cursor, 2, payload_limit)?;
60        cursor += section_ref.length;
61        Some(section_ref)
62    } else {
63        None
64    };
65
66    let bitmap = if product.has_bitmap {
67        let section_ref = parse_section(message_bytes, cursor, 3, payload_limit)?;
68        cursor += section_ref.length;
69        Some(section_ref)
70    } else {
71        None
72    };
73
74    let data_length = grib1_section4_length(message_bytes, cursor).ok_or(Error::Truncated {
75        offset: cursor as u64,
76    })?;
77    let data = parse_section_with_length(cursor, data_length, 4, payload_limit)?;
78    if data.offset + data.length != payload_limit {
79        return Err(Error::InvalidMessage(
80            "GRIB1 message contains trailing bytes before end marker".into(),
81        ));
82    }
83
84    Ok(Grib1Sections {
85        product,
86        pds,
87        grid,
88        bitmap,
89        data,
90    })
91}
92
93#[derive(Debug, Clone)]
94pub struct Grib1Sections {
95    pub product: ProductDefinition,
96    pub pds: SectionRef,
97    pub grid: Option<SectionRef>,
98    pub bitmap: Option<SectionRef>,
99    pub data: SectionRef,
100}
101
102fn parse_section(
103    message_bytes: &[u8],
104    offset: usize,
105    number: u8,
106    payload_limit: usize,
107) -> Result<SectionRef> {
108    let length_bytes = message_bytes
109        .get(offset..offset + 3)
110        .ok_or(Error::Truncated {
111            offset: offset as u64,
112        })?;
113    let length = read_u24_be(length_bytes).ok_or(Error::Truncated {
114        offset: offset as u64,
115    })? as usize;
116    parse_section_with_length(offset, length, number, payload_limit)
117}
118
119fn parse_section_with_length(
120    offset: usize,
121    length: usize,
122    number: u8,
123    payload_limit: usize,
124) -> Result<SectionRef> {
125    if length < 3 {
126        return Err(Error::InvalidSection {
127            section: number,
128            reason: format!("section length {length} is smaller than the 3-byte header"),
129        });
130    }
131
132    let end = offset
133        .checked_add(length)
134        .ok_or_else(|| Error::InvalidMessage("GRIB1 section length overflow".into()))?;
135    if end > payload_limit {
136        return Err(Error::Truncated {
137            offset: offset as u64,
138        });
139    }
140
141    Ok(section(number, offset, length))
142}
143
144fn section(number: u8, offset: usize, length: usize) -> SectionRef {
145    SectionRef {
146        number,
147        offset,
148        length,
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::{bitmap_payload, bitmap_table_reference, parse_message_sections};
155    use crate::error::Error;
156
157    #[test]
158    fn parses_minimal_section_layout() {
159        let mut message = Vec::new();
160        message.extend_from_slice(b"GRIB");
161        message.extend_from_slice(&[0, 0, 64, 1]);
162        let mut pds = vec![0u8; 28];
163        pds[..3].copy_from_slice(&[0, 0, 28]);
164        pds[7] = 0b1000_0000;
165        pds[12] = 26;
166        pds[13] = 3;
167        pds[14] = 20;
168        pds[15] = 12;
169        pds[24] = 21;
170        message.extend_from_slice(&pds);
171        let mut gds = vec![0u8; 32];
172        gds[..3].copy_from_slice(&[0, 0, 32]);
173        message.extend_from_slice(&gds);
174        let mut bds = vec![0u8; 12];
175        bds[..3].copy_from_slice(&[0, 0, 12]);
176        message.extend_from_slice(&bds);
177        message.extend_from_slice(b"7777");
178
179        let sections = parse_message_sections(&message).unwrap();
180        assert!(sections.grid.is_some());
181        assert!(sections.bitmap.is_none());
182        assert_eq!(sections.data.length, 12);
183    }
184
185    #[test]
186    fn resolves_large_message_section4_length_from_message_boundary() {
187        let mut message = Vec::new();
188        message.extend_from_slice(b"GRIB");
189        message.extend_from_slice(&[0x80, 0, 1, 1]);
190        let mut pds = vec![0u8; 28];
191        pds[..3].copy_from_slice(&[0, 0, 28]);
192        pds[7] = 0b1000_0000;
193        pds[12] = 26;
194        pds[13] = 3;
195        pds[14] = 20;
196        pds[15] = 12;
197        pds[24] = 21;
198        message.extend_from_slice(&pds);
199        let mut gds = vec![0u8; 32];
200        gds[..3].copy_from_slice(&[0, 0, 32]);
201        message.extend_from_slice(&gds);
202        let mut bds = vec![0u8; 12];
203        // ceil((84 - 4) / 120) * 120 - (84 - 4) = 40.
204        bds[..3].copy_from_slice(&[0, 0, 40]);
205        message.extend_from_slice(&bds);
206        message.extend_from_slice(b"7777");
207
208        let sections = parse_message_sections(&message).unwrap();
209        assert_eq!(sections.data.offset, 68);
210        assert_eq!(sections.data.length, 12);
211    }
212
213    #[test]
214    fn rejects_section_length_beyond_message_boundary() {
215        let mut message = Vec::new();
216        message.extend_from_slice(b"GRIB");
217        message.extend_from_slice(&[0, 0, 64, 1]);
218        let mut pds = vec![0u8; 28];
219        pds[..3].copy_from_slice(&[0, 0, 28]);
220        pds[7] = 0b1000_0000;
221        pds[12] = 26;
222        pds[13] = 3;
223        pds[14] = 20;
224        pds[15] = 12;
225        pds[24] = 21;
226        message.extend_from_slice(&pds);
227        let mut gds = vec![0u8; 32];
228        gds[..3].copy_from_slice(&[0, 0, 250]);
229        message.extend_from_slice(&gds);
230        let mut bds = vec![0u8; 12];
231        bds[..3].copy_from_slice(&[0, 0, 12]);
232        message.extend_from_slice(&bds);
233        message.extend_from_slice(b"7777");
234
235        assert!(parse_message_sections(&message).is_err());
236    }
237
238    #[test]
239    fn reports_predefined_bitmap_indicator() {
240        assert_eq!(bitmap_table_reference(&[0, 0, 6, 0, 1, 44]).unwrap(), 300);
241
242        let err = bitmap_payload(&[0, 0, 6, 0, 1, 44]).unwrap_err();
243        assert!(matches!(err, Error::UnsupportedBitmapIndicator(300)));
244    }
245}