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 {
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}