egs_api/api/
utils.rs

1use num::{BigUint, Zero};
2use std::convert::TryInto;
3use std::ops::Shl;
4use std::borrow::BorrowMut;
5use std::num::ParseIntError;
6use std::cmp::Ordering;
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_le_64_signed(buffer: &[u8], position: &mut usize) -> i64 {
57    *position += 8;
58    i64::from_le_bytes(buffer[*position - 8..*position].try_into().unwrap())
59}
60
61pub(crate) fn read_fstring(buffer: &[u8], position: &mut usize) -> Option<String> {
62    let mut length = read_le_signed(buffer, position);
63    match length.cmp(&0) {
64        Ordering::Less => {
65            length *= -2;
66            *position += length as usize;
67            Some(String::from_utf16_lossy(
68                buffer[*position - length as usize..*position - 2]
69                    .chunks_exact(2)
70                    .map(|a| u16::from_ne_bytes([a[0], a[1]]))
71                    .collect::<Vec<u16>>()
72                    .as_slice(),
73            ))
74        }
75        Ordering::Equal => { None }
76        Ordering::Greater => {
77            *position += length as usize;
78            match std::str::from_utf8(&buffer[*position - length as usize..*position - 1]) {
79                Ok(s) => Some(s.to_string()),
80                Err(_) => None,
81            }
82        }
83    }
84}
85
86pub(crate) fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
87    (0..s.len())
88        .step_by(2)
89        .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
90        .collect()
91}
92
93pub(crate) fn write_fstring(string: String) -> Vec<u8> {
94    let mut meta: Vec<u8> = Vec::new();
95    if !string.is_empty() {
96        meta.append(
97            ((string.len() + 1) as u32)
98                .to_le_bytes()
99                .to_vec()
100                .borrow_mut(),
101        );
102        meta.append(string.into_bytes().borrow_mut());
103        meta.push(0);
104    } else {
105        meta.append(0u32.to_le_bytes().to_vec().borrow_mut())
106    }
107    meta
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::api::utils::{
113        bigblob_to_num, blob_to_num, do_vecs_match, read_fstring, read_le, read_le_64,
114        read_le_64_signed, read_le_signed,
115    };
116    use num::bigint::ToBigUint;
117
118    #[test]
119    fn vector_match() {
120        let a = vec![0, 0, 0];
121        let b = vec![0, 0, 0];
122        assert_eq!(do_vecs_match(&a, &b), true);
123    }
124
125    #[test]
126    fn vector_not_match() {
127        let a = vec![0, 0, 0];
128        let b = vec![0, 0, 1];
129        assert_eq!(do_vecs_match(&a, &b), false);
130    }
131
132    #[test]
133    fn blob_to_num_test() {
134        assert_eq!(blob_to_num("165045004000"), 273829)
135    }
136
137    #[test]
138    fn blob_to_bignum_test() {
139        assert_eq!(
140            bigblob_to_num("165045004000"),
141            ToBigUint::to_biguint(&273829).unwrap()
142        )
143    }
144
145    #[test]
146    fn read_le_test() {
147        let mut position: usize = 0;
148        let buffer = vec![1, 2, 3, 4];
149        assert_eq!(read_le(&buffer, &mut position), 67305985);
150        assert_eq!(position, 4)
151    }
152
153    #[test]
154    fn read_le_signed_test() {
155        let mut position: usize = 0;
156        let buffer = vec![237, 201, 255, 255];
157        assert_eq!(read_le_signed(&buffer, &mut position), -13843);
158        assert_eq!(position, 4)
159    }
160
161    #[test]
162    fn read_le_64_test() {
163        let mut position: usize = 0;
164        let buffer = vec![0, 0, 5, 3, 0, 1, 2, 3];
165        assert_eq!(read_le_64(&buffer, &mut position), 216736831629492224);
166        assert_eq!(position, 8)
167    }
168
169    #[test]
170    fn read_le_64_signed_test() {
171        let mut position: usize = 0;
172        let buffer = vec![237, 201, 255, 255, 255, 255, 255, 255];
173        assert_eq!(read_le_64_signed(&buffer, &mut position), -13843);
174        assert_eq!(position, 8)
175    }
176
177    #[test]
178    fn read_fstring_utf8() {
179        let mut position: usize = 0;
180        let buffer = vec![5, 0, 0, 0, 97, 98, 99, 100, 0];
181        assert_eq!(
182            read_fstring(&buffer, &mut position),
183            Some("abcd".to_string())
184        );
185        assert_eq!(position, 9)
186    }
187
188    #[test]
189    fn read_fstring_utf16() {
190        let mut position: usize = 0;
191        let buffer = vec![251, 255, 255, 255, 97, 0, 98, 0, 99, 0, 100, 0, 0, 0];
192        assert_eq!(
193            read_fstring(&buffer, &mut position),
194            Some("abcd".to_string())
195        );
196        assert_eq!(position, 14)
197    }
198}