Skip to main content

grib/
encoder.rs

1pub use complex::*;
2use grib_template_helpers::WriteToBuffer;
3pub use simple::*;
4
5use crate::def::grib2::template::param_set;
6
7/// Encodes a sequence of numerical values as GRIB2 data sections.
8pub fn encode_gpv(data: &[f64], method: EncodingMethod) -> EncodeOutput {
9    let output = match method {
10        EncodingMethod::SimplePacking(simple_packing_strategy) => {
11            let encoder = simple::Encoder::new(data, simple_packing_strategy);
12            EncodeOutputInner::SimplePacking(encoder.encode())
13        }
14        EncodingMethod::ComplexPacking(
15            simple_packing_strategy,
16            complex_packing_strategy,
17            _spatial_differencing_option,
18        ) => {
19            let encoder =
20                complex::Encoder::new(data, simple_packing_strategy, complex_packing_strategy);
21            EncodeOutputInner::ComplexPacking(encoder.encode())
22        }
23    };
24    EncodeOutput(output)
25}
26
27#[derive(Debug, PartialEq, Eq, Clone)]
28pub enum EncodingMethod {
29    /// Simple packing.
30    SimplePacking(SimplePackingStrategy),
31    /// Complex packing.
32    ComplexPacking(
33        SimplePackingStrategy,
34        ComplexPackingStrategy,
35        SpatialDifferencingOption,
36    ),
37}
38
39/// Data obtained through encoding. Instances are typically used to write GRIB2
40/// data via the methods defined in [`WriteGrib2DataSections`].
41#[derive(Debug)]
42pub struct EncodeOutput(EncodeOutputInner);
43
44impl EncodeOutput {
45    /// Returns the parameter set.
46    pub fn params(&self) -> EncodeOutputParams<'_> {
47        match &self.0 {
48            EncodeOutputInner::SimplePacking(encoded) => {
49                EncodeOutputParams::SimplePacking(encoded.params())
50            }
51            EncodeOutputInner::ComplexPacking(encoded) => {
52                let (simple, complex) = encoded.params();
53                EncodeOutputParams::ComplexPacking(simple, complex)
54            }
55        }
56    }
57}
58
59impl WriteGrib2DataSections for EncodeOutput {
60    fn section5_len(&self) -> usize {
61        match &self.0 {
62            EncodeOutputInner::SimplePacking(encoded) => encoded.section5_len(),
63            EncodeOutputInner::ComplexPacking(encoded) => encoded.section5_len(),
64        }
65    }
66
67    fn write_section5(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
68        match &self.0 {
69            EncodeOutputInner::SimplePacking(encoded) => encoded.write_section5(buf),
70            EncodeOutputInner::ComplexPacking(encoded) => encoded.write_section5(buf),
71        }
72    }
73
74    fn section6_len(&self) -> usize {
75        match &self.0 {
76            EncodeOutputInner::SimplePacking(encoded) => encoded.section6_len(),
77            EncodeOutputInner::ComplexPacking(encoded) => encoded.section6_len(),
78        }
79    }
80
81    fn write_section6(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
82        match &self.0 {
83            EncodeOutputInner::SimplePacking(encoded) => encoded.write_section6(buf),
84            EncodeOutputInner::ComplexPacking(encoded) => encoded.write_section6(buf),
85        }
86    }
87
88    fn section7_len(&self) -> usize {
89        match &self.0 {
90            EncodeOutputInner::SimplePacking(encoded) => encoded.section7_len(),
91            EncodeOutputInner::ComplexPacking(encoded) => encoded.section7_len(),
92        }
93    }
94
95    fn write_section7(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
96        match &self.0 {
97            EncodeOutputInner::SimplePacking(encoded) => encoded.write_section7(buf),
98            EncodeOutputInner::ComplexPacking(encoded) => encoded.write_section7(buf),
99        }
100    }
101}
102
103pub enum EncodeOutputParams<'a> {
104    SimplePacking(&'a param_set::SimplePacking),
105    ComplexPacking(&'a param_set::SimplePacking, &'a param_set::ComplexPacking),
106}
107
108#[derive(Debug)]
109enum EncodeOutputInner {
110    SimplePacking(simple::Encoded),
111    ComplexPacking(complex::Encoded),
112}
113
114trait Encode {
115    type Output;
116
117    fn encode(&self) -> Self::Output;
118}
119
120/// A serializer that writes the byte sequence of sections concerning GPV data
121/// to the output buffer.
122pub trait WriteGrib2DataSections {
123    /// Returns the length of the byte sequence in Section 5.
124    fn section5_len(&self) -> usize;
125
126    /// Writes the byte sequence of Section 5 to the output buffer.
127    fn write_section5(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
128
129    /// Returns the length of the byte sequence in Section 6.
130    fn section6_len(&self) -> usize;
131
132    /// Writes the byte sequence of Section 6 to the output buffer.
133    fn write_section6(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
134
135    /// Returns the length of the byte sequence in Section 7.
136    fn section7_len(&self) -> usize;
137
138    /// Writes the byte sequence of Section 7 to the output buffer.
139    fn write_section7(&self, buf: &mut [u8]) -> Result<usize, &'static str>;
140}
141
142pub fn write_section0(discipline: u8, len: usize, buf: &mut [u8]) -> Result<usize, &'static str> {
143    const HEAD: [u8; 6] = [0x47, 0x52, 0x49, 0x42, 0xff, 0xff];
144    const EDITION: u8 = 2;
145    const LEN: usize = 16;
146    if buf.len() < LEN {
147        return Err("destination buffer is too small");
148    }
149
150    let mut pos = 0;
151    pos += HEAD.write_to_buffer(&mut buf[pos..])?;
152    pos += discipline.write_to_buffer(&mut buf[pos..])?;
153    pos += EDITION.write_to_buffer(&mut buf[pos..])?;
154    pos += (len as u64).write_to_buffer(&mut buf[pos..])?;
155    Ok(pos)
156}
157
158pub fn write_section1(
159    payload: &crate::def::grib2::Section1Payload,
160    buf: &mut [u8],
161) -> Result<usize, &'static str> {
162    const LEN: usize = 0x15;
163    if buf.len() < LEN {
164        return Err("destination buffer is too small");
165    }
166
167    let mut pos = 0;
168    pos += (LEN as u32).write_to_buffer(&mut buf[pos..])?;
169    pos += 1_u8.write_to_buffer(&mut buf[pos..])?;
170    pos += payload.write_to_buffer(&mut buf[pos..])?;
171    Ok(pos)
172}
173
174pub fn write_section3(
175    payload: &crate::def::grib2::Section3Payload,
176    buf: &mut [u8],
177) -> Result<usize, &'static str> {
178    let len: usize = 5 + payload.num_bytes_required();
179    if buf.len() < len {
180        return Err("destination buffer is too small");
181    }
182
183    let mut pos = 0;
184    pos += (len as u32).write_to_buffer(&mut buf[pos..])?;
185    pos += 3_u8.write_to_buffer(&mut buf[pos..])?;
186    pos += payload.write_to_buffer(&mut buf[pos..])?;
187    Ok(pos)
188}
189
190pub fn write_section8(buf: &mut [u8]) -> Result<usize, &'static str> {
191    const SIGNATURE: [u8; 4] = [0x37, 0x37, 0x37, 0x37];
192    if buf.len() < SIGNATURE.num_bytes_required() {
193        return Err("destination buffer is too small");
194    }
195    SIGNATURE.write_to_buffer(buf)
196}
197
198mod bitmap;
199mod complex;
200mod helpers;
201mod simple;
202mod writer;
203
204#[cfg(test)]
205mod tests {
206    use grib_template_helpers::TryFromSlice as _;
207
208    use super::*;
209    use crate::def::grib2::Section1;
210
211    #[test]
212    fn grib2_section1_roundtrip_test() -> Result<(), Box<dyn std::error::Error>> {
213        let sect = Section1 {
214            header: crate::def::grib2::SectionHeader {
215                len: 21,
216                sect_num: 1,
217            },
218            payload: crate::def::grib2::Section1Payload {
219                centre_id: 0xffff,
220                subcentre_id: 0,
221                master_table_version: 29,
222                local_table_version: 0,
223                ref_time_significance: 0,
224                ref_time: crate::def::grib2::RefTime {
225                    year: 2026,
226                    month: 1,
227                    day: 2,
228                    hour: 3,
229                    minute: 4,
230                    second: 5,
231                },
232                prod_status: 0,
233                data_type: 0,
234                optional: None,
235            },
236        };
237        let mut buf = vec![0; 21];
238        write_section1(&sect.payload, &mut buf)?;
239        let decoded = Section1::try_from_slice(&buf, &mut 0)?;
240        assert_eq!(decoded, sect);
241        Ok(())
242    }
243}