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