1use crate::error::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct SectionRef {
8 pub number: u8,
9 pub offset: usize,
10 pub length: usize,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct FieldSections {
16 pub identification: SectionRef,
17 pub local_use: Option<SectionRef>,
18 pub grid: SectionRef,
19 pub product: SectionRef,
20 pub data_representation: SectionRef,
21 pub bitmap: Option<SectionRef>,
22 pub data: SectionRef,
23}
24
25pub fn scan_sections(msg_bytes: &[u8]) -> Result<Vec<SectionRef>> {
27 if msg_bytes.len() < 20 {
28 return Err(Error::InvalidMessage(format!(
29 "GRIB2 message too short: {} bytes",
30 msg_bytes.len()
31 )));
32 }
33
34 let mut sections = Vec::new();
35 let mut pos = 16;
36
37 while pos < msg_bytes.len() {
38 if pos + 4 <= msg_bytes.len() && &msg_bytes[pos..pos + 4] == b"7777" {
39 if pos != msg_bytes.len() - 4 {
40 return Err(Error::InvalidMessage(
41 "end section 7777 encountered before message end".into(),
42 ));
43 }
44 sections.push(SectionRef {
45 number: 8,
46 offset: pos,
47 length: 4,
48 });
49 return Ok(sections);
50 }
51
52 if pos + 5 > msg_bytes.len() {
53 return Err(Error::Truncated { offset: pos as u64 });
54 }
55
56 let length = u32::from_be_bytes(msg_bytes[pos..pos + 4].try_into().unwrap()) as usize;
57 let number = msg_bytes[pos + 4];
58
59 if length < 5 {
60 return Err(Error::InvalidSection {
61 section: number,
62 reason: format!("section length {length} is smaller than the 5-byte header"),
63 });
64 }
65 let end = pos
66 .checked_add(length)
67 .filter(|end| *end <= msg_bytes.len())
68 .ok_or(Error::Truncated { offset: pos as u64 })?;
69
70 sections.push(SectionRef {
71 number,
72 offset: pos,
73 length,
74 });
75 pos = end;
76 }
77
78 Err(Error::InvalidMessage("missing end section 7777".into()))
79}
80
81pub fn index_fields(msg_bytes: &[u8]) -> Result<Vec<FieldSections>> {
83 let sections = scan_sections(msg_bytes)?;
84 let identification = sections
85 .iter()
86 .copied()
87 .find(|section| section.number == 1)
88 .ok_or_else(|| Error::InvalidSectionOrder("missing identification section".into()))?;
89
90 let mut fields = Vec::new();
91 let mut current_local_use = None;
92 let mut current_grid = None;
93 let mut current_product = None;
94 let mut current_representation = None;
95 let mut current_bitmap = None;
96 let mut last_defined_bitmap = None;
97
98 for section in sections {
99 match section.number {
100 1 => {}
101 2 => {
102 if current_product.is_some() || current_representation.is_some() {
103 return Err(Error::InvalidSectionOrder(
104 "local use section encountered inside a field definition".into(),
105 ));
106 }
107 current_local_use = Some(section);
108 }
109 3 => {
110 current_grid = Some(section);
111 current_product = None;
112 current_representation = None;
113 current_bitmap = None;
114 }
115 4 => {
116 if current_grid.is_none() {
117 return Err(Error::InvalidSectionOrder(
118 "product definition encountered before grid definition".into(),
119 ));
120 }
121 current_product = Some(section);
122 current_representation = None;
123 current_bitmap = None;
124 }
125 5 => {
126 if current_product.is_none() {
127 return Err(Error::InvalidSectionOrder(
128 "data representation encountered before product definition".into(),
129 ));
130 }
131 current_representation = Some(section);
132 current_bitmap = None;
133 }
134 6 => {
135 if current_representation.is_none() {
136 return Err(Error::InvalidSectionOrder(
137 "bitmap encountered before data representation".into(),
138 ));
139 }
140 let bitmap_indicator =
141 *msg_bytes
142 .get(section.offset + 5)
143 .ok_or(Error::InvalidSection {
144 section: 6,
145 reason: "bitmap section is missing its indicator".into(),
146 })?;
147 match bitmap_indicator {
148 0 => {
149 current_bitmap = Some(section);
150 last_defined_bitmap = Some(section);
151 }
152 254 => {
153 current_bitmap = Some(last_defined_bitmap.ok_or(Error::MissingBitmap)?);
154 }
155 255 => current_bitmap = None,
156 indicator => {
157 return Err(Error::UnsupportedBitmapIndicator(u16::from(indicator)));
158 }
159 }
160 }
161 7 => {
162 let grid = current_grid.ok_or_else(|| {
163 Error::InvalidSectionOrder(
164 "data section encountered before grid definition".into(),
165 )
166 })?;
167 let product = current_product.ok_or_else(|| {
168 Error::InvalidSectionOrder(
169 "data section encountered before product definition".into(),
170 )
171 })?;
172 let data_representation = current_representation.ok_or_else(|| {
173 Error::InvalidSectionOrder(
174 "data section encountered before data representation".into(),
175 )
176 })?;
177 fields.push(FieldSections {
178 identification,
179 local_use: current_local_use,
180 grid,
181 product,
182 data_representation,
183 bitmap: current_bitmap,
184 data: section,
185 });
186 current_product = None;
187 current_representation = None;
188 current_bitmap = None;
189 }
190 8 => break,
191 other => {
192 return Err(Error::InvalidSection {
193 section: other,
194 reason: "unexpected section number".into(),
195 });
196 }
197 }
198 }
199
200 if fields.is_empty() {
201 return Err(Error::InvalidSectionOrder(
202 "message did not contain a complete field".into(),
203 ));
204 }
205
206 Ok(fields)
207}
208
209#[cfg(test)]
210mod tests {
211 use crate::error::Error;
212
213 use super::{index_fields, scan_sections};
214
215 fn section(number: u8, payload_len: usize) -> Vec<u8> {
216 let len = (payload_len + 5) as u32;
217 let mut bytes = len.to_be_bytes().to_vec();
218 bytes.push(number);
219 bytes.resize(len as usize, 0);
220 bytes
221 }
222
223 fn bitmap_section(indicator: u8, bitmap: &[u8]) -> Vec<u8> {
224 let length = 6 + bitmap.len();
225 let mut bytes = (length as u32).to_be_bytes().to_vec();
226 bytes.push(6);
227 bytes.push(indicator);
228 bytes.extend_from_slice(bitmap);
229 bytes
230 }
231
232 #[test]
233 fn scan_minimal_sections() {
234 let mut data = vec![0u8; 16];
235 data.extend_from_slice(§ion(1, 16));
236 data.extend_from_slice(§ion(3, 20));
237 data.extend_from_slice(§ion(4, 29));
238 data.extend_from_slice(§ion(5, 16));
239 data.extend_from_slice(§ion(7, 3));
240 data.extend_from_slice(b"7777");
241
242 let sections = scan_sections(&data).unwrap();
243 assert_eq!(sections.len(), 6);
244 assert_eq!(sections[0].number, 1);
245 assert_eq!(sections[4].number, 7);
246 assert_eq!(sections[5].number, 8);
247 }
248
249 #[test]
250 fn indexes_repeated_fields() {
251 let mut data = vec![0u8; 16];
252 data.extend_from_slice(§ion(1, 16));
253 data.extend_from_slice(§ion(3, 20));
254 data.extend_from_slice(§ion(4, 29));
255 data.extend_from_slice(§ion(5, 16));
256 data.extend_from_slice(§ion(7, 3));
257 data.extend_from_slice(§ion(4, 29));
258 data.extend_from_slice(§ion(5, 16));
259 data.extend_from_slice(§ion(7, 3));
260 data.extend_from_slice(b"7777");
261
262 let fields = index_fields(&data).unwrap();
263 assert_eq!(fields.len(), 2);
264 assert_eq!(fields[0].grid.number, 3);
265 assert_eq!(fields[0].local_use, None);
266 assert_eq!(fields[1].product.number, 4);
267 }
268
269 #[test]
270 fn associates_local_use_section_with_each_following_field() {
271 let mut data = vec![0u8; 16];
272 data.extend_from_slice(§ion(1, 16));
273 data.extend_from_slice(§ion(2, 3));
274 data.extend_from_slice(§ion(3, 20));
275 data.extend_from_slice(§ion(4, 29));
276 data.extend_from_slice(§ion(5, 16));
277 data.extend_from_slice(§ion(7, 3));
278 data.extend_from_slice(§ion(4, 29));
279 data.extend_from_slice(§ion(5, 16));
280 data.extend_from_slice(§ion(7, 3));
281 data.extend_from_slice(b"7777");
282
283 let fields = index_fields(&data).unwrap();
284 assert_eq!(fields.len(), 2);
285 assert_eq!(fields[0].local_use.unwrap().number, 2);
286 assert_eq!(fields[1].local_use, fields[0].local_use);
287 }
288
289 #[test]
290 fn resolves_previously_defined_bitmap_indicator() {
291 let mut data = vec![0u8; 16];
292 data.extend_from_slice(§ion(1, 16));
293 data.extend_from_slice(§ion(3, 20));
294 data.extend_from_slice(§ion(4, 29));
295 data.extend_from_slice(§ion(5, 16));
296 data.extend_from_slice(&bitmap_section(0, &[0b1010_0000]));
297 data.extend_from_slice(§ion(7, 3));
298 data.extend_from_slice(§ion(4, 29));
299 data.extend_from_slice(§ion(5, 16));
300 data.extend_from_slice(&bitmap_section(254, &[]));
301 data.extend_from_slice(§ion(7, 3));
302 data.extend_from_slice(b"7777");
303
304 let fields = index_fields(&data).unwrap();
305 assert_eq!(fields.len(), 2);
306 assert_eq!(fields[1].bitmap, fields[0].bitmap);
307 assert_eq!(
308 data[fields[1].bitmap.unwrap().offset + 5],
309 0,
310 "resolved field must point to the defining bitmap"
311 );
312 }
313
314 #[test]
315 fn rejects_previous_bitmap_indicator_without_definition() {
316 let mut data = vec![0u8; 16];
317 data.extend_from_slice(§ion(1, 16));
318 data.extend_from_slice(§ion(3, 20));
319 data.extend_from_slice(§ion(4, 29));
320 data.extend_from_slice(§ion(5, 16));
321 data.extend_from_slice(&bitmap_section(254, &[]));
322 data.extend_from_slice(§ion(7, 3));
323 data.extend_from_slice(b"7777");
324
325 assert!(matches!(index_fields(&data), Err(Error::MissingBitmap)));
326 }
327
328 #[test]
329 fn rejects_early_end_section_before_message_end() {
330 let mut data = vec![0u8; 16];
331 data.extend_from_slice(§ion(1, 16));
332 data.extend_from_slice(b"7777");
333 data.extend_from_slice(&[0, 0, 0, 0]);
334
335 let err = scan_sections(&data).unwrap_err();
336 assert!(matches!(err, Error::InvalidMessage(_)));
337 }
338}