Skip to main content

pictorus_blocks/
byte_data.rs

1extern crate alloc;
2use alloc::vec::Vec;
3
4#[derive(Debug, PartialEq, Clone, Copy)]
5pub enum ByteDataError {
6    FindByteError,
7    UnpackError,
8    PackError,
9    StartDelimiterNotFound,
10    EndDelimiterNotFound,
11    InsufficientData,
12}
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::EnumString)]
15#[strum(ascii_case_insensitive)]
16pub enum DataType {
17    U8,
18    I8,
19    U16,
20    I16,
21    U24,
22    I24,
23    U32,
24    I32,
25    U48,
26    I48,
27    U64,
28    I64,
29    U128,
30    I128,
31    F32,
32    F64,
33}
34
35impl DataType {
36    pub fn byte_size(&self) -> usize {
37        match self {
38            DataType::U8 => 1,
39            DataType::I8 => 1,
40            DataType::U16 => 2,
41            DataType::I16 => 2,
42            DataType::U24 => 3,
43            DataType::I24 => 3,
44            DataType::U32 => 4,
45            DataType::I32 => 4,
46            DataType::U48 => 6,
47            DataType::I48 => 6,
48            DataType::U64 => 8,
49            DataType::I64 => 8,
50            DataType::U128 => 16,
51            DataType::I128 => 16,
52            DataType::F32 => 4,
53            DataType::F64 => 8,
54        }
55    }
56}
57
58#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::EnumString)]
59pub enum ByteOrderSpec {
60    BigEndian,
61    LittleEndian,
62}
63pub fn parse_byte_data_spec<S: AsRef<str>>(data: &[S]) -> Vec<(DataType, ByteOrderSpec)> {
64    data.iter()
65        .map(|d| {
66            d.as_ref()
67                .split_once(':')
68                .expect("Invalid byte data format")
69        })
70        .map(|(dt, bo)| {
71            (
72                dt.parse::<DataType>().unwrap(),
73                bo.parse::<ByteOrderSpec>().unwrap(),
74            )
75        })
76        .collect()
77}
78
79pub fn compare_bytes(data: &[u8], check: &[u8], skip_byte_indices: &[usize]) -> bool {
80    if skip_byte_indices.len() + check.len() != data.len() {
81        return false;
82    }
83
84    if skip_byte_indices.is_empty() {
85        return data == check;
86    }
87
88    let mut skipped = 0;
89    for (i, c) in data.iter().enumerate() {
90        if skip_byte_indices.contains(&i) {
91            skipped += 1;
92            continue;
93        }
94
95        let check_val = &check[i - skipped];
96        if c != check_val {
97            return false;
98        }
99    }
100
101    true
102}
103
104pub fn find_all_bytes_idx(data: &[u8], check: &[u8], skip_byte_indices: &[usize]) -> Vec<usize> {
105    let window_size = check.len() + skip_byte_indices.len();
106    data.windows(window_size)
107        .enumerate()
108        .filter(|&(_, w)| compare_bytes(w, check, skip_byte_indices))
109        .map(|(idx, _)| idx)
110        .collect()
111}
112
113pub fn rfind_all_bytes_idx(data: &[u8], check: &[u8], skip_byte_indices: &[usize]) -> Vec<usize> {
114    let window_size = check.len() + skip_byte_indices.len();
115    data.windows(window_size)
116        .rev()
117        .enumerate()
118        .filter(|&(_, w)| compare_bytes(w, check, skip_byte_indices))
119        .map(|(idx, _)| data.len() - idx - window_size)
120        .collect()
121}
122
123pub fn find_bytes_idx(
124    data: &[u8],
125    check: &[u8],
126    skip_byte_indices: &[usize],
127) -> Result<usize, ByteDataError> {
128    // Takes the data and splits it into window sized chunks and
129    // searches forwards through the windows for the "check" bytes and then stops
130    // at the first match. Then maps the data to index.
131    let window_size = check.len() + skip_byte_indices.len();
132    data.windows(window_size)
133        .enumerate()
134        .find(|&(_, w)| compare_bytes(w, check, skip_byte_indices))
135        .map(|(idx, _)| idx)
136        .ok_or(ByteDataError::FindByteError)
137}
138
139pub fn rfind_bytes_idx(
140    data: &[u8],
141    check: &[u8],
142    skip_byte_indices: &[usize],
143) -> Result<usize, ByteDataError> {
144    let window_size = check.len() + skip_byte_indices.len();
145    // Takes the data and splits it into window sized chunks and
146    // searches backwards through the windows for the "check" bytes and then stops
147    // at the first match. Then maps the data to index accounting for the window size.
148    data.windows(window_size)
149        .rev()
150        .enumerate()
151        .find(|&(_, w)| compare_bytes(w, check, skip_byte_indices))
152        .map(|(idx, _)| data.len() - idx - window_size)
153        .ok_or(ByteDataError::FindByteError)
154}
155
156const HEX_DELIM: &str = r"\x";
157pub fn parse_string_to_bytes(data: &str) -> Vec<u8> {
158    if data.starts_with(HEX_DELIM) {
159        let split_data: Vec<&str> = data.split(HEX_DELIM).filter(|c| !c.is_empty()).collect();
160        let byte_data: Vec<u8> = split_data
161            .iter()
162            .filter_map(|c| u8::from_str_radix(c, 16).ok())
163            .collect();
164
165        if byte_data.len() == split_data.len() {
166            return byte_data;
167        }
168    }
169
170    Vec::from(data.as_bytes())
171}
172
173pub fn parse_string_to_read_delimiter(data: &str) -> (Vec<u8>, Vec<usize>) {
174    if data.starts_with(HEX_DELIM) {
175        let split_data: Vec<&str> = data.split(HEX_DELIM).filter(|c| !c.is_empty()).collect();
176        let byte_data: Vec<u8> = split_data
177            .iter()
178            .filter_map(|c| u8::from_str_radix(c, 16).ok())
179            .collect();
180
181        let skip_bytes: Vec<usize> = split_data
182            .iter()
183            .enumerate()
184            .filter_map(|(i, c)| if *c == "**" { Some(i) } else { None })
185            .collect();
186
187        if byte_data.len() + skip_bytes.len() == split_data.len() {
188            return (byte_data, skip_bytes);
189        }
190    }
191
192    (Vec::from(data.as_bytes()), Vec::new())
193}
194
195pub const BUFF_SIZE_BYTES: usize = 1024;
196
197use byteorder::ByteOrder;
198
199pub fn try_unpack_data<Endian: ByteOrder>(
200    buf: &[u8],
201    data_type: DataType,
202) -> Result<f64, ByteDataError> {
203    if buf.len() < data_type.byte_size() {
204        return Err(ByteDataError::UnpackError);
205    }
206    let val = match data_type {
207        DataType::U8 => buf[0] as f64,
208        DataType::I8 => buf[0] as i8 as f64,
209        DataType::U16 => Endian::read_u16(buf) as f64,
210        DataType::I16 => Endian::read_i16(buf) as f64,
211        DataType::U24 => Endian::read_u24(buf) as f64,
212        DataType::I24 => Endian::read_i24(buf) as f64,
213        DataType::U32 => Endian::read_u32(buf) as f64,
214        DataType::I32 => Endian::read_i32(buf) as f64,
215        DataType::U48 => Endian::read_u48(buf) as f64,
216        DataType::I48 => Endian::read_i48(buf) as f64,
217        DataType::U64 => Endian::read_u64(buf) as f64,
218        DataType::I64 => Endian::read_i64(buf) as f64,
219        DataType::U128 => Endian::read_u128(buf) as f64,
220        DataType::I128 => Endian::read_i128(buf) as f64,
221        DataType::F32 => Endian::read_f32(buf) as f64,
222        DataType::F64 => Endian::read_f64(buf),
223    };
224    Ok(val)
225}
226
227pub fn try_pack_data<Endian: ByteOrder>(
228    buf: &mut [u8],
229    value: f64,
230    data_type: DataType,
231) -> Result<usize, ByteDataError> {
232    if buf.len() < data_type.byte_size() {
233        return Err(ByteDataError::PackError);
234    }
235    match data_type {
236        DataType::U8 => buf[0] = value as u8,
237        DataType::I8 => buf[0] = value as i8 as u8,
238        DataType::U16 => Endian::write_u16(buf, value as u16),
239        DataType::I16 => Endian::write_i16(buf, value as i16),
240        DataType::U24 => Endian::write_u24(buf, value as u32),
241        DataType::I24 => Endian::write_i24(buf, value as i32),
242        DataType::U32 => Endian::write_u32(buf, value as u32),
243        DataType::I32 => Endian::write_i32(buf, value as i32),
244        DataType::U48 => Endian::write_u48(buf, value as u64),
245        DataType::I48 => Endian::write_i48(buf, value as i64),
246        DataType::U64 => Endian::write_u64(buf, value as u64),
247        DataType::I64 => Endian::write_i64(buf, value as i64),
248        DataType::U128 => Endian::write_u128(buf, value as u128),
249        DataType::I128 => Endian::write_i128(buf, value as i128),
250        DataType::F32 => Endian::write_f32(buf, value as f32),
251        DataType::F64 => Endian::write_f64(buf, value),
252    };
253    Ok(data_type.byte_size())
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259    use alloc::string::ToString;
260    use alloc::vec;
261    use byteorder::BigEndian;
262
263    #[test]
264    fn test_parse_byte_data_spec() {
265        let input = vec![
266            "U8:BigEndian".to_string(),
267            "I16:LittleEndian".to_string(),
268            "F32:BigEndian".to_string(),
269        ];
270
271        let expected = vec![
272            (DataType::U8, ByteOrderSpec::BigEndian),
273            (DataType::I16, ByteOrderSpec::LittleEndian),
274            (DataType::F32, ByteOrderSpec::BigEndian),
275        ];
276
277        let output = parse_byte_data_spec(&input);
278
279        assert_eq!(output, expected);
280    }
281
282    #[test]
283    fn test_compare_bytes() {
284        let result = compare_bytes(b"Hello, world!", b"Hello, world!", &[]);
285        assert!(result);
286
287        let result = compare_bytes(b"Hello, world!", b"Hello, wor", &[]);
288        assert!(!result);
289
290        let result = compare_bytes(b"012345", b"045", &[1, 2, 3]);
291        assert!(result);
292
293        let result = compare_bytes(b"012345", b"0c45", &[1, 2]);
294        assert!(!result);
295    }
296
297    #[test]
298    fn test_find_all_bytes_idx() {
299        let data = b"Hello, world! This is an example of finding bytes.";
300        let pattern = b"l";
301        let expected = vec![2, 3, 10, 30];
302        let result = find_all_bytes_idx(data, pattern, &[]);
303        assert_eq!(result, expected);
304    }
305
306    #[test]
307    fn test_find_all_bytes_idx_skip_indices() {
308        let data = b"aaa123aba123aca123";
309        let pattern = b"aa";
310        let expected = vec![0, 6, 12];
311        let result = find_all_bytes_idx(data, pattern, &[1]);
312        assert_eq!(result, expected);
313    }
314
315    #[test]
316    fn test_rfind_all_bytes_idx() {
317        let data = b"Hello, world! This is an example of finding bytes.";
318        let pattern = b"l";
319        let expected = vec![30, 10, 3, 2];
320        let result = rfind_all_bytes_idx(data, pattern, &[]);
321        assert_eq!(result, expected);
322    }
323
324    #[test]
325    fn test_rfind_all_bytes_idx_skip_indices() {
326        let data = b"aaa123aba123aca123";
327        let pattern = b"aa";
328        let expected = vec![12, 6, 0];
329        let result = rfind_all_bytes_idx(data, pattern, &[1]);
330        assert_eq!(result, expected);
331    }
332
333    #[test]
334    fn test_find_bytes_idx() {
335        let data = b"Hello, world! This is an example of finding bytes.";
336        let pattern = b"world";
337        let expected = Ok(7);
338        let result = find_bytes_idx(data, pattern, &[]);
339        assert_eq!(result, expected);
340    }
341
342    #[test]
343    fn test_find_bytes_idx_skip_indices() {
344        let data = b"aaa123aba123aca123";
345        let pattern = b"aa";
346        let expected = Ok(0);
347        let result = find_bytes_idx(data, pattern, &[1]);
348        assert_eq!(result, expected);
349    }
350
351    #[test]
352    fn test_rfind_bytes_idx() {
353        let data = b"Hello, world! This is an example of finding bytes.";
354        let pattern = b"world";
355        let expected = Ok(7);
356        let result = rfind_bytes_idx(data, pattern, &[]);
357        assert_eq!(result, expected);
358    }
359
360    #[test]
361    fn test_rfind_bytes_idx_skip_indices() {
362        let data = b"aaa123aba123aca123";
363        let pattern = b"aa";
364        let expected = Ok(12);
365        let result = rfind_bytes_idx(data, pattern, &[1]);
366        assert_eq!(result, expected);
367    }
368
369    #[test]
370    fn test_parse_string_to_bytes() {
371        let input = r"\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21";
372        let expected = b"Hello, world!".to_vec();
373        let result = parse_string_to_bytes(input);
374        assert_eq!(result, expected);
375    }
376
377    #[test]
378    fn test_parse_string_to_read_delimiter() {
379        let input = r"\x48\x65\x**\x6c\x**\x2C";
380        let expected = (b"\x48\x65\x6c\x2C".to_vec(), vec![2, 4]);
381        let result = parse_string_to_read_delimiter(input);
382        assert_eq!(result, expected);
383    }
384
385    #[test]
386    fn test_try_unpack_data_and_try_pack_data() {
387        let input: f64 = 1234.5678;
388        let dt = DataType::F64;
389        let mut packed_data = vec![0; dt.byte_size()];
390
391        try_pack_data::<BigEndian>(&mut packed_data, input, dt).unwrap();
392        let unpacked_data = try_unpack_data::<BigEndian>(&packed_data, dt).unwrap();
393
394        assert_eq!(input, unpacked_data);
395    }
396}