Skip to main content

egs_api/api/
utils.rs

1use num::{BigUint, Zero};
2use std::borrow::BorrowMut;
3use std::cmp::Ordering;
4use std::convert::TryInto;
5use std::num::ParseIntError;
6use std::ops::Shl;
7
8/// Convert numbers in the Download Manifest from little indian and %03d concatenated string
9pub fn blob_to_num<T: Into<String>>(str: T) -> u128 {
10    let mut num: u128 = 0;
11    let mut shift: u128 = 0;
12    let string = str.into();
13    for i in (0..string.len()).step_by(3) {
14        if let Ok(n) = string[i..i + 3].parse::<u128>() {
15            num += n.checked_shl(shift as u32).unwrap_or(0);
16            shift += 8;
17        }
18    }
19    num
20}
21
22/// Convert BIG numbers in the Download Manifest from little indian and %03d concatenated string
23pub fn bigblob_to_num<T: Into<String>>(str: T) -> BigUint {
24    let mut num: BigUint = BigUint::zero();
25    let mut shift: u128 = 0;
26    let string = str.into();
27    for i in (0..string.len()).step_by(3) {
28        if let Ok(n) = string[i..i + 3].parse::<BigUint>() {
29            num += n.shl(shift);
30            shift += 8;
31        }
32    }
33    num
34}
35
36pub(crate) fn do_vecs_match<T: PartialEq>(a: &[T], b: &[T]) -> bool {
37    let matching = a.iter().zip(b.iter()).filter(|&(a, b)| a == b).count();
38    matching == a.len() && matching == b.len()
39}
40
41pub(crate) fn read_le(buffer: &[u8], position: &mut usize) -> u32 {
42    *position += 4;
43    u32::from_le_bytes(buffer[*position - 4..*position].try_into().unwrap())
44}
45
46pub(crate) fn read_le_signed(buffer: &[u8], position: &mut usize) -> i32 {
47    *position += 4;
48    i32::from_le_bytes(buffer[*position - 4..*position].try_into().unwrap())
49}
50
51pub(crate) fn read_le_64(buffer: &[u8], position: &mut usize) -> u64 {
52    *position += 8;
53    u64::from_le_bytes(buffer[*position - 8..*position].try_into().unwrap())
54}
55
56pub(crate) fn read_fstring(buffer: &[u8], position: &mut usize) -> Option<String> {
57    let mut length = read_le_signed(buffer, position);
58    match length.cmp(&0) {
59        Ordering::Less => {
60            length *= -2;
61            *position += length as usize;
62            Some(String::from_utf16_lossy(
63                buffer[*position - length as usize..*position - 2]
64                    .chunks_exact(2)
65                    .map(|a| u16::from_ne_bytes([a[0], a[1]]))
66                    .collect::<Vec<u16>>()
67                    .as_slice(),
68            ))
69        }
70        Ordering::Equal => None,
71        Ordering::Greater => {
72            *position += length as usize;
73            match std::str::from_utf8(&buffer[*position - length as usize..*position - 1]) {
74                Ok(s) => Some(s.to_string()),
75                Err(_) => None,
76            }
77        }
78    }
79}
80
81pub(crate) fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
82    (0..s.len())
83        .step_by(2)
84        .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
85        .collect()
86}
87
88pub(crate) fn write_fstring(string: &str) -> Vec<u8> {
89    let mut meta: Vec<u8> = Vec::new();
90    if !string.is_empty() {
91        meta.append(
92            ((string.len() + 1) as u32)
93                .to_le_bytes()
94                .to_vec()
95                .borrow_mut(),
96        );
97        meta.append(string.as_bytes().to_vec().borrow_mut());
98        meta.push(0);
99    } else {
100        meta.append(0u32.to_le_bytes().to_vec().borrow_mut())
101    }
102    meta
103}
104
105#[cfg(test)]
106mod tests {
107    use crate::api::utils::{
108        bigblob_to_num, blob_to_num, decode_hex, do_vecs_match, read_fstring, read_le, read_le_64,
109        read_le_signed, write_fstring,
110    };
111    use num::bigint::ToBigUint;
112
113    #[test]
114    fn vector_match() {
115        let a = vec![0, 0, 0];
116        let b = vec![0, 0, 0];
117        assert!(do_vecs_match(&a, &b));
118    }
119
120    #[test]
121    fn vector_not_match() {
122        let a = vec![0, 0, 0];
123        let b = vec![0, 0, 1];
124        assert!(!do_vecs_match(&a, &b));
125    }
126
127    #[test]
128    fn blob_to_num_test() {
129        assert_eq!(blob_to_num("165045004000"), 273829)
130    }
131
132    #[test]
133    fn blob_to_bignum_test() {
134        assert_eq!(
135            bigblob_to_num("165045004000"),
136            ToBigUint::to_biguint(&273829).unwrap()
137        )
138    }
139
140    #[test]
141    fn read_le_test() {
142        let mut position: usize = 0;
143        let buffer = vec![1, 2, 3, 4];
144        assert_eq!(read_le(&buffer, &mut position), 67305985);
145        assert_eq!(position, 4)
146    }
147
148    #[test]
149    fn read_le_signed_test() {
150        let mut position: usize = 0;
151        let buffer = vec![237, 201, 255, 255];
152        assert_eq!(read_le_signed(&buffer, &mut position), -13843);
153        assert_eq!(position, 4)
154    }
155
156    #[test]
157    fn read_le_64_test() {
158        let mut position: usize = 0;
159        let buffer = vec![0, 0, 5, 3, 0, 1, 2, 3];
160        assert_eq!(read_le_64(&buffer, &mut position), 216736831629492224);
161        assert_eq!(position, 8)
162    }
163
164    #[test]
165    fn read_fstring_utf8() {
166        let mut position: usize = 0;
167        let buffer = vec![5, 0, 0, 0, 97, 98, 99, 100, 0];
168        assert_eq!(
169            read_fstring(&buffer, &mut position),
170            Some("abcd".to_string())
171        );
172        assert_eq!(position, 9)
173    }
174
175    #[test]
176    fn read_fstring_utf16() {
177        let mut position: usize = 0;
178        let buffer = vec![251, 255, 255, 255, 97, 0, 98, 0, 99, 0, 100, 0, 0, 0];
179        assert_eq!(
180            read_fstring(&buffer, &mut position),
181            Some("abcd".to_string())
182        );
183        assert_eq!(position, 14)
184    }
185
186    #[test]
187    fn write_fstring_nonempty() {
188        let buffer = write_fstring("hello");
189        assert_eq!(buffer, vec![6, 0, 0, 0, 104, 101, 108, 108, 111, 0])
190    }
191
192    #[test]
193    fn write_fstring_empty() {
194        let buffer = write_fstring("");
195        assert_eq!(buffer, vec![0, 0, 0, 0])
196    }
197
198    #[test]
199    fn write_fstring_roundtrip() {
200        let original = "roundtrip".to_string();
201        let buffer = write_fstring(&original);
202        let mut position: usize = 0;
203        assert_eq!(read_fstring(&buffer, &mut position), Some(original));
204    }
205
206    #[test]
207    fn decode_hex_valid() {
208        assert_eq!(
209            decode_hex("48656c6c6f").unwrap(),
210            vec![72, 101, 108, 108, 111]
211        )
212    }
213
214    #[test]
215    fn decode_hex_empty() {
216        assert_eq!(decode_hex("").unwrap(), Vec::<u8>::new())
217    }
218
219    #[test]
220    fn decode_hex_invalid() {
221        assert!(decode_hex("zz").is_err())
222    }
223
224    #[test]
225    fn read_fstring_zero_length() {
226        let mut position: usize = 0;
227        let buffer = vec![0, 0, 0, 0];
228        assert_eq!(read_fstring(&buffer, &mut position), None);
229        assert_eq!(position, 4)
230    }
231
232    #[test]
233    fn read_fstring_invalid_utf8() {
234        let mut position: usize = 0;
235        let buffer = vec![3, 0, 0, 0, 255, 254, 0];
236        assert_eq!(read_fstring(&buffer, &mut position), None);
237        assert_eq!(position, 7)
238    }
239
240    #[test]
241    fn blob_to_num_empty() {
242        assert_eq!(blob_to_num(""), 0)
243    }
244
245    #[test]
246    fn blob_to_num_single() {
247        assert_eq!(blob_to_num("042"), 42)
248    }
249}