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