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 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 { abs } else { -abs }
48 }
49 4 => read_as!(u32, bytes, 0).as_grib_int(),
50 _ => unimplemented!(),
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn into_grib_i8() {
60 let input: Vec<u8> = vec![0b01000000, 0b00000001, 0b10000001, 0b11000000];
61 let output: Vec<i8> = vec![64, 1, -1, -64];
62
63 let mut actual = Vec::new();
64 let mut pos = 0;
65 while pos < input.len() {
66 let val = u8::from_be_bytes(input[pos..pos + 1].try_into().unwrap());
67 pos += 1;
68 let val = val.as_grib_int();
69 actual.push(val);
70 }
71
72 assert_eq!(actual, output);
73 }
74
75 #[test]
76 fn into_grib_i16() {
77 let input: Vec<u8> = vec![
78 0b00000000, 0b01000000, 0b00000000, 0b00000001, 0b10000000, 0b00000001, 0b10000000,
79 0b01000000,
80 ];
81 let output: Vec<i16> = vec![64, 1, -1, -64];
82
83 let mut actual = Vec::new();
84 let mut pos = 0;
85 while pos < input.len() {
86 let val = u16::from_be_bytes(input[pos..pos + 2].try_into().unwrap());
87 pos += 2;
88 let val = val.as_grib_int();
89 actual.push(val);
90 }
91
92 assert_eq!(actual, output);
93 }
94
95 macro_rules! test_conversion_from_bytes_to_grib_int {
96 ($(($name:ident, $input:expr, $expected:expr),)*) => ($(
97 #[test]
98 fn $name() {
99 let bytes = $input;
100 let actual = grib_int_from_bytes(&bytes);
101 let expected = $expected;
102 assert_eq!(actual, expected)
103 }
104 )*);
105 }
106
107 test_conversion_from_bytes_to_grib_int! {
108 (
109 conversion_from_bytes_to_grib_int_for_1_byte_positive,
110 vec![0b01010101],
111 0b01010101
112 ),
113 (
114 conversion_from_bytes_to_grib_int_for_1_byte_negative,
115 vec![0b11010101],
116 -0b01010101
117 ),
118 (
119 conversion_from_bytes_to_grib_int_for_2_bytes_positive,
120 vec![0b01010101, 0b10101010],
121 0b0101_0101_1010_1010
122 ),
123 (
124 conversion_from_bytes_to_grib_int_for_2_bytes_negative,
125 vec![0b11010101, 0b10101010],
126 -0b0101_0101_1010_1010
127 ),
128 (
129 conversion_from_bytes_to_grib_int_for_3_bytes_positive,
130 vec![0b01010101, 0b10101010, 0b10101010],
131 0b0101_0101_1010_1010_1010_1010
132 ),
133 (
134 conversion_from_bytes_to_grib_int_for_3_bytes_negative,
135 vec![0b11010101, 0b10101010, 0b10101010],
136 -0b0101_0101_1010_1010_1010_1010
137 ),
138 (
139 conversion_from_bytes_to_grib_int_for_3_bytes_negative_starting_from_0x80,
140 vec![0b10000000, 0b10101010, 0b10101010],
141 -0b0000_0000_1010_1010_1010_1010
142 ),
143 (
144 conversion_from_bytes_to_grib_int_for_4_bytes_positive,
145 vec![0b01010101, 0b10101010, 0b10101010, 0b10101010],
146 0b0101_0101_1010_1010_1010_1010_1010_1010
147 ),
148 (
149 conversion_from_bytes_to_grib_int_for_4_bytes_negative,
150 vec![0b11010101, 0b10101010, 0b10101010, 0b10101010],
151 -0b0101_0101_1010_1010_1010_1010_1010_1010
152 ),
153 }
154}