Skip to main content

grib_reader/
lib.rs

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