fastpasta/analyze/validators/
lib.rs1use crate::util::*;
4
5#[derive(Debug, PartialEq, Clone, Copy)]
6enum DataFormat {
7    V0,
8    V2,
9}
10
11pub fn preprocess_payload(payload: &[u8]) -> Result<ChunksExact<'_, u8>, String> {
26    let ff_padding = extract_payload_ff_padding(payload)?;
27
28    let detected_data_format = detect_payload_data_format(payload);
30
31    let gbt_word_chunks = chunkify_payload(payload, detected_data_format, &ff_padding);
32    Ok(gbt_word_chunks)
33}
34
35fn extract_payload_ff_padding(payload: &[u8]) -> Result<Vec<&u8>, String> {
37    let ff_padding = payload
38        .iter()
39        .rev()
40        .take_while(|&x| *x == 0xFF)
41        .collect::<Vec<_>>();
42    if ff_padding.len() > 15 {
44        return Err(format!("End of payload 0xFF padding is {} bytes, exceeding max of 15 bytes: Skipping current payload",
45        ff_padding.len()));
46    }
47    Ok(ff_padding)
48}
49
50fn detect_payload_data_format(payload: &[u8]) -> DataFormat {
52    if payload
54        .iter()
55        .skip(10)
57        .take(6)
59        .take_while(|&x| *x == 0x00)
61        .count()
63        == 6
64    {
65        DataFormat::V0
66    } else {
67        DataFormat::V2
68    }
69}
70
71fn chunkify_payload<'a>(
73    payload: &'a [u8],
74    data_format: DataFormat,
75    ff_padding: &[&'a u8],
76) -> ChunksExact<'a, u8> {
77    match data_format {
78        DataFormat::V0 => {
79            let chunks = payload.chunks_exact(16);
80            debug_assert!(chunks.remainder().is_empty());
82            chunks
83        }
84        DataFormat::V2 => {
85            if ff_padding.len() > 9 {
88                let last_idx_before_padding = payload.len() - ff_padding.len();
89                let chunks = payload[..last_idx_before_padding].chunks_exact(10);
90                debug_assert!(chunks.remainder().is_empty());
91                chunks
92            } else {
93                let chunks = payload.chunks_exact(10);
95                debug_assert!(chunks.remainder().iter().all(|&x| x == 0xFF)); chunks
97            }
98        }
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_preprocess_payload_flavors() {
108        let word_chunk_f0 = preprocess_payload(&START_PAYLOAD_FLAVOR_0).unwrap();
109        let word_chunks_f2 = preprocess_payload(&START_PAYLOAD_FLAVOR_2).unwrap();
110
111        let word_count = word_chunk_f0.count();
112        let word_count_f2 = word_chunks_f2.count();
113
114        assert_eq!(word_count, 2);
115        assert_eq!(word_count_f2, 2);
116    }
117
118    #[test]
119    fn test_extract_payload_padding() {
120        let end_payload_flavor_0_padding =
121            extract_payload_ff_padding(&END_PAYLOAD_FLAVOR_0).unwrap();
122        let end_payload_flavor_2_padding =
123            extract_payload_ff_padding(&END_PAYLOAD_FLAVOR_2).unwrap();
124
125        assert!(end_payload_flavor_0_padding.is_empty());
126        assert_eq!(end_payload_flavor_2_padding.len(), 6);
127    }
128
129    #[test]
130    fn test_detect_payload_data_format() {
131        let detected_data_format_f0 = detect_payload_data_format(&START_PAYLOAD_FLAVOR_0);
132        let detected_data_format_f2 = detect_payload_data_format(&START_PAYLOAD_FLAVOR_2);
133
134        assert_eq!(detected_data_format_f0, DataFormat::V0);
135        assert_eq!(detected_data_format_f2, DataFormat::V2);
136    }
137}