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