1pub mod data;
42pub mod error;
43pub mod grib1;
44pub mod grid;
45pub mod indicator;
46pub mod metadata;
47pub mod parameter;
48pub mod product;
49pub mod sections;
50
51pub use data::{
52 CcsdsBlockSize, CcsdsFlags, CcsdsPackingParams, ComplexPackingParams, DataRepresentation,
53 DecodeSample, Grib1SecondOrderLayout, Grib1SecondOrderPackingParams, IeeePackingParams,
54 IeeePrecision, Jpeg2000PackingParams, PngPackingParams, RunLengthPackingParams,
55 ScaledPackingParams, SpatialDifferencingParams,
56};
57pub use error::{Error, Result};
58pub use grib1::{BinaryDataSection, GridDescription, ProductDefinition as Grib1ProductDefinition};
59pub use grid::{
60 AlbersEqualAreaGrid, GridDefinition, LambertConformalGrid, LatLonGrid, MercatorGrid,
61 PolarStereographicGrid, ProjectedGridCore, RegularGaussianGrid, RotatedLatLonGrid,
62};
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#[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 pub fn strict(mut self, strict: bool) -> Self {
136 self.options.strict = strict;
137 self
138 }
139
140 pub fn max_decoded_points(mut self, max: usize) -> Self {
142 self.options.max_decoded_points = Some(max);
143 self
144 }
145
146 pub fn without_decoded_point_limit(mut self) -> Self {
148 self.options.max_decoded_points = None;
149 self
150 }
151
152 pub fn max_axis_points(mut self, max: usize) -> Self {
154 self.options.max_axis_points = Some(max);
155 self
156 }
157
158 pub fn without_axis_point_limit(mut self) -> Self {
160 self.options.max_axis_points = None;
161 self
162 }
163
164 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 pub fn local_parameters(mut self, entries: &'a [LocalParameterEntry<'a>]) -> Self {
173 self.local_parameters = entries;
174 self
175 }
176
177 pub fn grib1_predefined_bitmaps(mut self, bitmaps: &'a [PredefinedBitmap<'a>]) -> Self {
179 self.predefined_bitmaps = bitmaps;
180 self
181 }
182
183 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 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 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#[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#[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
262pub 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 pub fn builder<'a>() -> GribFileBuilder<'a> {
310 GribFileBuilder::default()
311 }
312
313 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
315 Self::builder().open(path)
316 }
317
318 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 pub fn message_count(&self) -> usize {
347 self.messages.len()
348 }
349
350 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 pub fn messages(&self) -> MessageIter<'_> {
373 MessageIter {
374 file: self,
375 index: 0,
376 }
377 }
378
379 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 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
414pub 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
741pub 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}