pub mod packet_type;
pub use packet_type::*;
pub mod pixel_config;
pub use pixel_config::{DataType, PixelConfig, PixelFormat};
pub mod id;
pub use id::ID;
pub mod message;
pub mod timecode;
use timecode::TimeCode;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
pub struct Header {
pub packet_type: PacketType,
pub sequence_number: u8,
pub pixel_config: PixelConfig,
pub id: ID,
pub offset: u32,
pub length: u16,
pub time_code: TimeCode,
}
impl Into<[u8; 10]> for Header {
fn into(self) -> [u8; 10] {
let mut buffer: [u8; 10] = [0u8; 10];
let packet_type_byte: u8 = self.packet_type.into();
buffer[0] = packet_type_byte;
buffer[1] = self.sequence_number;
buffer[2] = self.pixel_config.into();
buffer[3] = self.id.into();
let offset_bytes = self.offset.to_be_bytes();
buffer[4..8].copy_from_slice(&offset_bytes);
let length_bytes = self.length.to_be_bytes();
buffer[8..10].copy_from_slice(&length_bytes);
buffer
}
}
impl Into<[u8; 14]> for Header {
fn into(self) -> [u8; 14] {
let mut buffer = [0u8; 14];
let packet_type_byte: u8 = self.packet_type.into();
buffer[0] = packet_type_byte;
buffer[1] = self.sequence_number;
buffer[2] = self.pixel_config.into();
buffer[3] = self.id.into();
let offset_bytes: [u8; 4] = self.offset.to_be_bytes();
buffer[4..8].copy_from_slice(&offset_bytes);
let length_bytes: [u8; 2] = self.length.to_be_bytes();
buffer[8..10].copy_from_slice(&length_bytes);
let time_code: [u8; 4] = self.time_code.to_bytes();
buffer[10..14].copy_from_slice(&time_code);
buffer
}
}
impl<'a> From<&'a [u8]> for Header {
fn from(bytes: &'a [u8]) -> Self {
let packet_type = PacketType::from(bytes[0]);
let sequence_number = bytes[1];
let pixel_config = PixelConfig::from(bytes[2]);
let id = ID::from(bytes[3]);
let offset = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let length = u16::from_be_bytes([bytes[8], bytes[9]]);
if packet_type.timecode && bytes.len() >= 14 {
let time_code = TimeCode::from_4_bytes([bytes[10], bytes[11], bytes[12], bytes[13]]);
Header {
packet_type,
sequence_number,
pixel_config,
id,
offset,
length,
time_code,
}
} else {
Header {
packet_type,
sequence_number,
pixel_config,
id,
offset,
length,
time_code: TimeCode(None),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parsing() {
{
let data: [u8; 10] = [65, 6, 10, 1, 0, 0, 0, 0, 0, 3];
let header = Header::from(&data[..]);
assert_eq!(
header.packet_type,
PacketType {
version: 1,
timecode: false,
storage: false,
reply: false,
query: false,
push: true
}
);
assert_eq!(header.sequence_number, 6);
assert_eq!(header.length, 3);
assert_eq!(header.offset, 0);
}
{
let data: [u8; 10] = [255, 12, 13, 1, 0, 0, 0x99, 0xd5, 0x01, 0x19];
let header = Header::from(&data[..]);
assert_eq!(
header.packet_type,
PacketType {
version: 3,
timecode: true,
storage: true,
reply: true,
query: true,
push: true
}
);
assert_eq!(header.sequence_number, 12);
assert_eq!(
header.pixel_config,
PixelConfig {
data_type: pixel_config::DataType::RGB,
data_size: PixelFormat::Pixel24Bits,
customer_defined: false
}
);
assert_eq!(header.length, 281);
assert_eq!(header.offset, 39381);
}
}
use proptest::prelude::*;
proptest! {
#[test]
fn test_header_10_byte_roundtrip(
packet_type_byte in any::<u8>(),
seq_num in any::<u8>(),
pixel_config in any::<u8>(),
id in any::<u8>(),
offset in any::<u32>(),
length in any::<u16>(),
) {
let mut bytes = vec![packet_type_byte, seq_num, pixel_config, id];
bytes.extend_from_slice(&offset.to_be_bytes());
bytes.extend_from_slice(&length.to_be_bytes());
let header = Header::from(&bytes[..]);
let roundtrip_bytes: [u8; 10] = header.into();
prop_assert_eq!(header.sequence_number, seq_num);
prop_assert_eq!(header.offset, offset);
prop_assert_eq!(header.length, length);
let roundtrip_header = Header::from(&roundtrip_bytes[..]);
prop_assert_eq!(header.sequence_number, roundtrip_header.sequence_number);
prop_assert_eq!(header.offset, roundtrip_header.offset);
prop_assert_eq!(header.length, roundtrip_header.length);
}
#[test]
fn test_header_14_byte_with_timecode_roundtrip(
seq_num in any::<u8>(),
pixel_config in any::<u8>(),
id in any::<u8>(),
offset in any::<u32>(),
length in any::<u16>(),
timecode in any::<u32>(),
) {
let packet_type_byte = 0b01010000u8; let mut bytes = vec![packet_type_byte, seq_num, pixel_config, id];
bytes.extend_from_slice(&offset.to_be_bytes());
bytes.extend_from_slice(&length.to_be_bytes());
bytes.extend_from_slice(&timecode.to_be_bytes());
let header = Header::from(&bytes[..]);
prop_assert_eq!(header.time_code.0, Some(timecode));
prop_assert_eq!(header.sequence_number, seq_num);
prop_assert_eq!(header.offset, offset);
prop_assert_eq!(header.length, length);
prop_assert!(header.packet_type.timecode);
let roundtrip_bytes: [u8; 14] = header.into();
let roundtrip_header = Header::from(&roundtrip_bytes[..]);
prop_assert_eq!(header.time_code, roundtrip_header.time_code);
prop_assert_eq!(header.sequence_number, roundtrip_header.sequence_number);
}
#[test]
fn test_header_parsing_never_panics(
bytes in prop::collection::vec(any::<u8>(), 10..20)
) {
let _ = Header::from(&bytes[..]);
}
#[test]
fn test_header_offset_range(
offset in 0u32..=0xFFFFFFFF,
) {
let mut header = Header::default();
header.offset = offset;
let bytes: [u8; 10] = header.into();
let parsed = Header::from(&bytes[..]);
prop_assert_eq!(parsed.offset, offset);
}
#[test]
fn test_header_length_range(
length in 0u16..=1500,
) {
let mut header = Header::default();
header.length = length;
let bytes: [u8; 10] = header.into();
let parsed = Header::from(&bytes[..]);
prop_assert_eq!(parsed.length, length);
}
}
}