#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct MlowFrame<'a> {
pub data: &'a [u8],
pub time_code: u8,
pub is_main: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RedError {
PktSizeZero,
HeaderTooShort,
RedundantTooShort,
MainTooShort,
}
pub(crate) fn depack_split_red(p: &[u8]) -> Result<Vec<MlowFrame<'_>>, RedError> {
let n = p.len();
if n == 0 {
return Err(RedError::PktSizeZero);
}
struct RedBlock {
code: u8,
size: u8,
}
let mut red: Vec<RedBlock> = Vec::new();
let mut cur = 0usize;
let mut rem = n;
loop {
if rem == 0 {
return Err(RedError::HeaderTooShort);
}
let b0 = p[cur];
if b0 < 0x80 {
if rem <= 1 {
return Err(RedError::MainTooShort);
}
break;
}
if rem <= 2 {
return Err(RedError::RedundantTooShort);
}
let size = p[cur + 1];
if size as usize + 2 >= rem {
return Err(RedError::RedundantTooShort);
}
red.push(RedBlock {
code: b0 & 0x7f,
size,
});
cur += 2;
rem -= size as usize + 2;
}
let main_code = p[cur] & 0x7f;
cur += 1;
let mut frames = Vec::with_capacity(red.len() + 1);
for r in &red {
frames.push(MlowFrame {
data: &p[cur..cur + r.size as usize],
time_code: r.code,
is_main: false,
});
cur += r.size as usize;
}
let main_size = rem - 1; frames.push(MlowFrame {
data: &p[cur..cur + main_size],
time_code: main_code,
is_main: true,
});
log::debug!(
"mlow RED: {} redundant + 1 main frame ({}B main)",
red.len(),
main_size
);
Ok(frames)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn depack_one_redundant_plus_main() {
let p = [0x85, 0x03, 0x00, 0xAA, 0xBB, 0xCC, 0x50, 0x11, 0x22, 0x33];
let frames = depack_split_red(&p).unwrap();
assert_eq!(frames.len(), 2);
assert_eq!(
frames[0],
MlowFrame {
data: &[0xAA, 0xBB, 0xCC],
time_code: 5,
is_main: false
}
);
assert_eq!(
frames[1],
MlowFrame {
data: &[0x50, 0x11, 0x22, 0x33],
time_code: 0,
is_main: true
}
);
}
#[test]
fn depack_no_redundant_just_main() {
let p = [0x00, 0x50, 0x11, 0x22];
let frames = depack_split_red(&p).unwrap();
assert_eq!(frames.len(), 1);
assert!(frames[0].is_main);
assert_eq!(frames[0].data, &[0x50, 0x11, 0x22]);
}
#[test]
fn empty_packet_errors() {
assert_eq!(depack_split_red(&[]), Err(RedError::PktSizeZero));
}
#[test]
fn bare_frame_is_rejected() {
assert!(depack_split_red(&[0x90, 0x01, 0x02]).is_err());
}
}