1use 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(§ion_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}