1use std::sync::Arc;
66
67use serde::{Deserialize, Serialize};
68
69mod complex;
70mod jpeg2000;
71#[cfg(test)]
72mod tests;
73
74pub use complex::{ComplexPacking, SpatialDifferencing};
75pub use jpeg2000::{Jpeg2000Packing, MAX_BITS as MAX_JPEG2000_BITS, MAX_SIDE as MAX_JPEG2000_SIDE};
76
77pub const MAX_POINTS: u64 = 1 << 24;
83
84#[non_exhaustive]
86#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
87pub enum Grib2Error {
88 #[error("not a GRIB message at byte {offset}: it begins with {head}")]
90 NotGrib {
91 offset: usize,
93 head: String,
95 },
96 #[error("the message at byte {offset} is GRIB edition {edition}; only edition 2 is read")]
98 Edition {
99 offset: usize,
101 edition: u8,
103 },
104 #[error("message {message} ends early: {what} needs {needed} bytes, {available} remain")]
106 Truncated {
107 message: usize,
109 what: &'static str,
111 needed: u64,
113 available: u64,
115 },
116 #[error("message {message} is malformed: {reason}")]
118 Malformed {
119 message: usize,
121 reason: String,
123 },
124 #[error("message {message}: {what} {value} is not read")]
126 Unsupported {
127 message: usize,
129 what: &'static str,
131 value: u64,
133 },
134 #[error("grid point {index} is outside a grid of {points}")]
136 PointOutside {
137 index: u64,
139 points: u64,
141 },
142}
143
144#[non_exhaustive]
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub struct ReferenceTime {
148 pub significance: u8,
150 pub year: u16,
152 pub month: u8,
154 pub day: u8,
156 pub hour: u8,
158 pub minute: u8,
160 pub second: u8,
162}
163
164#[non_exhaustive]
166#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
167pub enum Earth {
168 #[non_exhaustive]
171 Sphere {
172 radius_m: f64,
174 },
175 #[non_exhaustive]
177 Other {
178 code: u8,
180 },
181}
182
183#[non_exhaustive]
185#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
186pub enum Projection {
187 #[non_exhaustive]
189 LatLon {
190 first_lat_deg: f64,
192 first_lon_deg: f64,
194 di_deg: f64,
196 dj_deg: f64,
198 },
199 #[non_exhaustive]
201 LambertConformal {
202 first_lat_deg: f64,
204 first_lon_deg: f64,
206 tangent_lat_deg: f64,
208 orientation_lon_deg: f64,
210 dx_m: f64,
212 dy_m: f64,
214 radius_m: f64,
216 },
217}
218
219#[non_exhaustive]
225#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
226pub struct Grid {
227 pub ni: u32,
229 pub nj: u32,
231 pub earth: Earth,
233 pub south_to_north: bool,
235 pub winds_grid_relative: bool,
238 pub projection: Projection,
240}
241
242#[non_exhaustive]
244#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
245pub struct Surface {
246 pub kind: u8,
249 pub value: Option<f64>,
251}
252
253#[non_exhaustive]
255#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
256pub struct Product {
257 pub template: u16,
259 pub category: u8,
261 pub number: u8,
263 pub process: u8,
265 pub time_unit: u8,
268 pub forecast_time: i64,
270 pub surface: Surface,
272 pub second_surface: Surface,
274 pub statistics: Option<Statistics>,
278}
279
280#[non_exhaustive]
282#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
283pub struct Statistics {
284 pub end: ReferenceTime,
286 pub process: u8,
288 pub time_unit: u8,
290 pub length: i64,
292}
293
294impl Product {
295 #[must_use]
298 pub fn forecast_time_s(&self) -> Option<i64> {
299 let unit_s = match self.time_unit {
300 0 => 60,
301 1 => 3_600,
302 2 => 86_400,
303 10 => 3 * 3_600,
304 11 => 6 * 3_600,
305 12 => 12 * 3_600,
306 13 => 1,
307 _ => return None,
308 };
309 self.forecast_time.checked_mul(unit_s)
310 }
311}
312
313#[non_exhaustive]
315#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
316pub struct SimplePacking {
317 pub reference: f32,
319 pub binary_scale: i16,
321 pub decimal_scale: i16,
323 pub bits: u8,
325 pub count: u32,
327}
328
329impl SimplePacking {
330 #[must_use]
332 pub fn unpack(&self, x: u32) -> f64 {
333 unpack(
334 self.reference,
335 self.binary_scale,
336 self.decimal_scale,
337 f64::from(x),
338 )
339 }
340}
341
342fn unpack(reference: f32, binary_scale: i16, decimal_scale: i16, x: f64) -> f64 {
344 let scaled = f64::from(reference) + x * 2_f64.powi(binary_scale.into());
345 let d = i32::from(decimal_scale);
349 if d >= 0 {
350 scaled / 10_f64.powi(d)
351 } else {
352 scaled * 10_f64.powi(-d)
353 }
354}
355
356#[non_exhaustive]
358#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
359pub enum Packing {
360 Simple(SimplePacking),
362 Complex(ComplexPacking),
364 Jpeg2000(Jpeg2000Packing),
366}
367
368impl Packing {
369 #[must_use]
371 pub fn count(&self) -> u32 {
372 match self {
373 Self::Simple(p) => p.count,
374 Self::Complex(p) => p.count,
375 Self::Jpeg2000(p) => p.count,
376 }
377 }
378
379 #[must_use]
381 pub fn template(&self) -> u16 {
382 match self {
383 Self::Simple(_) => 0,
384 Self::Complex(p) if p.spatial_differencing.is_some() => 3,
385 Self::Complex(_) => 2,
386 Self::Jpeg2000(_) => 40,
387 }
388 }
389}
390
391#[non_exhaustive]
397#[derive(Debug, Clone, PartialEq)]
398pub struct Field<'a> {
399 pub message: usize,
401 pub discipline: u8,
403 pub center: u16,
405 pub reference_time: ReferenceTime,
407 pub grid: Grid,
409 pub product: Product,
411 pub packing: Packing,
413 bitmap: Option<Bitmap<'a>>,
416 data: &'a [u8],
419 layout: Option<complex::Layout>,
422}
423
424impl Field<'_> {
425 #[must_use]
427 pub fn points(&self) -> u64 {
428 u64::from(self.grid.ni) * u64::from(self.grid.nj)
429 }
430
431 pub fn value(&self, index: u64) -> Result<Option<f64>, Grib2Error> {
441 if let (Packing::Simple(p), None) = (&self.packing, &self.layout) {
442 let points = self.points();
443 if index >= points {
444 return Err(Grib2Error::PointOutside { index, points });
445 }
446 let k = match &self.bitmap {
447 Some(bitmap) if !bitmap.get(index) => return Ok(None),
448 Some(bitmap) => bitmap.ones_before(index),
449 None => index,
450 };
451 return Ok(Some(p.unpack(self.packed(p, k))));
452 }
453 Ok(self.values_at(&[index])?.first().copied().flatten())
454 }
455
456 pub fn values_at(&self, indices: &[u64]) -> Result<Vec<Option<f64>>, Grib2Error> {
462 let points = self.points();
463 let mut wanted = Vec::with_capacity(indices.len());
465 for &index in indices {
466 if index >= points {
467 return Err(Grib2Error::PointOutside { index, points });
468 }
469 wanted.push(match &self.bitmap {
470 Some(bitmap) => bitmap.get(index).then(|| bitmap.ones_before(index)),
471 None => Some(index),
472 });
473 }
474 match (&self.packing, &self.layout) {
475 (_, Some(layout)) => {
476 let mut order: Vec<(u64, usize)> = wanted
477 .iter()
478 .enumerate()
479 .filter_map(|(k, w)| w.map(|w| (w, k)))
480 .collect();
481 order.sort_unstable();
482 let end = order.last().map_or(0, |&(w, _)| w + 1);
483 let mut out = vec![None; indices.len()];
484 let mut next = 0;
485 complex::decode(layout, self.data, end, self.message, |position, value| {
486 while let Some(&(w, k)) = order.get(next) {
487 if w != position {
488 break;
489 }
490 out[k] = value;
491 next += 1;
492 }
493 })?;
494 Ok(out)
495 }
496 (Packing::Simple(p), None) => Ok(wanted
497 .into_iter()
498 .map(|w| w.map(|k| p.unpack(self.packed(p, k))))
499 .collect()),
500 (Packing::Jpeg2000(p), None) => {
501 let x = jpeg2000::decode(p, self.data, self.message)?;
502 Ok(wanted
503 .into_iter()
504 .map(|w| w.and_then(|k| usize::try_from(k).ok().and_then(|k| x.get(k))))
507 .map(|x| x.map(|&x| p.unpack(x)))
508 .collect())
509 }
510 (Packing::Complex(_), None) => Err(malformed(self.message, "no layout")),
511 }
512 }
513
514 pub fn values(&self) -> Result<Vec<Option<f64>>, Grib2Error> {
522 let points = self.points();
523 let mut out = Vec::with_capacity(usize::try_from(points).unwrap_or(0));
525 let marked = |index: u64| self.bitmap.as_ref().is_none_or(|bitmap| bitmap.get(index));
526 match (&self.packing, &self.layout) {
527 (_, Some(layout)) => {
528 let mut index = 0;
530 complex::decode(layout, self.data, u64::MAX, self.message, |_, value| {
531 while index < points && !marked(index) {
532 out.push(None);
533 index += 1;
534 }
535 out.push(value);
536 index += 1;
537 })?;
538 out.resize(usize::try_from(points).unwrap_or(0), None);
539 }
540 (Packing::Simple(p), None) => {
541 let mut k = 0;
542 for index in 0..points {
543 out.push(marked(index).then(|| {
544 let x = self.packed(p, k);
545 k += 1;
546 p.unpack(x)
547 }));
548 }
549 }
550 (Packing::Jpeg2000(p), None) => {
551 let mut x = jpeg2000::decode(p, self.data, self.message)?.into_iter();
552 for index in 0..points {
553 out.push(if marked(index) {
555 x.next().map(|x| p.unpack(x))
556 } else {
557 None
558 });
559 }
560 }
561 (Packing::Complex(_), None) => return Err(malformed(self.message, "no layout")),
562 }
563 Ok(out)
564 }
565
566 fn packed(&self, p: &SimplePacking, k: u64) -> u32 {
569 u32::try_from(complex::bits(self.data, k * u64::from(p.bits), p.bits)).unwrap_or(u32::MAX)
571 }
572}
573
574#[derive(Debug, Clone, PartialEq)]
578struct Bitmap<'a> {
579 bits: &'a [u8],
580 ones: Arc<[u64]>,
583}
584
585impl<'a> Bitmap<'a> {
586 fn new(bits: &'a [u8]) -> Self {
587 let ones = std::iter::once(0)
588 .chain(bits.chunks(64).scan(0, |total, chunk| {
589 *total += count_ones(chunk);
590 Some(*total)
591 }))
592 .collect();
593 Self { bits, ones }
594 }
595
596 fn get(&self, index: u64) -> bool {
598 let byte = usize::try_from(index / 8)
599 .ok()
600 .and_then(|i| self.bits.get(i))
601 .copied()
602 .unwrap_or(0);
603 byte & (0x80 >> (index % 8)) != 0
604 }
605
606 fn ones_before(&self, index: u64) -> u64 {
608 let byte = usize::try_from(index / 8)
609 .unwrap_or(usize::MAX)
610 .min(self.bits.len());
611 let block = byte / 64;
612 let mut n =
613 self.ones.get(block).copied().unwrap_or(0) + count_ones(&self.bits[block * 64..byte]);
614 let rest = index % 8;
615 if rest > 0 {
616 let partial = self.bits.get(byte).copied().unwrap_or(0) & !(0xFF_u8 >> rest);
617 n += u64::from(partial.count_ones());
618 }
619 n
620 }
621}
622
623fn count_ones(bytes: &[u8]) -> u64 {
624 bytes.iter().map(|b| u64::from(b.count_ones())).sum()
625}
626
627impl Grid {
628 #[must_use]
630 pub fn points(&self) -> u64 {
631 u64::from(self.ni) * u64::from(self.nj)
632 }
633
634 #[must_use]
637 pub fn point_deg(&self, index: u64) -> Option<(f64, f64)> {
638 if index >= self.points() {
639 return None;
640 }
641 let i = (index % u64::from(self.ni)) as f64;
642 let j = (index / u64::from(self.ni)) as f64;
643 let j_sign = if self.south_to_north { 1.0 } else { -1.0 };
644 match self.projection {
645 Projection::LatLon {
646 first_lat_deg,
647 first_lon_deg,
648 di_deg,
649 dj_deg,
650 } => Some((
651 first_lat_deg + j_sign * j * dj_deg,
652 (first_lon_deg + i * di_deg).rem_euclid(360.0),
653 )),
654 Projection::LambertConformal {
655 first_lat_deg,
656 first_lon_deg,
657 dx_m,
658 dy_m,
659 ..
660 } => {
661 let lambert = Lambert::new(&self.projection)?;
662 let (x0, y0) = lambert.forward(first_lat_deg, first_lon_deg);
663 let (lat, lon) = lambert.inverse(x0 + i * dx_m, y0 + j_sign * j * dy_m);
664 Some((lat, lon.rem_euclid(360.0)))
665 }
666 }
667 }
668
669 #[must_use]
674 pub fn circles_the_earth(&self) -> bool {
675 match self.projection {
676 Projection::LatLon { di_deg, .. } => {
677 let ni = f64::from(self.ni);
678 (ni * di_deg - 360.0).abs() <= ni * 1e-6
679 }
680 Projection::LambertConformal { .. } => false,
681 }
682 }
683
684 #[must_use]
687 pub fn index_at(&self, latitude_deg: f64, longitude_deg: f64) -> (f64, f64) {
688 let j_sign = if self.south_to_north { 1.0 } else { -1.0 };
689 match self.projection {
690 Projection::LatLon {
691 first_lat_deg,
692 first_lon_deg,
693 di_deg,
694 dj_deg,
695 } => (
696 (longitude_deg - first_lon_deg).rem_euclid(360.0) / di_deg,
697 j_sign * (latitude_deg - first_lat_deg) / dj_deg,
698 ),
699 Projection::LambertConformal {
700 first_lat_deg,
701 first_lon_deg,
702 dx_m,
703 dy_m,
704 ..
705 } => match Lambert::new(&self.projection) {
706 Some(lambert) => {
707 let (x0, y0) = lambert.forward(first_lat_deg, first_lon_deg);
708 let (x, y) = lambert.forward(latitude_deg, longitude_deg);
709 ((x - x0) / dx_m, j_sign * (y - y0) / dy_m)
710 }
711 None => (f64::NAN, f64::NAN),
712 },
713 }
714 }
715
716 #[must_use]
721 pub fn north_to_grid_y_rad(&self, longitude_deg: f64) -> f64 {
722 match Lambert::new(&self.projection) {
723 Some(lambert) => lambert.theta(longitude_deg),
724 None => 0.0,
725 }
726 }
727
728 #[must_use]
734 pub fn earth_relative_wind(&self, u_m_s: f64, v_m_s: f64, longitude_deg: f64) -> (f64, f64) {
735 if !self.winds_grid_relative {
736 return (u_m_s, v_m_s);
737 }
738 let theta = self.north_to_grid_y_rad(longitude_deg);
739 let (sin, cos) = theta.sin_cos();
740 (u_m_s * cos + v_m_s * sin, -u_m_s * sin + v_m_s * cos)
741 }
742}
743
744struct Lambert {
746 n: f64,
748 rf: f64,
750 lon0_rad: f64,
752}
753
754impl Lambert {
755 fn new(projection: &Projection) -> Option<Self> {
756 let Projection::LambertConformal {
757 tangent_lat_deg,
758 orientation_lon_deg,
759 radius_m,
760 ..
761 } = *projection
762 else {
763 return None;
764 };
765 let phi1 = tangent_lat_deg.to_radians();
766 let n = phi1.sin();
767 let f = phi1.cos() * quarter_tan(phi1).powf(n) / n;
768 Some(Self {
769 n,
770 rf: radius_m * f,
771 lon0_rad: orientation_lon_deg.to_radians(),
772 })
773 }
774
775 fn theta(&self, longitude_deg: f64) -> f64 {
777 let d = (longitude_deg.to_radians() - self.lon0_rad + std::f64::consts::PI)
778 .rem_euclid(std::f64::consts::TAU)
779 - std::f64::consts::PI;
780 self.n * d
781 }
782
783 fn forward(&self, latitude_deg: f64, longitude_deg: f64) -> (f64, f64) {
786 let rho = self.rf / quarter_tan(latitude_deg.to_radians()).powf(self.n);
787 let theta = self.theta(longitude_deg);
788 (rho * theta.sin(), -rho * theta.cos())
789 }
790
791 fn inverse(&self, x: f64, y: f64) -> (f64, f64) {
794 let rho = x.hypot(y);
795 let theta = x.atan2(-y);
796 let phi = 2.0 * (self.rf / rho).powf(1.0 / self.n).atan() - std::f64::consts::FRAC_PI_2;
797 (
798 phi.to_degrees(),
799 (theta / self.n + self.lon0_rad).to_degrees(),
800 )
801 }
802}
803
804fn quarter_tan(phi: f64) -> f64 {
806 (std::f64::consts::FRAC_PI_4 + phi / 2.0).tan()
807}
808
809pub fn parse(bytes: &[u8]) -> Result<Vec<Field<'_>>, Grib2Error> {
819 let mut fields = Vec::new();
820 let mut offset = 0;
821 let mut message = 0;
822 while offset < bytes.len() {
823 let rest = &bytes[offset..];
824 if rest.len() < 16 || &rest[..4] != b"GRIB" {
825 return Err(Grib2Error::NotGrib {
826 offset,
827 head: rest.iter().take(8).map(|b| format!("{b:02x}")).collect(),
828 });
829 }
830 if rest[7] != 2 {
831 return Err(Grib2Error::Edition {
832 offset,
833 edition: rest[7],
834 });
835 }
836 let length = u64::from_be_bytes([
837 rest[8], rest[9], rest[10], rest[11], rest[12], rest[13], rest[14], rest[15],
838 ]);
839 let available = rest.len() as u64;
840 if length > available {
841 return Err(Grib2Error::Truncated {
842 message,
843 what: "the message",
844 needed: length,
845 available,
846 });
847 }
848 let length = usize::try_from(length).unwrap_or(usize::MAX);
850 if length < 20 || &rest[length - 4..length] != b"7777" {
851 return Err(malformed(message, "it does not end with 7777"));
852 }
853 read_message(&rest[..length], message, rest[6], &mut fields)?;
854 offset += length;
855 message += 1;
856 }
857 Ok(fields)
858}
859
860fn malformed(message: usize, reason: impl Into<String>) -> Grib2Error {
861 Grib2Error::Malformed {
862 message,
863 reason: reason.into(),
864 }
865}
866
867fn read_message<'a>(
869 bytes: &'a [u8],
870 message: usize,
871 discipline: u8,
872 fields: &mut Vec<Field<'a>>,
873) -> Result<(), Grib2Error> {
874 let end = bytes.len() - 4;
875 let mut offset = 16;
876 let mut identification: Option<(u16, ReferenceTime)> = None;
877 let mut grid: Option<Grid> = None;
878 let mut product: Option<Product> = None;
879 let mut packing: Option<Packing> = None;
880 let mut bitmap: Option<Option<Bitmap<'a>>> = None;
882 let mut last_bitmap: Option<Bitmap<'a>> = None;
883 let before = fields.len();
884 while offset < end {
885 if end - offset < 5 {
886 return Err(truncated(message, "a section's header", 5, end - offset));
887 }
888 let length = be_u32(&bytes[offset..offset + 4]) as usize;
889 let number = bytes[offset + 4];
890 if length < 5 {
891 return Err(malformed(
892 message,
893 format!("section {number} is {length} bytes long"),
894 ));
895 }
896 if length > end - offset {
897 return Err(truncated(message, "a section", length, end - offset));
898 }
899 let s = &bytes[offset..offset + length];
900 match number {
901 1 => identification = Some(read_identification(s, message)?),
902 2 => {}
903 3 => grid = Some(read_grid(s, message)?),
904 4 => product = Some(read_product(s, message)?),
905 5 => packing = Some(read_packing(s, message)?),
906 6 => {
907 let indicator = at(s, 5, message)?;
908 bitmap = Some(match indicator {
909 0 => {
910 last_bitmap = Some(Bitmap::new(&s[6..]));
911 last_bitmap.clone()
912 }
913 254 => Some(last_bitmap.clone().ok_or_else(|| {
914 malformed(message, "section 6 reuses a bitmap none gave")
915 })?),
916 255 => None,
917 other => {
918 return Err(Grib2Error::Unsupported {
919 message,
920 what: "bitmap indicator",
921 value: other.into(),
922 });
923 }
924 });
925 }
926 7 => {
927 let (
928 Some((center, reference_time)),
929 Some(grid),
930 Some(product),
931 Some(packing),
932 Some(bitmap),
933 ) = (
934 identification,
935 grid,
936 product.take(),
937 packing.take(),
938 bitmap.take(),
939 )
940 else {
941 return Err(malformed(
942 message,
943 "section 7 comes before sections 1 and 3 to 6 are all given",
944 ));
945 };
946 let mut field = Field {
947 message,
948 discipline,
949 center,
950 reference_time,
951 grid,
952 product,
953 packing,
954 bitmap,
955 data: &s[5..],
956 layout: None,
957 };
958 check_field(&mut field)?;
959 fields.push(field);
960 }
961 other => {
962 return Err(malformed(message, format!("it holds a section {other}")));
963 }
964 }
965 offset += length;
966 }
967 if product.is_some() || packing.is_some() || bitmap.is_some() {
968 return Err(malformed(message, "it ends before a section 7"));
969 }
970 if fields.len() == before {
971 return Err(malformed(message, "it holds no field"));
972 }
973 Ok(())
974}
975
976fn check_field(field: &mut Field<'_>) -> Result<(), Grib2Error> {
979 let message = field.message;
980 let points = field.points();
981 let count = u64::from(field.packing.count());
982 match &field.bitmap {
983 Some(bitmap) => {
984 let have = bitmap.bits.len() as u64 * 8;
985 if have < points {
986 return Err(truncated(
987 message,
988 "the bitmap",
989 points.div_ceil(8),
990 bitmap.bits.len(),
991 ));
992 }
993 let marked = bitmap.ones_before(points);
994 if marked != count {
995 return Err(malformed(
996 message,
997 format!("the bitmap marks {marked} points but section 5 packs {count} values"),
998 ));
999 }
1000 }
1001 None if count != points => {
1002 return Err(malformed(
1003 message,
1004 format!("a grid of {points} points without a bitmap packs {count} values"),
1005 ));
1006 }
1007 None => {}
1008 }
1009 #[allow(
1013 clippy::cast_precision_loss,
1014 reason = "the ends of i64 only need to be near, to bound the scale"
1015 )]
1016 let (low, high, e, d) = match &field.packing {
1017 Packing::Simple(p) => {
1018 let largest = u32::try_from((1_u64 << p.bits) - 1).unwrap_or(u32::MAX);
1019 (
1020 p.unpack(0),
1021 p.unpack(largest),
1022 p.binary_scale,
1023 p.decimal_scale,
1024 )
1025 }
1026 Packing::Complex(p) => (
1027 p.unpack(i64::MIN),
1028 p.unpack(i64::MAX),
1029 p.binary_scale,
1030 p.decimal_scale,
1031 ),
1032 Packing::Jpeg2000(p) => (
1033 p.unpack(0),
1034 p.unpack((1_u32 << p.bits) - 1),
1035 p.binary_scale,
1036 p.decimal_scale,
1037 ),
1038 };
1039 if !(low.is_finite() && high.is_finite()) {
1040 return Err(malformed(
1041 message,
1042 format!("the scale factors (binary {e}, decimal {d}) make values that are not finite"),
1043 ));
1044 }
1045 match &field.packing {
1046 Packing::Simple(p) => {
1047 let bits = count * u64::from(p.bits);
1048 let have = field.data.len() as u64 * 8;
1049 if have < bits {
1050 return Err(truncated(
1051 message,
1052 "the packed values",
1053 bits.div_ceil(8),
1054 field.data.len(),
1055 ));
1056 }
1057 }
1058 Packing::Complex(p) => field.layout = Some(complex::layout(p, field.data, message)?),
1059 Packing::Jpeg2000(p) => jpeg2000::check(p, field.data, message)?,
1060 }
1061 Ok(())
1062}
1063
1064fn truncated(
1065 message: usize,
1066 what: &'static str,
1067 needed: impl TryInto<u64>,
1068 available: impl TryInto<u64>,
1069) -> Grib2Error {
1070 Grib2Error::Truncated {
1071 message,
1072 what,
1073 needed: needed.try_into().unwrap_or(u64::MAX),
1074 available: available.try_into().unwrap_or(u64::MAX),
1075 }
1076}
1077
1078fn at(s: &[u8], i: usize, message: usize) -> Result<u8, Grib2Error> {
1080 s.get(i)
1081 .copied()
1082 .ok_or_else(|| truncated(message, "a section", i + 1, s.len()))
1083}
1084
1085fn need<'a>(
1087 s: &'a [u8],
1088 len: usize,
1089 what: &'static str,
1090 message: usize,
1091) -> Result<&'a [u8], Grib2Error> {
1092 if s.len() < len {
1093 return Err(truncated(message, what, len, s.len()));
1094 }
1095 Ok(s)
1096}
1097
1098fn be_u16(b: &[u8]) -> u16 {
1099 u16::from_be_bytes([b[0], b[1]])
1100}
1101
1102fn be_u32(b: &[u8]) -> u32 {
1103 u32::from_be_bytes([b[0], b[1], b[2], b[3]])
1104}
1105
1106fn on_earth(latitude_deg: f64, message: usize) -> Result<f64, Grib2Error> {
1108 if latitude_deg.abs() <= 90.0 {
1109 Ok(latitude_deg)
1110 } else {
1111 Err(malformed(message, format!("a latitude of {latitude_deg}°")))
1112 }
1113}
1114
1115fn be_i32(b: &[u8]) -> i64 {
1117 let raw = be_u32(b);
1118 let magnitude = i64::from(raw & 0x7FFF_FFFF);
1119 if raw & 0x8000_0000 != 0 {
1120 -magnitude
1121 } else {
1122 magnitude
1123 }
1124}
1125
1126fn be_i16(b: &[u8]) -> i16 {
1128 let raw = be_u16(b);
1129 let magnitude = i16::try_from(raw & 0x7FFF).unwrap_or(i16::MAX);
1131 if raw & 0x8000 != 0 {
1132 -magnitude
1133 } else {
1134 magnitude
1135 }
1136}
1137
1138fn be_i8(b: u8) -> i32 {
1140 let magnitude = i32::from(b & 0x7F);
1141 if b & 0x80 != 0 { -magnitude } else { magnitude }
1142}
1143
1144fn read_identification(s: &[u8], message: usize) -> Result<(u16, ReferenceTime), Grib2Error> {
1146 let s = need(s, 19, "section 1", message)?;
1147 Ok((
1148 be_u16(&s[5..7]),
1149 ReferenceTime {
1150 significance: s[11],
1151 year: be_u16(&s[12..14]),
1152 month: s[14],
1153 day: s[15],
1154 hour: s[16],
1155 minute: s[17],
1156 second: s[18],
1157 },
1158 ))
1159}
1160
1161fn read_earth(t: &[u8]) -> Earth {
1163 match t[0] {
1164 0 => Earth::Sphere {
1165 radius_m: 6_367_470.0,
1166 },
1167 1 => Earth::Sphere {
1168 radius_m: scaled(be_u32(&t[2..6]), be_i8(t[1])),
1169 },
1170 6 => Earth::Sphere {
1171 radius_m: 6_371_229.0,
1172 },
1173 8 => Earth::Sphere {
1174 radius_m: 6_371_200.0,
1175 },
1176 code => Earth::Other { code },
1177 }
1178}
1179
1180fn read_grid(s: &[u8], message: usize) -> Result<Grid, Grib2Error> {
1182 let s = need(s, 14, "section 3", message)?;
1183 let unsupported = |what, value: u64| Grib2Error::Unsupported {
1184 message,
1185 what,
1186 value,
1187 };
1188 if s[5] != 0 {
1189 return Err(unsupported("source of grid definition", s[5].into()));
1190 }
1191 if s[10] != 0 {
1192 return Err(unsupported("list of points per row, bytes", s[10].into()));
1193 }
1194 let declared = u64::from(be_u32(&s[6..10]));
1195 let template = be_u16(&s[12..14]);
1196 let (ni, nj, flags, scanning, projection, earth) = match template {
1197 0 => {
1198 let s = need(s, 72, "grid template 3.0", message)?;
1199 let earth = read_earth(&s[14..30]);
1200 let basic = be_u32(&s[38..42]);
1201 let subdivisions = be_u32(&s[42..46]);
1202 let basic = if basic == 0 || basic == u32::MAX {
1207 1
1208 } else {
1209 basic
1210 };
1211 let subdivisions = if subdivisions == u32::MAX || subdivisions == 0 {
1212 1_000_000
1213 } else {
1214 subdivisions
1215 };
1216 let degrees = |units: f64| units * f64::from(basic) / f64::from(subdivisions);
1217 let di = be_u32(&s[63..67]);
1218 let dj = be_u32(&s[67..71]);
1219 if di == u32::MAX || dj == u32::MAX || di == 0 || dj == 0 {
1220 return Err(malformed(message, "the grid's increments are missing or 0"));
1221 }
1222 let nj = be_u32(&s[34..38]);
1223 let first_lat_deg = degrees(be_i32(&s[46..50]) as f64);
1224 let (di_deg, dj_deg) = (degrees(f64::from(di)), degrees(f64::from(dj)));
1225 let span_deg = f64::from(nj.saturating_sub(1)) * dj_deg;
1228 let last_lat_deg = if s[71] & 0x40 != 0 {
1229 first_lat_deg + span_deg
1230 } else {
1231 first_lat_deg - span_deg
1232 };
1233 let on_earth = |lat: f64| lat.abs() <= 90.0 + 1e-6;
1234 if !on_earth(first_lat_deg) || !on_earth(last_lat_deg) || di_deg > 360.0 {
1235 return Err(malformed(
1236 message,
1237 format!(
1238 "the grid's rows from {first_lat_deg}° to {last_lat_deg}° or its \
1239 {di_deg}° steps are off the Earth"
1240 ),
1241 ));
1242 }
1243 let projection = Projection::LatLon {
1244 first_lat_deg,
1245 first_lon_deg: degrees(be_i32(&s[50..54]) as f64).rem_euclid(360.0),
1246 di_deg,
1247 dj_deg,
1248 };
1249 (
1250 be_u32(&s[30..34]),
1251 be_u32(&s[34..38]),
1252 s[54],
1253 s[71],
1254 projection,
1255 earth,
1256 )
1257 }
1258 30 => {
1259 let s = need(s, 81, "grid template 3.30", message)?;
1260 let earth = read_earth(&s[14..30]);
1261 let Earth::Sphere { radius_m } = earth else {
1262 return Err(unsupported(
1263 "Lambert grid on the figure of the Earth",
1264 s[14].into(),
1265 ));
1266 };
1267 if !(6.0e6..=7.0e6).contains(&radius_m) {
1269 return Err(malformed(
1270 message,
1271 format!("the Earth's radius is {radius_m} m"),
1272 ));
1273 }
1274 let center = s[63];
1275 if center & 0xC0 != 0 {
1276 return Err(unsupported("Lambert projection center flag", center.into()));
1277 }
1278 let micro = |b: &[u8]| be_i32(b) as f64 / 1e6;
1279 let lad = micro(&s[47..51]);
1280 let latin1 = micro(&s[65..69]);
1281 let latin2 = micro(&s[69..73]);
1282 if latin1 != latin2 || lad != latin1 || !(latin1 > 0.0 && latin1 < 90.0) {
1285 return Err(malformed(
1286 message,
1287 format!(
1288 "the Lambert grid's Latin1 {latin1}°, Latin2 {latin2}° and LaD {lad}° are \
1289 not one latitude between 0° and 90°: only a northern tangent cone is read"
1290 ),
1291 ));
1292 }
1293 let dx = be_u32(&s[55..59]);
1294 let dy = be_u32(&s[59..63]);
1295 if dx == 0 || dy == 0 || dx == u32::MAX || dy == u32::MAX {
1296 return Err(malformed(message, "the grid's lengths are missing or 0"));
1297 }
1298 let projection = Projection::LambertConformal {
1299 first_lat_deg: on_earth(micro(&s[38..42]), message)?,
1300 first_lon_deg: micro(&s[42..46]).rem_euclid(360.0),
1301 tangent_lat_deg: latin1,
1302 orientation_lon_deg: micro(&s[51..55]).rem_euclid(360.0),
1303 dx_m: f64::from(dx) * 1e-3,
1304 dy_m: f64::from(dy) * 1e-3,
1305 radius_m,
1306 };
1307 (
1308 be_u32(&s[30..34]),
1309 be_u32(&s[34..38]),
1310 s[46],
1311 s[64],
1312 projection,
1313 earth,
1314 )
1315 }
1316 other => return Err(unsupported("grid definition template", other.into())),
1317 };
1318 if scanning & !0x40 != 0 {
1321 return Err(unsupported("scanning mode", scanning.into()));
1322 }
1323 let points = u64::from(ni) * u64::from(nj);
1324 if points == 0 || points != declared {
1325 return Err(malformed(
1326 message,
1327 format!("the grid is {ni} by {nj} but declares {declared} points"),
1328 ));
1329 }
1330 if points > MAX_POINTS {
1331 return Err(unsupported("grid of this many points", points));
1332 }
1333 Ok(Grid {
1334 ni,
1335 nj,
1336 earth,
1337 south_to_north: scanning & 0x40 != 0,
1338 winds_grid_relative: flags & 0x08 != 0,
1339 projection,
1340 })
1341}
1342
1343fn scaled(value: u32, scale: i32) -> f64 {
1345 if scale >= 0 {
1346 f64::from(value) / 10_f64.powi(scale)
1347 } else {
1348 f64::from(value) * 10_f64.powi(-scale)
1349 }
1350}
1351
1352fn read_surface(s: &[u8]) -> Surface {
1354 let kind = s[0];
1355 let scale = s[1];
1356 let raw = be_u32(&s[2..6]);
1357 let value = (scale != 0xFF && raw != u32::MAX).then(|| scaled(raw, be_i8(scale)));
1358 Surface { kind, value }
1359}
1360
1361fn read_product(s: &[u8], message: usize) -> Result<Product, Grib2Error> {
1363 let s = need(s, 9, "section 4", message)?;
1364 let template = be_u16(&s[7..9]);
1365 let statistics = match template {
1366 0 => None,
1367 8 => {
1368 let s = need(s, 58, "product template 4.8", message)?;
1371 if s[41] != 1 {
1372 return Err(Grib2Error::Unsupported {
1373 message,
1374 what: "number of time ranges",
1375 value: s[41].into(),
1376 });
1377 }
1378 Some(Statistics {
1379 end: ReferenceTime {
1380 significance: 2,
1381 year: be_u16(&s[34..36]),
1382 month: s[36],
1383 day: s[37],
1384 hour: s[38],
1385 minute: s[39],
1386 second: s[40],
1387 },
1388 process: s[46],
1389 time_unit: s[48],
1390 length: be_u32(&s[49..53]).into(),
1391 })
1392 }
1393 _ => {
1394 return Err(Grib2Error::Unsupported {
1395 message,
1396 what: "product definition template",
1397 value: template.into(),
1398 });
1399 }
1400 };
1401 let s = need(s, 34, "product template 4.0", message)?;
1402 Ok(Product {
1403 template,
1404 category: s[9],
1405 number: s[10],
1406 process: s[11],
1407 time_unit: s[17],
1408 forecast_time: be_i32(&s[18..22]),
1409 surface: read_surface(&s[22..28]),
1410 second_surface: read_surface(&s[28..34]),
1411 statistics,
1412 })
1413}
1414
1415fn read_packing(s: &[u8], message: usize) -> Result<Packing, Grib2Error> {
1417 let s = need(s, 11, "section 5", message)?;
1418 let template = be_u16(&s[9..11]);
1419 match template {
1420 0 => {}
1421 2 | 3 => return complex::read(s, template, message).map(Packing::Complex),
1422 40 => return jpeg2000::read(s, message).map(Packing::Jpeg2000),
1423 _ => {
1424 return Err(Grib2Error::Unsupported {
1425 message,
1426 what: "data representation template",
1427 value: template.into(),
1428 });
1429 }
1430 }
1431 let s = need(s, 21, "data template 5.0", message)?;
1432 let bits = s[19];
1433 if bits > 32 {
1434 return Err(Grib2Error::Unsupported {
1435 message,
1436 what: "bits per value",
1437 value: bits.into(),
1438 });
1439 }
1440 if s[20] > 1 {
1442 return Err(Grib2Error::Unsupported {
1443 message,
1444 what: "type of original field values",
1445 value: s[20].into(),
1446 });
1447 }
1448 let reference = f32::from_bits(be_u32(&s[11..15]));
1449 if !reference.is_finite() {
1450 return Err(malformed(message, "the reference value is not finite"));
1451 }
1452 Ok(Packing::Simple(SimplePacking {
1453 reference,
1454 binary_scale: be_i16(&s[15..17]),
1455 decimal_scale: be_i16(&s[17..19]),
1456 bits,
1457 count: be_u32(&s[5..9]),
1458 }))
1459}