1pub mod data;
42pub mod error;
43pub mod grib1;
44pub mod grid;
45pub mod indicator;
46pub mod metadata;
47pub mod parameter;
48pub mod product;
49pub mod sections;
50
51pub use data::{
52 CcsdsBlockSize, CcsdsFlags, CcsdsPackingParams, ComplexPackingParams, DataRepresentation,
53 DecodeSample, Grib1SecondOrderLayout, Grib1SecondOrderPackingParams, IeeePackingParams,
54 IeeePrecision, Jpeg2000PackingParams, PngPackingParams, RunLengthPackingParams,
55 ScaledPackingParams, SpatialDifferencingParams,
56};
57pub use error::{Error, Result};
58pub use grib1::{BinaryDataSection, GridDescription, ProductDefinition as Grib1ProductDefinition};
59pub use grid::{
60 AlbersEqualAreaGrid, GridDefinition, LambertConformalGrid, LatLonGrid, MercatorGrid,
61 PolarStereographicGrid, ProjectedGridCore, RegularGaussianGrid, RotatedLatLonGrid,
62 MISSING_INCREMENT,
63};
64pub use metadata::{ForecastTimeUnit, Parameter, ParameterTableSource, ReferenceTime};
65pub use parameter::{
66 LocalParameterEntry, LocalParameterTable, OwnedLocalParameterEntry, BUILTIN_LOCAL_PARAMETERS,
67 LOCAL_PARAMETER_TABLE_CSV_HEADER,
68};
69pub use product::{
70 AnalysisOrForecastTemplate, DerivedForecastTemplate, DerivedStatisticalProcessTemplate,
71 EnsembleStatisticalProcessTemplate, FixedSurface, Identification,
72 IndividualEnsembleForecastTemplate, PercentileForecastTemplate,
73 PercentileStatisticalProcessTemplate, ProbabilityForecastTemplate, ProbabilityLimit,
74 ProbabilityStatisticalProcessTemplate, ProbabilityType, ProductDefinition,
75 ProductDefinitionTemplate, ScaledValue, SpatialProcessTemplate, StatisticalInterval,
76 StatisticalProcessTemplate, StatisticalTimeRange,
77};
78
79use std::io::Read;
80use std::path::Path;
81use std::sync::Arc;
82
83use grib_core::bit::read_bit;
84use grib_core::{ensure_limit, filled_vec};
85use memchr::memmem::Finder;
86use memmap2::Mmap;
87use ndarray::{ArrayD, IxDyn};
88
89use crate::data::{
90 bitmap_payload as grib2_bitmap_payload, count_bitmap_present_points,
91 decode_field_into_with_group_lengths, decode_payload_into_with_group_lengths,
92};
93use crate::indicator::Indicator;
94use crate::parameter::lookup_parameter_with_local_entries;
95use crate::sections::{index_fields, FieldSections, SectionRef};
96
97#[cfg(feature = "rayon")]
98use rayon::prelude::*;
99
100const GRIB_MAGIC: &[u8; 4] = b"GRIB";
101pub const DEFAULT_MAX_DECODED_POINTS: usize = 25_000_000;
102pub const DEFAULT_MAX_AXIS_POINTS: usize = 1_000_000;
103
104#[derive(Debug, Clone, Copy)]
105struct ReaderOptions {
106 strict: bool,
107 max_decoded_points: Option<usize>,
108 max_axis_points: Option<usize>,
109}
110
111impl Default for ReaderOptions {
112 fn default() -> Self {
113 Self {
114 strict: true,
115 max_decoded_points: Some(DEFAULT_MAX_DECODED_POINTS),
116 max_axis_points: Some(DEFAULT_MAX_AXIS_POINTS),
117 }
118 }
119}
120
121#[derive(Debug, Clone, Copy, Default)]
124pub struct GribFileBuilder<'a> {
125 options: ReaderOptions,
126 local_parameters: &'a [LocalParameterEntry<'a>],
127 predefined_bitmaps: &'a [PredefinedBitmap<'a>],
128}
129
130impl<'a> GribFileBuilder<'a> {
131 pub fn strict(mut self, strict: bool) -> Self {
137 self.options.strict = strict;
138 self
139 }
140
141 pub fn max_decoded_points(mut self, max: usize) -> Self {
143 self.options.max_decoded_points = Some(max);
144 self
145 }
146
147 pub fn without_decoded_point_limit(mut self) -> Self {
149 self.options.max_decoded_points = None;
150 self
151 }
152
153 pub fn max_axis_points(mut self, max: usize) -> Self {
155 self.options.max_axis_points = Some(max);
156 self
157 }
158
159 pub fn without_axis_point_limit(mut self) -> Self {
161 self.options.max_axis_points = None;
162 self
163 }
164
165 pub fn without_limits(mut self) -> Self {
167 self.options.max_decoded_points = None;
168 self.options.max_axis_points = None;
169 self
170 }
171
172 pub fn local_parameters(mut self, entries: &'a [LocalParameterEntry<'a>]) -> Self {
174 self.local_parameters = entries;
175 self
176 }
177
178 pub fn grib1_predefined_bitmaps(mut self, bitmaps: &'a [PredefinedBitmap<'a>]) -> Self {
180 self.predefined_bitmaps = bitmaps;
181 self
182 }
183
184 pub fn open<P: AsRef<Path>>(self, path: P) -> Result<GribFile> {
186 let file = std::fs::File::open(path.as_ref())
187 .map_err(|err| Error::Io(err, path.as_ref().display().to_string()))?;
188 let mmap = unsafe { Mmap::map(&file) }
189 .map_err(|err| Error::Io(err, path.as_ref().display().to_string()))?;
190 GribFile::from_data(
191 GribData::Mmap(mmap),
192 self.options,
193 self.local_parameters,
194 self.predefined_bitmaps,
195 )
196 }
197
198 pub fn from_bytes(self, data: Vec<u8>) -> Result<GribFile> {
200 GribFile::from_data(
201 GribData::Bytes(data),
202 self.options,
203 self.local_parameters,
204 self.predefined_bitmaps,
205 )
206 }
207
208 pub fn from_reader<R: Read>(self, mut reader: R) -> Result<GribFile> {
210 let mut data = Vec::new();
211 let mut chunk = [0u8; 64 * 1024];
212 loop {
213 let read = match reader.read(&mut chunk) {
214 Ok(read) => read,
215 Err(err) if err.kind() == std::io::ErrorKind::Interrupted => continue,
216 Err(err) => return Err(Error::Io(err, "GRIB input stream".into())),
217 };
218 if read == 0 {
219 break;
220 }
221 data.try_reserve(read)
222 .map_err(|error| Error::allocation("GRIB input bytes", read, error))?;
223 data.extend_from_slice(&chunk[..read]);
224 }
225 self.from_bytes(data)
226 }
227}
228
229#[derive(Debug, Clone, Copy)]
235pub struct PredefinedBitmap<'a> {
236 pub center_id: u16,
237 pub subcenter_id: Option<u16>,
238 pub table_reference: u16,
239 pub bitmap: &'a [u8],
240}
241
242#[derive(Debug, Clone)]
244pub struct MessageMetadata {
245 pub edition: u8,
246 pub center_id: u16,
247 pub subcenter_id: u16,
248 pub discipline: Option<u8>,
249 pub reference_time: ReferenceTime,
250 pub parameter: Parameter,
251 pub grid: GridDefinition,
252 pub data_representation: DataRepresentation,
253 pub forecast_time_unit: Option<u8>,
254 pub forecast_time: Option<i64>,
255 pub message_offset: u64,
256 pub message_length: u64,
257 pub field_index_in_message: usize,
258 grib1_product: Option<grib1::ProductDefinition>,
259 grib2_identification: Option<Identification>,
260 grib2_product: Option<ProductDefinition>,
261}
262
263pub struct GribFile {
265 data: GribData,
266 messages: Vec<MessageIndex>,
267 options: ReaderOptions,
268}
269
270#[derive(Clone)]
271struct MessageIndex {
272 offset: usize,
273 length: usize,
274 metadata: MessageMetadata,
275 decode_plan: DecodePlan,
276 row_group_lengths: Option<Arc<[usize]>>,
277}
278
279#[derive(Clone)]
280enum DecodePlan {
281 Grib1 {
282 bitmap: Option<Grib1BitmapSource>,
283 data: SectionRef,
284 },
285 Grib2(FieldSections),
286}
287
288#[derive(Clone)]
289enum Grib1BitmapSource {
290 Explicit(SectionRef),
291 Predefined(Arc<[u8]>),
292}
293
294enum GribData {
295 Mmap(Mmap),
296 Bytes(Vec<u8>),
297}
298
299impl GribData {
300 fn as_bytes(&self) -> &[u8] {
301 match self {
302 GribData::Mmap(m) => m,
303 GribData::Bytes(b) => b,
304 }
305 }
306}
307
308impl GribFile {
309 pub fn builder<'a>() -> GribFileBuilder<'a> {
311 GribFileBuilder::default()
312 }
313
314 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
316 Self::builder().open(path)
317 }
318
319 pub fn from_bytes(data: Vec<u8>) -> Result<Self> {
321 Self::builder().from_bytes(data)
322 }
323
324 fn from_data(
325 data: GribData,
326 options: ReaderOptions,
327 local_parameters: &[LocalParameterEntry<'_>],
328 predefined_bitmaps: &[PredefinedBitmap<'_>],
329 ) -> Result<Self> {
330 let messages = scan_messages(
331 data.as_bytes(),
332 options,
333 local_parameters,
334 predefined_bitmaps,
335 )?;
336 if messages.is_empty() {
337 return Err(Error::NoMessages);
338 }
339 Ok(Self {
340 data,
341 messages,
342 options,
343 })
344 }
345
346 pub fn message_count(&self) -> usize {
348 self.messages.len()
349 }
350
351 pub fn message(&self, index: usize) -> Result<Message<'_>> {
353 if index >= self.messages.len() {
354 return Err(Error::MessageNotFound(index));
355 }
356 Ok(self.message_at(index))
357 }
358
359 fn message_at(&self, index: usize) -> Message<'_> {
360 let record = &self.messages[index];
361 let bytes = &self.data.as_bytes()[record.offset..record.offset + record.length];
362 Message {
363 index,
364 bytes,
365 metadata: &record.metadata,
366 decode_plan: &record.decode_plan,
367 row_group_lengths: record.row_group_lengths.as_deref(),
368 options: self.options,
369 }
370 }
371
372 pub fn messages(&self) -> MessageIter<'_> {
374 MessageIter {
375 file: self,
376 index: 0,
377 end: self.messages.len(),
378 }
379 }
380
381 pub fn read_all_data_as_f64(&self) -> Result<Vec<ArrayD<f64>>> {
383 #[cfg(feature = "rayon")]
384 {
385 (0..self.message_count())
386 .into_par_iter()
387 .map(|index| self.message_at(index).read_data_as_f64())
388 .collect()
389 }
390 #[cfg(not(feature = "rayon"))]
391 {
392 (0..self.message_count())
393 .map(|index| self.message_at(index).read_data_as_f64())
394 .collect()
395 }
396 }
397
398 pub fn read_all_data_as_f32(&self) -> Result<Vec<ArrayD<f32>>> {
400 #[cfg(feature = "rayon")]
401 {
402 (0..self.message_count())
403 .into_par_iter()
404 .map(|index| self.message_at(index).read_data_as_f32())
405 .collect()
406 }
407 #[cfg(not(feature = "rayon"))]
408 {
409 (0..self.message_count())
410 .map(|index| self.message_at(index).read_data_as_f32())
411 .collect()
412 }
413 }
414}
415
416impl std::fmt::Debug for GribFile {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 f.debug_struct("GribFile")
419 .field("message_count", &self.messages.len())
420 .finish_non_exhaustive()
421 }
422}
423
424pub struct Message<'a> {
426 bytes: &'a [u8],
427 metadata: &'a MessageMetadata,
428 decode_plan: &'a DecodePlan,
429 row_group_lengths: Option<&'a [usize]>,
430 options: ReaderOptions,
431 index: usize,
432}
433
434impl std::fmt::Debug for Message<'_> {
435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436 f.debug_struct("Message")
437 .field("index", &self.index)
438 .field("metadata", self.metadata)
439 .finish_non_exhaustive()
440 }
441}
442
443impl<'a> Message<'a> {
444 pub fn edition(&self) -> u8 {
445 self.metadata.edition
446 }
447
448 pub fn index(&self) -> usize {
449 self.index
450 }
451
452 pub fn metadata(&self) -> &MessageMetadata {
453 self.metadata
454 }
455
456 pub fn reference_time(&self) -> &ReferenceTime {
457 &self.metadata.reference_time
458 }
459
460 pub fn parameter(&self) -> &Parameter {
461 &self.metadata.parameter
462 }
463
464 pub fn center_id(&self) -> u16 {
465 self.metadata.center_id
466 }
467
468 pub fn subcenter_id(&self) -> u16 {
469 self.metadata.subcenter_id
470 }
471
472 pub fn identification(&self) -> Option<&Identification> {
473 self.metadata.grib2_identification.as_ref()
474 }
475
476 pub fn product_definition(&self) -> Option<&ProductDefinition> {
477 self.metadata.grib2_product.as_ref()
478 }
479
480 pub fn grib1_product_definition(&self) -> Option<&grib1::ProductDefinition> {
481 self.metadata.grib1_product.as_ref()
482 }
483
484 pub fn local_use_section(&self) -> Option<&'a [u8]> {
487 match self.decode_plan {
488 DecodePlan::Grib2(field) => field
489 .local_use
490 .map(|section| section_bytes(self.bytes, section)),
491 DecodePlan::Grib1 { .. } => None,
492 }
493 }
494
495 pub fn grid_definition(&self) -> &GridDefinition {
496 &self.metadata.grid
497 }
498
499 pub fn parameter_name(&self) -> &str {
500 self.metadata.parameter.short_name.as_ref()
501 }
502
503 pub fn parameter_description(&self) -> &str {
504 self.metadata.parameter.description.as_ref()
505 }
506
507 pub fn forecast_time_unit(&self) -> Option<u8> {
508 self.metadata.forecast_time_unit
509 }
510
511 pub fn forecast_time_unit_kind(&self) -> Option<ForecastTimeUnit> {
512 let unit = self.metadata.forecast_time_unit?;
513 ForecastTimeUnit::from_edition_and_code(self.metadata.edition, unit)
514 }
515
516 pub fn forecast_time(&self) -> Option<i64> {
520 self.metadata.forecast_time
521 }
522
523 pub fn valid_time(&self) -> Option<ReferenceTime> {
524 if let Some(end) = self
525 .metadata
526 .grib2_product
527 .as_ref()
528 .and_then(ProductDefinition::end_of_overall_time_interval)
529 {
530 return Some(end);
531 }
532
533 let unit = self.metadata.forecast_time_unit?;
534 let lead = match &self.metadata.grib1_product {
535 Some(product) => i64::from(product.valid_time_offset()?),
536 None => self.metadata.forecast_time?,
537 };
538 self.metadata
539 .reference_time
540 .checked_add_forecast_time_by_edition(self.metadata.edition, unit, lead)
541 }
542
543 pub fn grid_shape(&self) -> (usize, usize) {
544 self.metadata.grid.shape()
545 }
546
547 pub fn latitudes(&self) -> Result<Option<Vec<f64>>> {
548 self.metadata
549 .grid
550 .latitudes_with_limit(self.options.max_axis_points)
551 }
552
553 pub fn longitudes(&self) -> Result<Option<Vec<f64>>> {
554 self.metadata
555 .grid
556 .longitudes_with_limit(self.options.max_axis_points)
557 }
558
559 pub fn rotated_latitudes(&self) -> Result<Option<Vec<f64>>> {
561 self.metadata
562 .grid
563 .as_rotated_lat_lon()
564 .map(|grid| grid.rotated_latitudes_with_limit(self.options.max_axis_points))
565 .transpose()
566 }
567
568 pub fn rotated_longitudes(&self) -> Result<Option<Vec<f64>>> {
570 self.metadata
571 .grid
572 .as_rotated_lat_lon()
573 .map(|grid| grid.rotated_longitudes_with_limit(self.options.max_axis_points))
574 .transpose()
575 }
576
577 pub fn projected_x_coordinates(&self) -> Result<Option<Vec<f64>>> {
578 self.metadata
579 .grid
580 .projected_x_coordinates_with_limit(self.options.max_axis_points)
581 }
582
583 pub fn projected_y_coordinates(&self) -> Result<Option<Vec<f64>>> {
584 self.metadata
585 .grid
586 .projected_y_coordinates_with_limit(self.options.max_axis_points)
587 }
588
589 pub fn decode_into<T: DecodeSample>(&self, out: &mut [T]) -> Result<()> {
597 self.decode_scan_order_into(out)?;
598 self.metadata.grid.reorder_for_ndarray_in_place(out)
599 }
600
601 pub fn decode_into_north_up<T: DecodeSample>(&self, out: &mut [T]) -> Result<()> {
607 self.decode_scan_order_into(out)?;
608 self.metadata.grid.normalize_north_up_in_place(out)
609 }
610
611 fn decode_scan_order_into<T: DecodeSample>(&self, out: &mut [T]) -> Result<()> {
612 self.metadata.grid.validate_supported_scan_order()?;
613 let num_grid_points = self.checked_num_points()?;
614 if out.len() != num_grid_points {
615 return Err(Error::DataLengthMismatch {
616 expected: num_grid_points,
617 actual: out.len(),
618 });
619 }
620
621 match &self.decode_plan {
622 DecodePlan::Grib2(field) => {
623 let data_section = section_bytes(self.bytes, field.data);
624 let bitmap_section = match field.bitmap {
625 Some(section) => grib2_bitmap_payload(section_bytes(self.bytes, section))?,
626 None => None,
627 };
628 decode_field_into_with_group_lengths(
629 data_section,
630 &self.metadata.data_representation,
631 bitmap_section,
632 num_grid_points,
633 out,
634 self.row_group_lengths,
635 )?
636 }
637 DecodePlan::Grib1 { bitmap, data } => {
638 let bitmap_section = match bitmap {
639 Some(Grib1BitmapSource::Explicit(section)) => {
640 grib1::bitmap_payload(section_bytes(self.bytes, *section))?
641 }
642 Some(Grib1BitmapSource::Predefined(payload)) => Some(payload.as_ref()),
643 None => None,
644 };
645 let data_section = section_bytes(self.bytes, *data);
646 if data_section.len() < 11 {
647 return Err(Error::InvalidSection {
648 section: 4,
649 reason: format!("expected at least 11 bytes, got {}", data_section.len()),
650 });
651 }
652 decode_payload_into_with_group_lengths(
653 &data_section[11..],
654 &self.metadata.data_representation,
655 bitmap_section,
656 num_grid_points,
657 out,
658 self.row_group_lengths,
659 )?
660 }
661 }
662
663 Ok(())
664 }
665
666 pub fn read_flat_data_as_f64(&self) -> Result<Vec<f64>> {
672 let num_grid_points = self.checked_num_points()?;
673 let mut decoded = filled_vec(num_grid_points, 0.0_f64, "decoded f64 field")?;
674 self.decode_into(&mut decoded)?;
675 Ok(decoded)
676 }
677
678 pub fn read_flat_data_as_f32(&self) -> Result<Vec<f32>> {
684 let num_grid_points = self.checked_num_points()?;
685 let mut decoded = filled_vec(num_grid_points, 0.0_f32, "decoded f32 field")?;
686 self.decode_into(&mut decoded)?;
687 Ok(decoded)
688 }
689
690 pub fn read_flat_data_north_up_as_f64(&self) -> Result<Vec<f64>> {
692 let num_grid_points = self.checked_num_points()?;
693 let mut decoded = filled_vec(num_grid_points, 0.0_f64, "decoded f64 field")?;
694 self.decode_into_north_up(&mut decoded)?;
695 Ok(decoded)
696 }
697
698 pub fn read_flat_data_north_up_as_f32(&self) -> Result<Vec<f32>> {
700 let num_grid_points = self.checked_num_points()?;
701 let mut decoded = filled_vec(num_grid_points, 0.0_f32, "decoded f32 field")?;
702 self.decode_into_north_up(&mut decoded)?;
703 Ok(decoded)
704 }
705
706 pub fn read_data_as_f64(&self) -> Result<ArrayD<f64>> {
712 let ordered = self.read_flat_data_as_f64()?;
713 decoded_array(self.metadata.grid.ndarray_shape(), ordered)
714 }
715
716 pub fn read_data_as_f32(&self) -> Result<ArrayD<f32>> {
722 let ordered = self.read_flat_data_as_f32()?;
723 decoded_array(self.metadata.grid.ndarray_shape(), ordered)
724 }
725
726 pub fn read_data_north_up_as_f64(&self) -> Result<ArrayD<f64>> {
728 let ordered = self.read_flat_data_north_up_as_f64()?;
729 decoded_array(self.metadata.grid.ndarray_shape(), ordered)
730 }
731
732 pub fn read_data_north_up_as_f32(&self) -> Result<ArrayD<f32>> {
734 let ordered = self.read_flat_data_north_up_as_f32()?;
735 decoded_array(self.metadata.grid.ndarray_shape(), ordered)
736 }
737
738 pub fn raw_bytes(&self) -> &[u8] {
739 self.bytes
740 }
741
742 fn checked_num_points(&self) -> Result<usize> {
743 let num_grid_points = self.metadata.grid.checked_num_points()?;
744 ensure_limit(
745 "decoded grid points",
746 num_grid_points,
747 self.options.max_decoded_points,
748 )?;
749 Ok(num_grid_points)
750 }
751}
752
753fn decoded_array<T>(shape: Vec<usize>, values: Vec<T>) -> Result<ArrayD<T>> {
754 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|error| Error::InvalidSection {
755 section: 3,
756 reason: format!("grid shape cannot represent decoded field: {error}"),
757 })
758}
759
760#[derive(Clone)]
762pub struct MessageIter<'a> {
763 file: &'a GribFile,
764 index: usize,
765 end: usize,
766}
767
768impl<'a> Iterator for MessageIter<'a> {
769 type Item = Message<'a>;
770
771 fn next(&mut self) -> Option<Self::Item> {
772 if self.index >= self.end {
773 return None;
774 }
775 let message = self.file.message_at(self.index);
776 self.index += 1;
777 Some(message)
778 }
779
780 fn size_hint(&self) -> (usize, Option<usize>) {
781 let remaining = self.end - self.index;
782 (remaining, Some(remaining))
783 }
784
785 fn nth(&mut self, n: usize) -> Option<Self::Item> {
786 self.index = self.index.saturating_add(n).min(self.end);
787 self.next()
788 }
789}
790
791impl DoubleEndedIterator for MessageIter<'_> {
792 fn next_back(&mut self) -> Option<Self::Item> {
793 if self.index >= self.end {
794 return None;
795 }
796 self.end -= 1;
797 Some(self.file.message_at(self.end))
798 }
799}
800
801impl ExactSizeIterator for MessageIter<'_> {}
802
803impl std::iter::FusedIterator for MessageIter<'_> {}
804
805impl<'a> IntoIterator for &'a GribFile {
806 type Item = Message<'a>;
807 type IntoIter = MessageIter<'a>;
808
809 fn into_iter(self) -> Self::IntoIter {
810 self.messages()
811 }
812}
813
814fn section_bytes(msg_bytes: &[u8], section: SectionRef) -> &[u8] {
815 &msg_bytes[section.offset..section.offset + section.length]
816}
817
818fn scan_messages(
819 data: &[u8],
820 options: ReaderOptions,
821 local_parameters: &[LocalParameterEntry<'_>],
822 predefined_bitmaps: &[PredefinedBitmap<'_>],
823) -> Result<Vec<MessageIndex>> {
824 let mut messages = Vec::new();
825 let mut pos = 0usize;
826 let finder = Finder::new(GRIB_MAGIC);
827
828 while data.len().saturating_sub(pos) >= 8 {
829 let Some(relative_pos) = finder.find(&data[pos..]) else {
830 break;
831 };
832 pos = pos
833 .checked_add(relative_pos)
834 .ok_or(Error::ArithmeticOverflow {
835 operation: "locating a GRIB signature",
836 })?;
837 if data.len().saturating_sub(pos) < 8 {
838 break;
839 }
840
841 let (indicator, next_pos) = match locate_message(data, pos) {
842 Ok(located) => located,
843 Err(err) if !options.strict && is_recoverable_candidate_error(&err) => {
844 pos = pos
845 .checked_add(GRIB_MAGIC.len())
846 .ok_or(Error::ArithmeticOverflow {
847 operation: "advancing past a rejected GRIB signature",
848 })?;
849 continue;
850 }
851 Err(err) => return Err(err),
852 };
853
854 let message_bytes = &data[pos..next_pos];
855 let indexed = match indicator.edition {
856 1 => index_grib1_message(message_bytes, pos, predefined_bitmaps)?,
857 2 => index_grib2_message(message_bytes, pos, &indicator, local_parameters)?,
858 other => return Err(Error::UnsupportedEdition(other)),
859 };
860
861 messages.extend(indexed);
862 pos = next_pos;
863 }
864
865 Ok(messages)
866}
867
868fn is_recoverable_candidate_error(err: &Error) -> bool {
869 matches!(
870 err,
871 Error::InvalidMessage(_) | Error::Truncated { .. } | Error::UnsupportedEdition(_)
872 )
873}
874
875fn locate_message(data: &[u8], pos: usize) -> Result<(Indicator, usize)> {
876 let edition = Indicator::edition(&data[pos..]).ok_or_else(|| {
877 Error::InvalidMessage(format!("failed to parse indicator at byte offset {pos}"))
878 })?;
879 if !matches!(edition, 1 | 2) {
880 return Err(Error::UnsupportedEdition(edition));
881 }
882
883 let indicator = Indicator::parse(&data[pos..]).ok_or_else(|| {
884 Error::InvalidMessage(format!("failed to parse indicator at byte offset {pos}"))
885 })?;
886 let length = usize::try_from(indicator.total_length).map_err(|_| {
887 Error::InvalidMessage(format!(
888 "message at byte offset {pos} reports a length that exceeds usize"
889 ))
890 })?;
891 if length < 12 {
892 return Err(Error::InvalidMessage(format!(
893 "message at byte offset {pos} reports impossible length {length}"
894 )));
895 }
896 let end = pos
897 .checked_add(length)
898 .ok_or_else(|| Error::InvalidMessage("message length overflow".into()))?;
899 if end > data.len() {
900 return Err(Error::Truncated { offset: end as u64 });
901 }
902 if &data[end - 4..end] != b"7777" {
903 return Err(Error::InvalidMessage(format!(
904 "message at byte offset {pos} does not end with 7777"
905 )));
906 }
907
908 Ok((indicator, end))
909}
910
911fn index_grib1_message(
912 message_bytes: &[u8],
913 offset: usize,
914 predefined_bitmaps: &[PredefinedBitmap<'_>],
915) -> Result<Vec<MessageIndex>> {
916 let sections = grib1::parse_message_sections(message_bytes)?;
917 let grid_ref = sections.grid.ok_or_else(|| {
918 Error::InvalidSectionOrder(
919 "GRIB1 decoding requires an explicit grid definition section".into(),
920 )
921 })?;
922 let grid_description = GridDescription::parse(section_bytes(message_bytes, grid_ref))?;
923 let grid = grid_description.grid;
924 let num_grid_points = grid.checked_num_points()?;
925
926 let bitmap = match sections.bitmap {
927 Some(section) => {
928 let section_bytes = section_bytes(message_bytes, section);
929 let table_reference = grib1::bitmap_table_reference(section_bytes)?;
930 if table_reference == 0 {
931 Some(Grib1BitmapSource::Explicit(section))
932 } else {
933 let payload = resolve_predefined_bitmap(
934 predefined_bitmaps,
935 sections.product.center_id as u16,
936 sections.product.subcenter_id as u16,
937 table_reference,
938 )?;
939 Some(Grib1BitmapSource::Predefined(Arc::from(payload)))
940 }
941 }
942 None => None,
943 };
944
945 let bitmap_present_count = match &bitmap {
946 Some(Grib1BitmapSource::Explicit(section)) => {
947 let bitmap_payload = grib1::bitmap_payload(section_bytes(message_bytes, *section))?
948 .ok_or(Error::MissingBitmap)?;
949 count_bitmap_present_points(bitmap_payload, num_grid_points)?
950 }
951 Some(Grib1BitmapSource::Predefined(payload)) => {
952 count_bitmap_present_points(payload, num_grid_points)?
953 }
954 None => num_grid_points,
955 };
956
957 let (_bds, data_representation) = BinaryDataSection::parse(
958 section_bytes(message_bytes, sections.data),
959 sections.product.decimal_scale,
960 bitmap_present_count,
961 )?;
962 let bitmap_payload = match &bitmap {
963 Some(Grib1BitmapSource::Explicit(section)) => {
964 grib1::bitmap_payload(section_bytes(message_bytes, *section))?
965 }
966 Some(Grib1BitmapSource::Predefined(payload)) => Some(payload.as_ref()),
967 None => None,
968 };
969 let row_group_lengths =
970 row_group_lengths_for_representation(&grid, bitmap_payload, &data_representation)?;
971 let parameter = sections.product.parameter();
972
973 Ok(vec![MessageIndex {
974 offset,
975 length: message_bytes.len(),
976 decode_plan: DecodePlan::Grib1 {
977 bitmap,
978 data: sections.data,
979 },
980 row_group_lengths,
981 metadata: MessageMetadata {
982 edition: 1,
983 center_id: sections.product.center_id as u16,
984 subcenter_id: sections.product.subcenter_id as u16,
985 discipline: None,
986 reference_time: sections.product.reference_time,
987 parameter,
988 grid,
989 data_representation,
990 forecast_time_unit: Some(sections.product.forecast_time_unit),
991 forecast_time: sections.product.forecast_time().map(i64::from),
992 message_offset: offset as u64,
993 message_length: message_bytes.len() as u64,
994 field_index_in_message: 0,
995 grib1_product: Some(sections.product),
996 grib2_identification: None,
997 grib2_product: None,
998 },
999 }])
1000}
1001
1002fn resolve_predefined_bitmap<'a>(
1003 predefined_bitmaps: &[PredefinedBitmap<'a>],
1004 center_id: u16,
1005 subcenter_id: u16,
1006 table_reference: u16,
1007) -> Result<&'a [u8]> {
1008 predefined_bitmaps
1009 .iter()
1010 .find(|entry| {
1011 entry.center_id == center_id
1012 && entry.subcenter_id == Some(subcenter_id)
1013 && entry.table_reference == table_reference
1014 })
1015 .or_else(|| {
1016 predefined_bitmaps.iter().find(|entry| {
1017 entry.center_id == center_id
1018 && entry.subcenter_id.is_none()
1019 && entry.table_reference == table_reference
1020 })
1021 })
1022 .map(|entry| entry.bitmap)
1023 .ok_or(Error::UnsupportedBitmapIndicator(table_reference))
1024}
1025
1026fn row_group_lengths_for_representation(
1027 grid: &GridDefinition,
1028 bitmap_payload: Option<&[u8]>,
1029 representation: &DataRepresentation,
1030) -> Result<Option<Arc<[usize]>>> {
1031 let (expected_groups, expected_values, section) = match representation {
1032 DataRepresentation::Grib1SecondOrderPacking(params)
1033 if params.layout() == Grib1SecondOrderLayout::RowByRow =>
1034 {
1035 (params.group_count(), params.packing().encoded_values, 4)
1036 }
1037 DataRepresentation::ComplexPacking(params) if params.group_splitting_method == 0 => {
1038 (params.num_groups, params.encoded_values, 5)
1039 }
1040 _ => return Ok(None),
1041 };
1042
1043 let num_grid_points = grid.checked_num_points()?;
1044 let (points_per_line, number_of_lines) = grid.scan_line_shape()?;
1045 let shape_points =
1046 points_per_line
1047 .checked_mul(number_of_lines)
1048 .ok_or(Error::ArithmeticOverflow {
1049 operation: "computing scan-line grid point count",
1050 })?;
1051 if shape_points != num_grid_points {
1052 return Err(Error::InvalidSection {
1053 section: 3,
1054 reason: format!(
1055 "scan-line shape {points_per_line}x{number_of_lines} contains {shape_points} points, but the grid declares {num_grid_points}"
1056 ),
1057 });
1058 }
1059 if number_of_lines != expected_groups {
1060 return Err(Error::InvalidSection {
1061 section,
1062 reason: format!(
1063 "row-wise packing declares {expected_groups} groups, but the grid has {number_of_lines} scan lines"
1064 ),
1065 });
1066 }
1067
1068 let mut lengths = Vec::new();
1069 lengths
1070 .try_reserve_exact(number_of_lines)
1071 .map_err(|error| Error::allocation("row-wise group lengths", number_of_lines, error))?;
1072 match bitmap_payload {
1073 Some(bitmap) => {
1074 count_bitmap_present_points(bitmap, num_grid_points)?;
1075 for line_index in 0..number_of_lines {
1076 let line_start =
1077 line_index
1078 .checked_mul(points_per_line)
1079 .ok_or(Error::ArithmeticOverflow {
1080 operation: "computing bitmap scan-line offset",
1081 })?;
1082 let mut present = 0usize;
1083 for point_index in 0..points_per_line {
1084 if read_bit(bitmap, line_start + point_index)? {
1085 present += 1;
1086 }
1087 }
1088 lengths.push(present);
1089 }
1090 }
1091 None => lengths.resize(number_of_lines, points_per_line),
1092 }
1093
1094 let actual_values = lengths.iter().try_fold(0usize, |total, length| {
1095 total.checked_add(*length).ok_or(Error::ArithmeticOverflow {
1096 operation: "summing row-wise group lengths",
1097 })
1098 })?;
1099 if actual_values != expected_values {
1100 return Err(Error::DataLengthMismatch {
1101 expected: expected_values,
1102 actual: actual_values,
1103 });
1104 }
1105
1106 Ok(Some(Arc::from(lengths)))
1107}
1108
1109fn validate_grib2_grid_points(grid: &GridDefinition) -> Result<()> {
1110 let Some(declared) = grid.declared_num_points() else {
1111 return Ok(());
1112 };
1113 let projected = grid.shape_num_points()?;
1114 if declared != projected {
1115 return Err(Error::InvalidSection {
1116 section: 3,
1117 reason: format!(
1118 "grid template 3.{} declares {declared} points but Nx*Ny is {projected}",
1119 grid.template_number()
1120 ),
1121 });
1122 }
1123 Ok(())
1124}
1125
1126fn index_grib2_message(
1127 message_bytes: &[u8],
1128 offset: usize,
1129 indicator: &Indicator,
1130 local_parameters: &[LocalParameterEntry<'_>],
1131) -> Result<Vec<MessageIndex>> {
1132 let fields = index_fields(message_bytes)?;
1133 let mut messages = Vec::with_capacity(fields.len());
1134
1135 for (field_index_in_message, field_sections) in fields.into_iter().enumerate() {
1136 let identification =
1137 Identification::parse(section_bytes(message_bytes, field_sections.identification))?;
1138 let grid_section = section_bytes(message_bytes, field_sections.grid);
1139 let grid = GridDefinition::parse(grid_section)?;
1140 validate_grib2_grid_points(&grid)?;
1141 let product =
1142 ProductDefinition::parse(section_bytes(message_bytes, field_sections.product))?;
1143 let data_representation = DataRepresentation::parse(section_bytes(
1144 message_bytes,
1145 field_sections.data_representation,
1146 ))?;
1147 let bitmap_payload = match field_sections.bitmap {
1148 Some(section) => grib2_bitmap_payload(section_bytes(message_bytes, section))?,
1149 None => None,
1150 };
1151 let row_group_lengths =
1152 row_group_lengths_for_representation(&grid, bitmap_payload, &data_representation)?;
1153 let parameter = lookup_parameter_with_local_entries(
1154 indicator.discipline,
1155 product.parameter_category,
1156 product.parameter_number,
1157 identification.center_id,
1158 identification.subcenter_id,
1159 identification.local_table_version,
1160 local_parameters,
1161 );
1162
1163 messages.push(MessageIndex {
1164 offset,
1165 length: message_bytes.len(),
1166 decode_plan: DecodePlan::Grib2(field_sections),
1167 row_group_lengths,
1168 metadata: MessageMetadata {
1169 edition: indicator.edition,
1170 center_id: identification.center_id,
1171 subcenter_id: identification.subcenter_id,
1172 discipline: Some(indicator.discipline),
1173 reference_time: ReferenceTime {
1174 year: identification.reference_year,
1175 month: identification.reference_month,
1176 day: identification.reference_day,
1177 hour: identification.reference_hour,
1178 minute: identification.reference_minute,
1179 second: identification.reference_second,
1180 },
1181 parameter,
1182 grid,
1183 data_representation,
1184 forecast_time_unit: product.forecast_time_unit(),
1185 forecast_time: product.forecast_time().map(i64::from),
1186 message_offset: offset as u64,
1187 message_length: message_bytes.len() as u64,
1188 field_index_in_message,
1189 grib1_product: None,
1190 grib2_identification: Some(identification),
1191 grib2_product: Some(product),
1192 },
1193 });
1194 }
1195
1196 Ok(messages)
1197}
1198
1199#[cfg(test)]
1200mod tests {
1201 use super::*;
1202
1203 fn grib_i32_bytes(value: i32) -> [u8; 4] {
1204 if value >= 0 {
1205 (value as u32).to_be_bytes()
1206 } else {
1207 ((-value) as u32 | 0x8000_0000).to_be_bytes()
1208 }
1209 }
1210
1211 fn build_indicator(total_len: usize, discipline: u8) -> Vec<u8> {
1212 let mut indicator = Vec::with_capacity(16);
1213 indicator.extend_from_slice(b"GRIB");
1214 indicator.extend_from_slice(&[0, 0]);
1215 indicator.push(discipline);
1216 indicator.push(2);
1217 indicator.extend_from_slice(&(total_len as u64).to_be_bytes());
1218 indicator
1219 }
1220
1221 fn build_identification() -> Vec<u8> {
1222 let mut section = vec![0u8; 21];
1223 section[..4].copy_from_slice(&(21u32).to_be_bytes());
1224 section[4] = 1;
1225 section[5..7].copy_from_slice(&7u16.to_be_bytes());
1226 section[7..9].copy_from_slice(&0u16.to_be_bytes());
1227 section[9] = 35;
1228 section[10] = 1;
1229 section[11] = 1;
1230 section[12..14].copy_from_slice(&2026u16.to_be_bytes());
1231 section[14] = 3;
1232 section[15] = 20;
1233 section[16] = 12;
1234 section[17] = 0;
1235 section[18] = 0;
1236 section[19] = 0;
1237 section[20] = 1;
1238 section
1239 }
1240
1241 fn build_local_use(payload: &[u8]) -> Vec<u8> {
1242 let mut section = vec![0u8; payload.len() + 5];
1243 section[..4].copy_from_slice(&((payload.len() + 5) as u32).to_be_bytes());
1244 section[4] = 2;
1245 section[5..].copy_from_slice(payload);
1246 section
1247 }
1248
1249 fn build_grid(ni: u32, nj: u32, scanning_mode: u8) -> Vec<u8> {
1250 let mut section = vec![0u8; 72];
1251 section[..4].copy_from_slice(&(72u32).to_be_bytes());
1252 section[4] = 3;
1253 section[6..10].copy_from_slice(&(ni * nj).to_be_bytes());
1254 section[12..14].copy_from_slice(&0u16.to_be_bytes());
1255 section[30..34].copy_from_slice(&ni.to_be_bytes());
1256 section[34..38].copy_from_slice(&nj.to_be_bytes());
1257 section[46..50].copy_from_slice(&grib_i32_bytes(50_000_000));
1258 section[50..54].copy_from_slice(&grib_i32_bytes(-120_000_000));
1259 section[55..59].copy_from_slice(&grib_i32_bytes(49_000_000));
1260 section[59..63].copy_from_slice(&grib_i32_bytes(-119_000_000));
1261 section[63..67].copy_from_slice(&1_000_000u32.to_be_bytes());
1262 section[67..71].copy_from_slice(&1_000_000u32.to_be_bytes());
1263 section[71] = scanning_mode;
1264 section
1265 }
1266
1267 fn build_rotated_grid() -> Vec<u8> {
1268 let mut section = vec![0u8; 84];
1269 section[..4].copy_from_slice(&84u32.to_be_bytes());
1270 section[4] = 3;
1271 section[6..10].copy_from_slice(&6u32.to_be_bytes());
1272 section[12..14].copy_from_slice(&1u16.to_be_bytes());
1273 section[30..34].copy_from_slice(&3u32.to_be_bytes());
1274 section[34..38].copy_from_slice(&2u32.to_be_bytes());
1275 section[46..50].copy_from_slice(&grib_i32_bytes(-10_000_000));
1276 section[50..54].copy_from_slice(&grib_i32_bytes(-20_000_000));
1277 section[55..59].copy_from_slice(&grib_i32_bytes(0));
1278 section[59..63].copy_from_slice(&grib_i32_bytes(20_000_000));
1279 section[63..67].copy_from_slice(&20_000_000u32.to_be_bytes());
1280 section[67..71].copy_from_slice(&10_000_000u32.to_be_bytes());
1281 section[71] = 0b0100_0000;
1282 section[72..76].copy_from_slice(&grib_i32_bytes(-30_000_000));
1283 section[76..80].copy_from_slice(&10_000_000u32.to_be_bytes());
1284 section[80..84].copy_from_slice(&15.5f32.to_be_bytes());
1285 section
1286 }
1287
1288 fn build_regular_gaussian_grid() -> Vec<u8> {
1289 let mut section = vec![0u8; 72];
1290 section[..4].copy_from_slice(&72u32.to_be_bytes());
1291 section[4] = 3;
1292 section[6..10].copy_from_slice(&16u32.to_be_bytes());
1293 section[12..14].copy_from_slice(&40u16.to_be_bytes());
1294 section[30..34].copy_from_slice(&4u32.to_be_bytes());
1295 section[34..38].copy_from_slice(&4u32.to_be_bytes());
1296 section[46..50].copy_from_slice(&grib_i32_bytes(59_444_408));
1297 section[50..54].copy_from_slice(&grib_i32_bytes(0));
1298 section[55..59].copy_from_slice(&grib_i32_bytes(-59_444_408));
1299 section[59..63].copy_from_slice(&grib_i32_bytes(270_000_000));
1300 section[63..67].copy_from_slice(&90_000_000u32.to_be_bytes());
1301 section[67..71].copy_from_slice(&2u32.to_be_bytes());
1302 section
1303 }
1304
1305 fn build_product(parameter_category: u8, parameter_number: u8) -> Vec<u8> {
1306 let mut section = vec![0u8; 34];
1307 section[..4].copy_from_slice(&(34u32).to_be_bytes());
1308 section[4] = 4;
1309 section[7..9].copy_from_slice(&0u16.to_be_bytes());
1310 section[9] = parameter_category;
1311 section[10] = parameter_number;
1312 section[11] = 2;
1313 section[17] = 1;
1314 section[18..22].copy_from_slice(&0u32.to_be_bytes());
1315 section[22] = 103;
1316 section[23] = 0;
1317 section[24..28].copy_from_slice(&850u32.to_be_bytes());
1318 section[28] = 255;
1319 section
1320 }
1321
1322 fn build_simple_representation(encoded_values: usize, bits_per_value: u8) -> Vec<u8> {
1323 let mut section = vec![0u8; 21];
1324 section[..4].copy_from_slice(&(21u32).to_be_bytes());
1325 section[4] = 5;
1326 section[5..9].copy_from_slice(&(encoded_values as u32).to_be_bytes());
1327 section[9..11].copy_from_slice(&0u16.to_be_bytes());
1328 section[11..15].copy_from_slice(&0f32.to_be_bytes());
1329 section[19] = bits_per_value;
1330 section[20] = 0;
1331 section
1332 }
1333
1334 fn pack_u8_values(values: &[u8]) -> Vec<u8> {
1335 values.to_vec()
1336 }
1337
1338 fn build_bitmap(bits: &[bool]) -> Vec<u8> {
1339 let payload_len = bits.len().div_ceil(8) + 1;
1340 let mut section = vec![0u8; payload_len + 5];
1341 section[..4].copy_from_slice(&((payload_len + 5) as u32).to_be_bytes());
1342 section[4] = 6;
1343 section[5] = 0;
1344 for (index, bit) in bits.iter().copied().enumerate() {
1345 if bit {
1346 section[6 + index / 8] |= 1 << (7 - (index % 8));
1347 }
1348 }
1349 section
1350 }
1351
1352 fn build_reused_bitmap() -> Vec<u8> {
1353 let mut section = vec![0u8; 6];
1354 section[..4].copy_from_slice(&6u32.to_be_bytes());
1355 section[4] = 6;
1356 section[5] = 254;
1357 section
1358 }
1359
1360 fn build_data(payload: &[u8]) -> Vec<u8> {
1361 let mut section = vec![0u8; payload.len() + 5];
1362 section[..4].copy_from_slice(&((payload.len() + 5) as u32).to_be_bytes());
1363 section[4] = 7;
1364 section[5..].copy_from_slice(payload);
1365 section
1366 }
1367
1368 fn assemble_grib2_message(sections: &[Vec<u8>]) -> Vec<u8> {
1369 let total_len = 16 + sections.iter().map(|section| section.len()).sum::<usize>() + 4;
1370 let mut message = build_indicator(total_len, 0);
1371 for section in sections {
1372 message.extend_from_slice(section);
1373 }
1374 message.extend_from_slice(b"7777");
1375 message
1376 }
1377
1378 fn build_grib1_message(values: &[u8]) -> Vec<u8> {
1379 let mut pds = vec![0u8; 28];
1380 pds[..3].copy_from_slice(&[0, 0, 28]);
1381 pds[3] = 2;
1382 pds[4] = 7;
1383 pds[5] = 255;
1384 pds[6] = 0;
1385 pds[7] = 0b1000_0000;
1386 pds[8] = 11;
1387 pds[9] = 100;
1388 pds[10..12].copy_from_slice(&850u16.to_be_bytes());
1389 pds[12] = 26;
1390 pds[13] = 3;
1391 pds[14] = 20;
1392 pds[15] = 12;
1393 pds[16] = 0;
1394 pds[17] = 1;
1395 pds[18] = 0;
1396 pds[19] = 0;
1397 pds[20] = 0;
1398 pds[24] = 21;
1399 pds[25] = 0;
1400
1401 let mut gds = vec![0u8; 32];
1402 gds[..3].copy_from_slice(&[0, 0, 32]);
1403 gds[5] = 0;
1404 gds[6..8].copy_from_slice(&2u16.to_be_bytes());
1405 gds[8..10].copy_from_slice(&2u16.to_be_bytes());
1406 gds[10..13].copy_from_slice(&[0x01, 0x4d, 0x50]);
1407 gds[13..16].copy_from_slice(&[0x81, 0xd4, 0xc0]);
1408 gds[16] = 0x80;
1409 gds[17..20].copy_from_slice(&[0x01, 0x49, 0x68]);
1410 gds[20..23].copy_from_slice(&[0x81, 0xd0, 0xd8]);
1411 gds[23..25].copy_from_slice(&1000u16.to_be_bytes());
1412 gds[25..27].copy_from_slice(&1000u16.to_be_bytes());
1413
1414 let mut bds = vec![0u8; 11 + values.len()];
1415 let len = bds.len() as u32;
1416 bds[..3].copy_from_slice(&[(len >> 16) as u8, (len >> 8) as u8, len as u8]);
1417 bds[3] = 0;
1418 bds[10] = 8;
1419 bds[11..].copy_from_slice(values);
1420
1421 let total_len = 8 + pds.len() + gds.len() + bds.len() + 4;
1422 let mut message = Vec::new();
1423 message.extend_from_slice(b"GRIB");
1424 message.extend_from_slice(&[
1425 ((total_len >> 16) & 0xff) as u8,
1426 ((total_len >> 8) & 0xff) as u8,
1427 (total_len & 0xff) as u8,
1428 1,
1429 ]);
1430 message.extend_from_slice(&pds);
1431 message.extend_from_slice(&gds);
1432 message.extend_from_slice(&bds);
1433 message.extend_from_slice(b"7777");
1434 message
1435 }
1436
1437 fn build_grib1_message_with_predefined_bitmap(values: &[u8], table_reference: u16) -> Vec<u8> {
1438 let mut message = build_grib1_message(values);
1439 message[8 + 7] = 0b1100_0000;
1440
1441 let mut bitmap = [0u8; 6];
1442 bitmap[..3].copy_from_slice(&[0, 0, 6]);
1443 bitmap[4..6].copy_from_slice(&table_reference.to_be_bytes());
1444
1445 let bitmap_offset = 8 + 28 + 32;
1446 message.splice(bitmap_offset..bitmap_offset, bitmap);
1447 let total_len = message.len();
1448 message[4] = ((total_len >> 16) & 0xff) as u8;
1449 message[5] = ((total_len >> 8) & 0xff) as u8;
1450 message[6] = (total_len & 0xff) as u8;
1451 message
1452 }
1453
1454 fn build_grib1_second_order_message(layout: Grib1SecondOrderLayout) -> Vec<u8> {
1455 let mut message = build_grib1_message(&[0; 6]);
1456 let grid_offset = 8 + 28;
1457 message[grid_offset + 6..grid_offset + 8].copy_from_slice(&3u16.to_be_bytes());
1458 message[grid_offset + 8..grid_offset + 10].copy_from_slice(&2u16.to_be_bytes());
1459
1460 let (length, extended_flags, n1, n2, widths, bitmap_offset) = match layout {
1461 Grib1SecondOrderLayout::RowByRow => (27usize, 0x10, 24u16, 26u16, &[2, 2][..], None),
1462 Grib1SecondOrderLayout::ConstantWidth => (27, 0x20, 24, 26, &[2][..], Some(22usize)),
1463 Grib1SecondOrderLayout::VariableWidth => (28, 0x30, 25, 27, &[2, 2][..], Some(23usize)),
1464 _ => unreachable!(),
1465 };
1466 let mut bds = vec![0u8; length];
1467 bds[..3].copy_from_slice(&[
1468 ((length >> 16) & 0xff) as u8,
1469 ((length >> 8) & 0xff) as u8,
1470 (length & 0xff) as u8,
1471 ]);
1472 bds[3] = 0x44;
1473 bds[10] = 5;
1474 bds[11..13].copy_from_slice(&n1.to_be_bytes());
1475 bds[13] = extended_flags;
1476 bds[14..16].copy_from_slice(&n2.to_be_bytes());
1477 bds[16..18].copy_from_slice(&2u16.to_be_bytes());
1478 bds[18..20].copy_from_slice(&6u16.to_be_bytes());
1479 bds[21..21 + widths.len()].copy_from_slice(widths);
1480 if let Some(offset) = bitmap_offset {
1481 bds[offset] = 0b1001_0000;
1482 }
1483 let first_order_index = usize::from(n1) - 1;
1484 bds[first_order_index..first_order_index + 2].copy_from_slice(&[0x55, 0x00]);
1485 let second_order_index = usize::from(n2) - 1;
1486 bds[second_order_index..second_order_index + 2].copy_from_slice(&[0x18, 0xb0]);
1487
1488 let data_offset = 8 + 28 + 32;
1489 message.truncate(data_offset);
1490 message.extend_from_slice(&bds);
1491 message.extend_from_slice(b"7777");
1492 let total_len = message.len();
1493 message[4] = ((total_len >> 16) & 0xff) as u8;
1494 message[5] = ((total_len >> 8) & 0xff) as u8;
1495 message[6] = (total_len & 0xff) as u8;
1496 message
1497 }
1498
1499 fn build_grib1_row_by_row_message_with_primary_bitmap() -> Vec<u8> {
1500 let mut message = build_grib1_second_order_message(Grib1SecondOrderLayout::RowByRow);
1501 let data_offset = 8 + 28 + 32;
1502 let mut bds = vec![0u8; 26];
1503 bds[..3].copy_from_slice(&[0, 0, 26]);
1504 bds[3] = 0x44;
1505 bds[10] = 5;
1506 bds[11..13].copy_from_slice(&24u16.to_be_bytes());
1507 bds[13] = 0x10;
1508 bds[14..16].copy_from_slice(&26u16.to_be_bytes());
1509 bds[16..18].copy_from_slice(&2u16.to_be_bytes());
1510 bds[18..20].copy_from_slice(&2u16.to_be_bytes());
1511 bds[21..23].copy_from_slice(&[2, 0]);
1512 bds[23..25].copy_from_slice(&[0x50, 0x00]);
1513 bds[25] = 0x20;
1514
1515 message.truncate(data_offset);
1516 message[8 + 7] = 0b1100_0000;
1517 message.extend_from_slice(&[0, 0, 7, 2, 0, 0, 0b1010_0000]);
1518 message.extend_from_slice(&bds);
1519 message.extend_from_slice(b"7777");
1520 let total_len = message.len();
1521 message[4] = ((total_len >> 16) & 0xff) as u8;
1522 message[5] = ((total_len >> 8) & 0xff) as u8;
1523 message[6] = (total_len & 0xff) as u8;
1524 message
1525 }
1526
1527 #[test]
1528 fn scans_single_grib2_message() {
1529 let message = assemble_grib2_message(&[
1530 build_identification(),
1531 build_grid(2, 2, 0),
1532 build_product(0, 0),
1533 build_simple_representation(4, 8),
1534 build_data(&pack_u8_values(&[1, 2, 3, 4])),
1535 ]);
1536 let messages = scan_messages(&message, ReaderOptions::default(), &[], &[]).unwrap();
1537 assert_eq!(messages.len(), 1);
1538 assert_eq!(messages[0].metadata.parameter.short_name, "TMP");
1539 }
1540
1541 #[test]
1542 fn scans_across_large_non_grib_prefix() {
1543 let message = assemble_grib2_message(&[
1544 build_identification(),
1545 build_grid(2, 2, 0),
1546 build_product(0, 0),
1547 build_simple_representation(4, 8),
1548 build_data(&pack_u8_values(&[1, 2, 3, 4])),
1549 ]);
1550 let mut input = vec![0xa5; 1024 * 1024];
1551 input.extend_from_slice(&message);
1552
1553 let messages = scan_messages(&input, ReaderOptions::default(), &[], &[]).unwrap();
1554 assert_eq!(messages.len(), 1);
1555 assert_eq!(messages[0].offset, 1024 * 1024);
1556 }
1557
1558 #[test]
1559 fn decodes_simple_grib2_message_to_ndarray() {
1560 let message = assemble_grib2_message(&[
1561 build_identification(),
1562 build_grid(2, 2, 0),
1563 build_product(0, 0),
1564 build_simple_representation(4, 8),
1565 build_data(&pack_u8_values(&[1, 2, 3, 4])),
1566 ]);
1567 let file = GribFile::from_bytes(message).unwrap();
1568 let array = file.message(0).unwrap().read_data_as_f64().unwrap();
1569 assert_eq!(array.shape(), &[2, 2]);
1570 assert_eq!(
1571 array.iter().copied().collect::<Vec<_>>(),
1572 vec![1.0, 2.0, 3.0, 4.0]
1573 );
1574 }
1575
1576 #[test]
1577 fn decodes_rotated_latlon_grid_and_exposes_rotated_axes() {
1578 let message = assemble_grib2_message(&[
1579 build_identification(),
1580 build_rotated_grid(),
1581 build_product(0, 0),
1582 build_simple_representation(6, 8),
1583 build_data(&pack_u8_values(&[1, 2, 3, 4, 5, 6])),
1584 ]);
1585 let file = GribFile::from_bytes(message).unwrap();
1586 let field = file.message(0).unwrap();
1587
1588 assert_eq!(field.grid_shape(), (3, 2));
1589 assert_eq!(field.latitudes().unwrap(), None);
1590 assert_eq!(
1591 field.rotated_latitudes().unwrap().unwrap(),
1592 vec![-10.0, 0.0]
1593 );
1594 assert_eq!(
1595 field.rotated_longitudes().unwrap().unwrap(),
1596 vec![-20.0, 0.0, 20.0]
1597 );
1598 assert_eq!(
1599 field
1600 .read_data_as_f64()
1601 .unwrap()
1602 .iter()
1603 .copied()
1604 .collect::<Vec<_>>(),
1605 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
1606 );
1607 }
1608
1609 #[test]
1610 fn decodes_regular_gaussian_grid_and_computes_axes() {
1611 let values: Vec<_> = (0u8..16).collect();
1612 let message = assemble_grib2_message(&[
1613 build_identification(),
1614 build_regular_gaussian_grid(),
1615 build_product(0, 0),
1616 build_simple_representation(values.len(), 8),
1617 build_data(&pack_u8_values(&values)),
1618 ]);
1619 let file = GribFile::from_bytes(message).unwrap();
1620 let field = file.message(0).unwrap();
1621
1622 assert_eq!(field.grid_shape(), (4, 4));
1623 assert_eq!(
1624 field.longitudes().unwrap().unwrap(),
1625 vec![0.0, 90.0, 180.0, 270.0]
1626 );
1627 let latitudes = field.latitudes().unwrap().unwrap();
1628 assert!((latitudes[0] - 59.444_408_289_166_77).abs() < 1e-12);
1629 assert!((latitudes[3] + 59.444_408_289_166_77).abs() < 1e-12);
1630 assert_eq!(field.read_data_as_f64().unwrap().shape(), &[4, 4]);
1631 }
1632
1633 #[test]
1634 fn applies_bitmap_to_missing_values() {
1635 let message = assemble_grib2_message(&[
1636 build_identification(),
1637 build_grid(2, 2, 0),
1638 build_product(0, 1),
1639 build_simple_representation(3, 8),
1640 build_bitmap(&[true, false, true, true]),
1641 build_data(&pack_u8_values(&[10, 20, 30])),
1642 ]);
1643 let file = GribFile::from_bytes(message).unwrap();
1644 let array = file.message(0).unwrap().read_data_as_f64().unwrap();
1645 let values = array.iter().copied().collect::<Vec<_>>();
1646 assert_eq!(values[0], 10.0);
1647 assert!(values[1].is_nan());
1648 assert_eq!(values[2], 20.0);
1649 assert_eq!(values[3], 30.0);
1650 }
1651
1652 #[test]
1653 fn indexes_multiple_fields_in_one_grib2_message() {
1654 let message = assemble_grib2_message(&[
1655 build_identification(),
1656 build_grid(2, 2, 0),
1657 build_product(0, 0),
1658 build_simple_representation(4, 8),
1659 build_data(&pack_u8_values(&[1, 2, 3, 4])),
1660 build_product(0, 2),
1661 build_simple_representation(4, 8),
1662 build_data(&pack_u8_values(&[5, 6, 7, 8])),
1663 ]);
1664 let file = GribFile::from_bytes(message).unwrap();
1665 assert_eq!(file.message_count(), 2);
1666 assert_eq!(file.message(0).unwrap().parameter_name(), "TMP");
1667 assert_eq!(file.message(1).unwrap().parameter_name(), "POT");
1668 }
1669
1670 #[test]
1671 fn exposes_local_use_section_for_each_following_field() {
1672 let local_use = build_local_use(&[0xde, 0xad, 0xbe, 0xef]);
1673 let message = assemble_grib2_message(&[
1674 build_identification(),
1675 local_use.clone(),
1676 build_grid(2, 2, 0),
1677 build_product(0, 0),
1678 build_simple_representation(4, 8),
1679 build_data(&[1, 2, 3, 4]),
1680 build_product(0, 2),
1681 build_simple_representation(4, 8),
1682 build_data(&[5, 6, 7, 8]),
1683 ]);
1684
1685 let file = GribFile::from_bytes(message).unwrap();
1686 assert_eq!(
1687 file.message(0).unwrap().local_use_section(),
1688 Some(local_use.as_slice())
1689 );
1690 assert_eq!(
1691 file.message(1).unwrap().local_use_section(),
1692 Some(local_use.as_slice())
1693 );
1694 }
1695
1696 #[test]
1697 fn decodes_fields_that_reuse_a_previous_bitmap() {
1698 let message = assemble_grib2_message(&[
1699 build_identification(),
1700 build_grid(2, 2, 0),
1701 build_product(0, 0),
1702 build_simple_representation(2, 8),
1703 build_bitmap(&[true, false, true, false]),
1704 build_data(&[1, 2]),
1705 build_product(0, 2),
1706 build_simple_representation(2, 8),
1707 build_reused_bitmap(),
1708 build_data(&[3, 4]),
1709 ]);
1710
1711 let file = GribFile::from_bytes(message).unwrap();
1712 let first = file.message(0).unwrap().read_flat_data_as_f64().unwrap();
1713 let second = file.message(1).unwrap().read_flat_data_as_f64().unwrap();
1714 assert_eq!([first[0], first[2]], [1.0, 2.0]);
1715 assert!(first[1].is_nan() && first[3].is_nan());
1716 assert_eq!([second[0], second[2]], [3.0, 4.0]);
1717 assert!(second[1].is_nan() && second[3].is_nan());
1718 }
1719
1720 #[test]
1721 fn decodes_north_up_without_changing_default_reader_order() {
1722 let message = assemble_grib2_message(&[
1723 build_identification(),
1724 build_grid(2, 2, 0b1100_0000),
1725 build_product(0, 0),
1726 build_simple_representation(4, 8),
1727 build_data(&[1, 2, 3, 4]),
1728 ]);
1729 let file = GribFile::from_bytes(message).unwrap();
1730 let field = file.message(0).unwrap();
1731
1732 assert_eq!(
1733 field.read_flat_data_as_f64().unwrap(),
1734 vec![1.0, 2.0, 3.0, 4.0]
1735 );
1736 assert_eq!(
1737 field.read_flat_data_north_up_as_f64().unwrap(),
1738 vec![4.0, 3.0, 2.0, 1.0]
1739 );
1740 }
1741
1742 #[test]
1743 fn decodes_simple_grib1_message_to_ndarray() {
1744 let message = build_grib1_message(&[1, 2, 3, 4]);
1745 let file = GribFile::from_bytes(message).unwrap();
1746 let field = file.message(0).unwrap();
1747 assert_eq!(field.edition(), 1);
1748 assert_eq!(field.parameter_name(), "TMP");
1749 assert!(field.identification().is_none());
1750 assert!(field.grib1_product_definition().is_some());
1751 let array = field.read_data_as_f64().unwrap();
1752 assert_eq!(array.shape(), &[2, 2]);
1753 assert_eq!(
1754 array.iter().copied().collect::<Vec<_>>(),
1755 vec![1.0, 2.0, 3.0, 4.0]
1756 );
1757 }
1758
1759 #[test]
1760 fn decodes_standard_grib1_second_order_layouts() {
1761 for layout in [
1762 Grib1SecondOrderLayout::RowByRow,
1763 Grib1SecondOrderLayout::ConstantWidth,
1764 Grib1SecondOrderLayout::VariableWidth,
1765 ] {
1766 let opened = GribFile::from_bytes(build_grib1_second_order_message(layout)).unwrap();
1767 let field = opened.message(0).unwrap();
1768 assert_eq!(field.grid_shape(), (3, 2));
1769 assert_eq!(
1770 field.read_flat_data_as_f64().unwrap(),
1771 vec![10.0, 11.0, 12.0, 20.0, 22.0, 23.0],
1772 "layout {layout:?}"
1773 );
1774 let DataRepresentation::Grib1SecondOrderPacking(params) =
1775 &field.metadata().data_representation
1776 else {
1777 panic!("expected GRIB1 second-order metadata");
1778 };
1779 assert_eq!(params.layout(), layout);
1780 }
1781 }
1782
1783 #[test]
1784 fn decodes_grib1_row_by_row_groups_reduced_by_primary_bitmap() {
1785 let opened =
1786 GribFile::from_bytes(build_grib1_row_by_row_message_with_primary_bitmap()).unwrap();
1787 let values = opened.message(0).unwrap().read_flat_data_as_f64().unwrap();
1788 assert_eq!(values[0], 10.0);
1789 assert!(values[1].is_nan());
1790 assert_eq!(values[2], 12.0);
1791 assert!(values[3..].iter().all(|value| value.is_nan()));
1792 }
1793
1794 #[test]
1795 fn decodes_grib1_predefined_bitmap_when_supplied() {
1796 let message = build_grib1_message_with_predefined_bitmap(&[9, 7], 300);
1797 let bitmap = [0b1010_0000];
1798 let predefined = [PredefinedBitmap {
1799 center_id: 7,
1800 subcenter_id: Some(0),
1801 table_reference: 300,
1802 bitmap: &bitmap,
1803 }];
1804
1805 let file = GribFile::builder()
1806 .grib1_predefined_bitmaps(&predefined)
1807 .from_bytes(message)
1808 .unwrap();
1809 let decoded = file.message(0).unwrap().read_flat_data_as_f64().unwrap();
1810 assert_eq!(decoded[0], 9.0);
1811 assert!(decoded[1].is_nan());
1812 assert_eq!(decoded[2], 7.0);
1813 assert!(decoded[3].is_nan());
1814 }
1815
1816 #[test]
1817 fn message_iterator_is_exact_size_and_double_ended() {
1818 let mut bytes = build_grib1_message(&[1, 2, 3, 4]);
1819 bytes.extend(build_grib1_message(&[5, 6, 7, 8]));
1820 bytes.extend(build_grib1_message(&[9, 10, 11, 12]));
1821 let file = GribFile::from_bytes(bytes).unwrap();
1822
1823 let mut messages = file.messages();
1824 assert_eq!(messages.len(), 3);
1825 assert_eq!(messages.next_back().unwrap().index(), 2);
1826 assert_eq!(messages.len(), 2);
1827 assert_eq!(messages.next().unwrap().index(), 0);
1828 assert_eq!(messages.next().unwrap().index(), 1);
1829 assert!(messages.next().is_none());
1830 assert!(messages.next_back().is_none());
1831
1832 assert_eq!(file.messages().nth(2).unwrap().index(), 2);
1833 assert!(file.messages().nth(3).is_none());
1834 let indices = (&file).into_iter().rev().map(|message| message.index());
1835 assert_eq!(indices.collect::<Vec<_>>(), vec![2, 1, 0]);
1836 assert!(format!("{file:?}").contains("message_count: 3"));
1837 }
1838
1839 #[test]
1840 fn rejects_grib1_predefined_bitmap_without_supplied_table() {
1841 let message = build_grib1_message_with_predefined_bitmap(&[9, 7], 300);
1842 let err = match GribFile::from_bytes(message) {
1843 Ok(_) => panic!("expected unsupported predefined bitmap"),
1844 Err(err) => err,
1845 };
1846
1847 assert!(matches!(err, Error::UnsupportedBitmapIndicator(300)));
1848 }
1849}