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 grid: SectionRef,
18 pub product: SectionRef,
19 pub data_representation: SectionRef,
20 pub bitmap: Option<SectionRef>,
21 pub data: SectionRef,
22}
23
24pub fn scan_sections(msg_bytes: &[u8]) -> Result<Vec<SectionRef>> {
26 if msg_bytes.len() < 20 {
27 return Err(Error::InvalidMessage(format!(
28 "GRIB2 message too short: {} bytes",
29 msg_bytes.len()
30 )));
31 }
32
33 let mut sections = Vec::new();
34 let mut pos = 16;
35
36 while pos < msg_bytes.len() {
37 if pos + 4 <= msg_bytes.len() && &msg_bytes[pos..pos + 4] == b"7777" {
38 if pos != msg_bytes.len() - 4 {
39 return Err(Error::InvalidMessage(
40 "end section 7777 encountered before message end".into(),
41 ));
42 }
43 sections.push(SectionRef {
44 number: 8,
45 offset: pos,
46 length: 4,
47 });
48 return Ok(sections);
49 }
50
51 if pos + 5 > msg_bytes.len() {
52 return Err(Error::Truncated { offset: pos as u64 });
53 }
54
55 let length = u32::from_be_bytes(msg_bytes[pos..pos + 4].try_into().unwrap()) as usize;
56 let number = msg_bytes[pos + 4];
57
58 if length < 5 {
59 return Err(Error::InvalidSection {
60 section: number,
61 reason: format!("section length {length} is smaller than the 5-byte header"),
62 });
63 }
64 if pos + length > msg_bytes.len() {
65 return Err(Error::Truncated { offset: pos as u64 });
66 }
67
68 sections.push(SectionRef {
69 number,
70 offset: pos,
71 length,
72 });
73 pos += length;
74 }
75
76 Err(Error::InvalidMessage("missing end section 7777".into()))
77}
78
79pub fn index_fields(msg_bytes: &[u8]) -> Result<Vec<FieldSections>> {
81 let sections = scan_sections(msg_bytes)?;
82 let identification = sections
83 .iter()
84 .copied()
85 .find(|section| section.number == 1)
86 .ok_or_else(|| Error::InvalidSectionOrder("missing identification section".into()))?;
87
88 let mut fields = Vec::new();
89 let mut current_grid = None;
90 let mut current_product = None;
91 let mut current_representation = None;
92 let mut current_bitmap = None;
93
94 for section in sections {
95 match section.number {
96 1 | 2 => {}
97 3 => {
98 current_grid = Some(section);
99 current_product = None;
100 current_representation = None;
101 current_bitmap = None;
102 }
103 4 => {
104 if current_grid.is_none() {
105 return Err(Error::InvalidSectionOrder(
106 "product definition encountered before grid definition".into(),
107 ));
108 }
109 current_product = Some(section);
110 current_representation = None;
111 current_bitmap = None;
112 }
113 5 => {
114 if current_product.is_none() {
115 return Err(Error::InvalidSectionOrder(
116 "data representation encountered before product definition".into(),
117 ));
118 }
119 current_representation = Some(section);
120 current_bitmap = None;
121 }
122 6 => {
123 if current_representation.is_none() {
124 return Err(Error::InvalidSectionOrder(
125 "bitmap encountered before data representation".into(),
126 ));
127 }
128 current_bitmap = Some(section);
129 }
130 7 => {
131 let grid = current_grid.ok_or_else(|| {
132 Error::InvalidSectionOrder(
133 "data section encountered before grid definition".into(),
134 )
135 })?;
136 let product = current_product.ok_or_else(|| {
137 Error::InvalidSectionOrder(
138 "data section encountered before product definition".into(),
139 )
140 })?;
141 let data_representation = current_representation.ok_or_else(|| {
142 Error::InvalidSectionOrder(
143 "data section encountered before data representation".into(),
144 )
145 })?;
146 fields.push(FieldSections {
147 identification,
148 grid,
149 product,
150 data_representation,
151 bitmap: current_bitmap,
152 data: section,
153 });
154 current_product = None;
155 current_representation = None;
156 current_bitmap = None;
157 }
158 8 => break,
159 other => {
160 return Err(Error::InvalidSection {
161 section: other,
162 reason: "unexpected section number".into(),
163 });
164 }
165 }
166 }
167
168 if fields.is_empty() {
169 return Err(Error::InvalidSectionOrder(
170 "message did not contain a complete field".into(),
171 ));
172 }
173
174 Ok(fields)
175}
176
177#[cfg(test)]
178mod tests {
179 use crate::error::Error;
180
181 use super::{index_fields, scan_sections};
182
183 fn section(number: u8, payload_len: usize) -> Vec<u8> {
184 let len = (payload_len + 5) as u32;
185 let mut bytes = len.to_be_bytes().to_vec();
186 bytes.push(number);
187 bytes.resize(len as usize, 0);
188 bytes
189 }
190
191 #[test]
192 fn scan_minimal_sections() {
193 let mut data = vec![0u8; 16];
194 data.extend_from_slice(§ion(1, 16));
195 data.extend_from_slice(§ion(3, 20));
196 data.extend_from_slice(§ion(4, 29));
197 data.extend_from_slice(§ion(5, 16));
198 data.extend_from_slice(§ion(7, 3));
199 data.extend_from_slice(b"7777");
200
201 let sections = scan_sections(&data).unwrap();
202 assert_eq!(sections.len(), 6);
203 assert_eq!(sections[0].number, 1);
204 assert_eq!(sections[4].number, 7);
205 assert_eq!(sections[5].number, 8);
206 }
207
208 #[test]
209 fn indexes_repeated_fields() {
210 let mut data = vec![0u8; 16];
211 data.extend_from_slice(§ion(1, 16));
212 data.extend_from_slice(§ion(3, 20));
213 data.extend_from_slice(§ion(4, 29));
214 data.extend_from_slice(§ion(5, 16));
215 data.extend_from_slice(§ion(7, 3));
216 data.extend_from_slice(§ion(4, 29));
217 data.extend_from_slice(§ion(5, 16));
218 data.extend_from_slice(§ion(7, 3));
219 data.extend_from_slice(b"7777");
220
221 let fields = index_fields(&data).unwrap();
222 assert_eq!(fields.len(), 2);
223 assert_eq!(fields[0].grid.number, 3);
224 assert_eq!(fields[1].product.number, 4);
225 }
226
227 #[test]
228 fn rejects_early_end_section_before_message_end() {
229 let mut data = vec![0u8; 16];
230 data.extend_from_slice(§ion(1, 16));
231 data.extend_from_slice(b"7777");
232 data.extend_from_slice(&[0, 0, 0, 0]);
233
234 let err = scan_sections(&data).unwrap_err();
235 assert!(matches!(err, Error::InvalidMessage(_)));
236 }
237}