use crate::util::*;
#[derive(Debug, PartialEq, Clone, Copy)]
enum DataFormat {
V0,
V2,
}
pub fn preprocess_payload(payload: &[u8]) -> Result<ChunksExact<'_, u8>, String> {
let ff_padding = extract_payload_ff_padding(payload)?;
let detected_data_format = detect_payload_data_format(payload);
let gbt_word_chunks = chunkify_payload(payload, detected_data_format, &ff_padding);
Ok(gbt_word_chunks)
}
fn extract_payload_ff_padding(payload: &[u8]) -> Result<Vec<&u8>, String> {
let ff_padding = payload
.iter()
.rev()
.take_while(|&x| *x == 0xFF)
.collect::<Vec<_>>();
if ff_padding.len() > 15 {
return Err(format!("End of payload 0xFF padding is {} bytes, exceeding max of 15 bytes: Skipping current payload",
ff_padding.len()));
}
Ok(ff_padding)
}
fn detect_payload_data_format(payload: &[u8]) -> DataFormat {
if payload
.iter()
.skip(10)
.take(6)
.take_while(|&x| *x == 0x00)
.count()
== 6
{
DataFormat::V0
} else {
DataFormat::V2
}
}
fn chunkify_payload<'a>(
payload: &'a [u8],
data_format: DataFormat,
ff_padding: &[&'a u8],
) -> ChunksExact<'a, u8> {
match data_format {
DataFormat::V0 => {
let chunks = payload.chunks_exact(16);
debug_assert!(chunks.remainder().is_empty());
chunks
}
DataFormat::V2 => {
if ff_padding.len() > 9 {
let last_idx_before_padding = payload.len() - ff_padding.len();
let chunks = payload[..last_idx_before_padding].chunks_exact(10);
debug_assert!(chunks.remainder().is_empty());
chunks
} else {
let chunks = payload.chunks_exact(10);
debug_assert!(chunks.remainder().iter().all(|&x| x == 0xFF)); chunks
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preprocess_payload_flavors() {
let word_chunk_f0 = preprocess_payload(&START_PAYLOAD_FLAVOR_0).unwrap();
let word_chunks_f2 = preprocess_payload(&START_PAYLOAD_FLAVOR_2).unwrap();
let word_count = word_chunk_f0.count();
let word_count_f2 = word_chunks_f2.count();
assert_eq!(word_count, 2);
assert_eq!(word_count_f2, 2);
}
#[test]
fn test_extract_payload_padding() {
let end_payload_flavor_0_padding =
extract_payload_ff_padding(&END_PAYLOAD_FLAVOR_0).unwrap();
let end_payload_flavor_2_padding =
extract_payload_ff_padding(&END_PAYLOAD_FLAVOR_2).unwrap();
assert!(end_payload_flavor_0_padding.is_empty());
assert_eq!(end_payload_flavor_2_padding.len(), 6);
}
#[test]
fn test_detect_payload_data_format() {
let detected_data_format_f0 = detect_payload_data_format(&START_PAYLOAD_FLAVOR_0);
let detected_data_format_f2 = detect_payload_data_format(&START_PAYLOAD_FLAVOR_2);
assert_eq!(detected_data_format_f0, DataFormat::V0);
assert_eq!(detected_data_format_f2, DataFormat::V2);
}
}