use crate::{
blocks::{
extended::ExtendedNavigationBlock, external::ExternalBlock, navigation::NavigationBlock,
},
error::{Error, Result},
};
use std::io::{Cursor, Read};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncType {
NavData,
Command,
Answer,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OutputHeader {
pub version: u8,
pub nav_bitmask: u32,
pub extended_nav_bitmask: u32,
pub external_bitmask: u32,
pub nav_data_size: u16,
pub total_size: u16,
pub validity_time: u32,
pub counter: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InputHeader {
pub version: u8,
pub nav_bitmask: u32,
pub extended_nav_bitmask: u32,
pub external_bitmask: u32,
pub nav_data_size: u16,
pub total_size: u16,
pub timestamp_ref: u8,
pub rfu: [u8; 7],
}
#[derive(Debug, Clone, PartialEq)]
pub struct CommandHeader {
pub version: u8,
pub total_size: u16,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AnswerHeader {
pub version: u8,
pub total_size: u16,
}
fn read_u8(cursor: &mut Cursor<&[u8]>) -> Result<u8> {
let mut buf = [0u8; 1];
cursor.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(buf[0])
}
fn read_u16(cursor: &mut Cursor<&[u8]>) -> Result<u16> {
let mut buf = [0u8; 2];
cursor.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(u16::from_be_bytes(buf))
}
fn read_u32(cursor: &mut Cursor<&[u8]>) -> Result<u32> {
let mut buf = [0u8; 4];
cursor.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(u32::from_be_bytes(buf))
}
impl OutputHeader {
pub fn new(
version: u8,
navigation: &[NavigationBlock],
extended_navigation: &[ExtendedNavigationBlock],
external: &[ExternalBlock],
validity_time: u32,
counter: u32,
) -> Result<Self> {
let header_size = Self::header_size(version)?;
let nav_size: usize = navigation.iter().map(|b| b.size()).sum();
let ext_nav_size: usize = extended_navigation.iter().map(|b| b.size()).sum();
let external_size: usize = external.iter().map(|b| b.size()).sum();
let total_size = (header_size + nav_size + ext_nav_size + external_size + 4) as u16;
let nav_data_size = if version >= 4 {
(nav_size + ext_nav_size) as u16
} else {
0
};
Ok(Self {
version,
nav_bitmask: NavigationBlock::bitmask(navigation),
extended_nav_bitmask: ExtendedNavigationBlock::bitmask(extended_navigation),
external_bitmask: ExternalBlock::bitmask(external),
nav_data_size,
total_size,
validity_time,
counter,
})
}
pub fn header_size(version: u8) -> Result<usize> {
match version {
2 => Ok(21),
3 => Ok(25),
4..=6 => Ok(27),
v => Err(Error::UnsupportedVersion(v)),
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
let cursor = &mut Cursor::new(data);
let version = read_u8(cursor)?;
match version {
2 => {
let nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let total_size = read_u16(cursor)?;
let validity_time = read_u32(cursor)?;
let counter = read_u32(cursor)?;
Ok(OutputHeader {
version,
nav_bitmask,
extended_nav_bitmask: 0,
external_bitmask,
nav_data_size: 0,
total_size,
validity_time,
counter,
})
}
3 => {
let nav_bitmask = read_u32(cursor)?;
let extended_nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let total_size = read_u16(cursor)?;
let validity_time = read_u32(cursor)?;
let counter = read_u32(cursor)?;
Ok(OutputHeader {
version,
nav_bitmask,
extended_nav_bitmask,
external_bitmask,
nav_data_size: 0,
total_size,
validity_time,
counter,
})
}
4..=6 => {
let nav_bitmask = read_u32(cursor)?;
let extended_nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let nav_data_size = read_u16(cursor)?;
let total_size = read_u16(cursor)?;
let validity_time = read_u32(cursor)?;
let counter = read_u32(cursor)?;
Ok(OutputHeader {
version,
nav_bitmask,
extended_nav_bitmask,
external_bitmask,
nav_data_size,
total_size,
validity_time,
counter,
})
}
v => Err(Error::UnsupportedVersion(v)),
}
}
}
impl InputHeader {
pub fn new(version: u8, external: &[ExternalBlock], timestamp_ref: u8) -> Result<Self> {
let header_size = Self::header_size(version)?;
let external_size: usize = external.iter().map(|b| b.size()).sum();
let total_size = (header_size + external_size + 4) as u16;
Ok(Self {
version,
nav_bitmask: 0,
extended_nav_bitmask: 0,
external_bitmask: ExternalBlock::bitmask(external),
nav_data_size: 0,
total_size,
timestamp_ref,
rfu: [0u8; 7],
})
}
pub fn header_size(version: u8) -> Result<usize> {
match version {
2 => Ok(21),
3 => Ok(25),
4..=6 => Ok(27),
v => Err(Error::UnsupportedVersion(v)),
}
}
pub fn parse(data: &[u8]) -> Result<Self> {
let cursor = &mut Cursor::new(data);
let version = read_u8(cursor)?;
match version {
2 => {
let nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let total_size = read_u16(cursor)?;
let timestamp_ref = read_u8(cursor)?;
let mut rfu = [0u8; 7];
cursor.read_exact(&mut rfu).map_err(Error::Incomplete)?;
Ok(InputHeader {
version,
nav_bitmask,
extended_nav_bitmask: 0,
external_bitmask,
nav_data_size: 0,
total_size,
timestamp_ref,
rfu,
})
}
3 => {
let nav_bitmask = read_u32(cursor)?;
let extended_nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let total_size = read_u16(cursor)?;
let timestamp_ref = read_u8(cursor)?;
let mut rfu = [0u8; 7];
cursor.read_exact(&mut rfu).map_err(Error::Incomplete)?;
Ok(InputHeader {
version,
nav_bitmask,
extended_nav_bitmask,
external_bitmask,
nav_data_size: 0,
total_size,
timestamp_ref,
rfu,
})
}
4..=6 => {
let nav_bitmask = read_u32(cursor)?;
let extended_nav_bitmask = read_u32(cursor)?;
let external_bitmask = read_u32(cursor)?;
let nav_data_size = read_u16(cursor)?;
let total_size = read_u16(cursor)?;
let timestamp_ref = read_u8(cursor)?;
let mut rfu = [0u8; 7];
cursor.read_exact(&mut rfu).map_err(Error::Incomplete)?;
Ok(InputHeader {
version,
nav_bitmask,
extended_nav_bitmask,
external_bitmask,
nav_data_size,
total_size,
timestamp_ref,
rfu,
})
}
v => Err(Error::UnsupportedVersion(v)),
}
}
}
impl CommandHeader {
pub const HEADER_SIZE: usize = 5;
pub fn parse(data: &[u8]) -> Result<Self> {
let cursor = &mut Cursor::new(data);
let version = read_u8(cursor)?;
if version != 3 {
return Err(Error::UnsupportedVersion(version));
}
let total_size = read_u16(cursor)?;
Ok(CommandHeader {
version,
total_size,
})
}
}
impl AnswerHeader {
pub const HEADER_SIZE: usize = 5;
pub fn parse(data: &[u8]) -> Result<Self> {
let cursor = &mut Cursor::new(data);
let version = read_u8(cursor)?;
if version != 3 {
return Err(Error::UnsupportedVersion(version));
}
let total_size = read_u16(cursor)?;
Ok(AnswerHeader {
version,
total_size,
})
}
}
#[cfg(test)]
mod tests {
use crate::header::{AnswerHeader, CommandHeader, OutputHeader};
#[test]
fn test_command_header_parse() {
let data = [0x03, 0x00, 0x09];
let hdr = CommandHeader::parse(&data).unwrap();
assert_eq!(hdr.version, 3);
assert_eq!(hdr.total_size, 9);
}
#[test]
fn test_answer_header_parse() {
let data = [0x03, 0x00, 0x09];
let hdr = AnswerHeader::parse(&data).unwrap();
assert_eq!(hdr.version, 3);
assert_eq!(hdr.total_size, 9);
}
#[test]
fn test_output_header_v2() {
let mut buf = Vec::new();
buf.push(0x02);
buf.extend_from_slice(&0x0000_0001u32.to_be_bytes());
buf.extend_from_slice(&0x0000_0000u32.to_be_bytes());
buf.extend_from_slice(&21u16.to_be_bytes());
buf.extend_from_slice(&1000u32.to_be_bytes());
buf.extend_from_slice(&42u32.to_be_bytes());
let hdr = OutputHeader::parse(&buf).unwrap();
assert_eq!(hdr.version, 2);
assert_eq!(hdr.nav_bitmask, 1);
assert_eq!(hdr.extended_nav_bitmask, 0);
assert_eq!(hdr.counter, 42);
}
#[test]
fn test_output_header_v4() {
let mut buf = Vec::new();
buf.push(0x04);
buf.extend_from_slice(&0x0000_0003u32.to_be_bytes());
buf.extend_from_slice(&0x0000_0000u32.to_be_bytes());
buf.extend_from_slice(&0x0000_0000u32.to_be_bytes());
buf.extend_from_slice(&24u16.to_be_bytes());
buf.extend_from_slice(&51u16.to_be_bytes());
buf.extend_from_slice(&2000u32.to_be_bytes());
buf.extend_from_slice(&99u32.to_be_bytes());
let hdr = OutputHeader::parse(&buf).unwrap();
assert_eq!(hdr.version, 4);
assert_eq!(hdr.nav_bitmask, 3);
assert_eq!(hdr.nav_data_size, 24);
assert_eq!(hdr.total_size, 51);
assert_eq!(hdr.counter, 99);
}
}