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    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/// Builder for opening GRIB data with explicit scanning, allocation, and
122/// center-defined metadata policies.
123#[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    /// Controls whether malformed `GRIB` candidates abort scanning.
132    ///
133    /// Strict mode is enabled by default. Tolerant mode skips candidates with
134    /// invalid framing or unsupported edition numbers, while still reporting
135    /// semantic errors in well-framed GRIB1/GRIB2 messages.
136    pub fn strict(mut self, strict: bool) -> Self {
137        self.options.strict = strict;
138        self
139    }
140
141    /// Sets the maximum number of points decoded into a field buffer.
142    pub fn max_decoded_points(mut self, max: usize) -> Self {
143        self.options.max_decoded_points = Some(max);
144        self
145    }
146
147    /// Disables the decoded-field point limit.
148    pub fn without_decoded_point_limit(mut self) -> Self {
149        self.options.max_decoded_points = None;
150        self
151    }
152
153    /// Sets the maximum number of coordinates allocated by an axis helper.
154    pub fn max_axis_points(mut self, max: usize) -> Self {
155        self.options.max_axis_points = Some(max);
156        self
157    }
158
159    /// Disables the coordinate-axis point limit.
160    pub fn without_axis_point_limit(mut self) -> Self {
161        self.options.max_axis_points = None;
162        self
163    }
164
165    /// Disables both decoded-field and coordinate-axis limits.
166    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    /// Supplies GRIB2 local parameter definitions used during indexing.
173    pub fn local_parameters(mut self, entries: &'a [LocalParameterEntry<'a>]) -> Self {
174        self.local_parameters = entries;
175        self
176    }
177
178    /// Supplies center-defined GRIB1 predefined bitmaps.
179    pub fn grib1_predefined_bitmaps(mut self, bitmaps: &'a [PredefinedBitmap<'a>]) -> Self {
180        self.predefined_bitmaps = bitmaps;
181        self
182    }
183
184    /// Opens a file through a read-only memory mapping.
185    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    /// Opens an owned in-memory GRIB buffer without copying it.
199    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    /// Reads GRIB data from an arbitrary byte stream.
209    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/// Caller-supplied GRIB1 predefined bitmap.
230///
231/// GRIB1 predefined bitmap table references are center-defined, not globally
232/// standardized. `bitmap` is the raw bitmap payload: one bit per grid point in
233/// GRIB scan order, with `1` meaning present and `0` meaning missing.
234#[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/// A parsed GRIB field.
243#[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
263/// A GRIB file containing one or more logical fields.
264pub 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    /// Starts a configurable GRIB open operation.
310    pub fn builder<'a>() -> GribFileBuilder<'a> {
311        GribFileBuilder::default()
312    }
313
314    /// Open a GRIB file from disk using memory-mapped I/O.
315    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
316        Self::builder().open(path)
317    }
318
319    /// Open a GRIB file from an owned byte buffer.
320    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    /// Returns the number of logical fields in the file.
347    pub fn message_count(&self) -> usize {
348        self.messages.len()
349    }
350
351    /// Access a field by index.
352    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    /// Iterate over all fields.
373    pub fn messages(&self) -> MessageIter<'_> {
374        MessageIter {
375            file: self,
376            index: 0,
377            end: self.messages.len(),
378        }
379    }
380
381    /// Decode every field in the file.
382    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    /// Decode every field in the file as `f32`.
399    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
424/// A single logical GRIB field.
425pub 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    /// Returns the complete encoded GRIB2 Section 2, including its five-byte
485    /// length and section-number header, when present for this field.
486    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    /// Forecast offset in the units returned by [`Message::forecast_time_unit`].
517    ///
518    /// GRIB2 offsets are signed; GRIB1 offsets are widened to the same type.
519    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    /// Latitude axis in the rotated coordinate system for Template 3.1.
560    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    /// Longitude axis in the rotated coordinate system for Template 3.1.
569    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    /// Decodes into row-major reader order.
590    ///
591    /// Alternating scan rows are normalized, but the GRIB i/j scan directions
592    /// are preserved. Use [`Message::decode_into_north_up`] when deterministic
593    /// +i columns and -j rows are required.
594    /// Bitmap omissions and embedded complex-packing missing codes are written
595    /// as `NaN`; substitute metadata is not applied automatically.
596    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    /// Decodes into deterministic row-major orientation: +i columns and -j
602    /// rows (north-to-south for conventional geographic grids).
603    ///
604    /// Bitmap omissions and embedded complex-packing missing codes are written
605    /// as `NaN`; substitute metadata is not applied automatically.
606    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    /// Returns a flat `f64` field in row-major reader order.
667    ///
668    /// Alternating rows are normalized, while encoded i/j directions are
669    /// preserved. Use [`Message::read_flat_data_north_up_as_f64`] for +i
670    /// columns and -j rows.
671    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    /// Returns a flat `f32` field in row-major reader order.
679    ///
680    /// Alternating rows are normalized, while encoded i/j directions are
681    /// preserved. Use [`Message::read_flat_data_north_up_as_f32`] for +i
682    /// columns and -j rows.
683    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    /// Returns a flat `f64` field with +i columns and -j rows.
691    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    /// Returns a flat `f32` field with +i columns and -j rows.
699    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    /// Returns an `f64` ndarray in row-major reader order.
707    ///
708    /// Alternating rows are normalized, while encoded i/j directions are
709    /// preserved. Use [`Message::read_data_north_up_as_f64`] for deterministic
710    /// +i columns and -j rows.
711    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    /// Returns an `f32` ndarray in row-major reader order.
717    ///
718    /// Alternating rows are normalized, while encoded i/j directions are
719    /// preserved. Use [`Message::read_data_north_up_as_f32`] for deterministic
720    /// +i columns and -j rows.
721    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    /// Returns an `f64` ndarray with +i columns and -j rows.
727    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    /// Returns an `f32` ndarray with +i columns and -j rows.
733    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/// Iterator over fields in a GRIB file.
761#[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}