grib/
helpers.rs

1pub(crate) trait GribInt<I> {
2    fn as_grib_int(&self) -> I;
3}
4
5macro_rules! add_impl_for_ints {
6    ($(($ty_src:ty, $ty_dst:ty),)*) => ($(
7        impl GribInt<$ty_dst> for $ty_src {
8            fn as_grib_int(&self) -> $ty_dst {
9                if self.leading_zeros() == 0 {
10                    let abs = (self << 1 >> 1) as $ty_dst;
11                    -abs
12                } else {
13                    *self as $ty_dst
14                }
15            }
16        }
17    )*);
18}
19
20add_impl_for_ints! {
21    (u8, i8),
22    (u16, i16),
23    (u32, i32),
24    (u64, i64),
25}
26
27macro_rules! read_as {
28    ($ty:ty, $buf:ident, $start:expr) => {{
29        let end = $start + std::mem::size_of::<$ty>();
30        <$ty>::from_be_bytes($buf[$start..end].try_into().unwrap())
31    }};
32}
33pub(crate) use read_as;
34
35pub(crate) fn grib_int_from_bytes(bytes: &[u8]) -> i32 {
36    let len = bytes.len();
37    // Although there is logic that can be used to generalize, not so many patterns
38    // exist that generalization is necessary.
39    match len {
40        1 => i32::from(read_as!(u8, bytes, 0).as_grib_int()),
41        2 => i32::from(read_as!(u16, bytes, 0).as_grib_int()),
42        3 => {
43            let first = read_as!(u8, bytes, 0);
44            let positive = first.leading_zeros() != 0;
45            let rest = i32::from(read_as!(u16, bytes, 1));
46            let abs = i32::from(first << 1 >> 1) * 0x10000 + rest;
47            if positive {
48                abs
49            } else {
50                -abs
51            }
52        }
53        4 => read_as!(u32, bytes, 0).as_grib_int(),
54        _ => unimplemented!(),
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn into_grib_i8() {
64        let input: Vec<u8> = vec![0b01000000, 0b00000001, 0b10000001, 0b11000000];
65        let output: Vec<i8> = vec![64, 1, -1, -64];
66
67        let mut actual = Vec::new();
68        let mut pos = 0;
69        while pos < input.len() {
70            let val = u8::from_be_bytes(input[pos..pos + 1].try_into().unwrap());
71            pos += 1;
72            let val = val.as_grib_int();
73            actual.push(val);
74        }
75
76        assert_eq!(actual, output);
77    }
78
79    #[test]
80    fn into_grib_i16() {
81        let input: Vec<u8> = vec![
82            0b00000000, 0b01000000, 0b00000000, 0b00000001, 0b10000000, 0b00000001, 0b10000000,
83            0b01000000,
84        ];
85        let output: Vec<i16> = vec![64, 1, -1, -64];
86
87        let mut actual = Vec::new();
88        let mut pos = 0;
89        while pos < input.len() {
90            let val = u16::from_be_bytes(input[pos..pos + 2].try_into().unwrap());
91            pos += 2;
92            let val = val.as_grib_int();
93            actual.push(val);
94        }
95
96        assert_eq!(actual, output);
97    }
98
99    macro_rules! test_conversion_from_bytes_to_grib_int {
100        ($(($name:ident, $input:expr, $expected:expr),)*) => ($(
101            #[test]
102            fn $name() {
103                let bytes = $input;
104                let actual = grib_int_from_bytes(&bytes);
105                let expected = $expected;
106                assert_eq!(actual, expected)
107            }
108        )*);
109    }
110
111    test_conversion_from_bytes_to_grib_int! {
112        (
113            conversion_from_bytes_to_grib_int_for_1_byte_positive,
114            vec![0b01010101],
115            0b01010101
116        ),
117        (
118            conversion_from_bytes_to_grib_int_for_1_byte_negative,
119            vec![0b11010101],
120            -0b01010101
121        ),
122        (
123            conversion_from_bytes_to_grib_int_for_2_bytes_positive,
124            vec![0b01010101, 0b10101010],
125            0b0101_0101_1010_1010
126        ),
127        (
128            conversion_from_bytes_to_grib_int_for_2_bytes_negative,
129            vec![0b11010101, 0b10101010],
130            -0b0101_0101_1010_1010
131        ),
132        (
133            conversion_from_bytes_to_grib_int_for_3_bytes_positive,
134            vec![0b01010101, 0b10101010, 0b10101010],
135            0b0101_0101_1010_1010_1010_1010
136        ),
137        (
138            conversion_from_bytes_to_grib_int_for_3_bytes_negative,
139            vec![0b11010101, 0b10101010, 0b10101010],
140            -0b0101_0101_1010_1010_1010_1010
141        ),
142        (
143            conversion_from_bytes_to_grib_int_for_3_bytes_negative_starting_from_0x80,
144            vec![0b10000000, 0b10101010, 0b10101010],
145            -0b0000_0000_1010_1010_1010_1010
146        ),
147        (
148            conversion_from_bytes_to_grib_int_for_4_bytes_positive,
149            vec![0b01010101, 0b10101010, 0b10101010, 0b10101010],
150            0b0101_0101_1010_1010_1010_1010_1010_1010
151        ),
152        (
153            conversion_from_bytes_to_grib_int_for_4_bytes_negative,
154            vec![0b11010101, 0b10101010, 0b10101010, 0b10101010],
155            -0b0101_0101_1010_1010_1010_1010_1010_1010
156        ),
157    }
158}