use crate::error::{Error, Result};
use broadcast_common::len::{need_in, need_out};
use broadcast_common::{Parse, Serialize};
pub const SEGMENT_TYPE: u8 = 0x14;
pub const HEADER_LEN: usize = 6;
pub const FIXED_BODY_LEN: usize = 5;
pub const WINDOW_LEN: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DisplayDefinitionSegment {
pub page_id: u16,
pub dds_version_number: u8,
pub display_window_flag: bool,
pub reserved: u8,
pub display_width: u16,
pub display_height: u16,
pub display_window_horizontal_position_minimum: Option<u16>,
pub display_window_horizontal_position_maximum: Option<u16>,
pub display_window_vertical_position_minimum: Option<u16>,
pub display_window_vertical_position_maximum: Option<u16>,
}
impl<'a> Parse<'a> for DisplayDefinitionSegment {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let min = HEADER_LEN + FIXED_BODY_LEN;
need_in(bytes, min, "display_definition_segment")?;
if bytes[0] != crate::pes_data_field::SYNC_BYTE {
return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
what: "sync_byte",
expected: u32::from(crate::pes_data_field::SYNC_BYTE),
found: u32::from(bytes[0]),
}));
}
if bytes[1] != SEGMENT_TYPE {
return Err(Error::UnknownSegmentType(bytes[1]));
}
let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
let total = HEADER_LEN + segment_length;
need_in(bytes, total, "display_definition_segment data")?;
let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
need_in(body, FIXED_BODY_LEN, "display_definition_segment body")?;
let b0 = body[0];
let dds_version_number = b0 >> 4;
let display_window_flag = (b0 & 0x08) != 0;
let reserved = b0 & 0x07;
let display_width = u16::from_be_bytes([body[1], body[2]]);
let display_height = u16::from_be_bytes([body[3], body[4]]);
if display_window_flag {
need_in(
body,
FIXED_BODY_LEN + WINDOW_LEN,
"display_definition_segment window",
)?;
let dw = &body[FIXED_BODY_LEN..FIXED_BODY_LEN + WINDOW_LEN];
Ok(DisplayDefinitionSegment {
page_id,
dds_version_number,
display_window_flag,
reserved,
display_width,
display_height,
display_window_horizontal_position_minimum: Some(u16::from_be_bytes([
dw[0], dw[1],
])),
display_window_horizontal_position_maximum: Some(u16::from_be_bytes([
dw[2], dw[3],
])),
display_window_vertical_position_minimum: Some(u16::from_be_bytes([dw[4], dw[5]])),
display_window_vertical_position_maximum: Some(u16::from_be_bytes([dw[6], dw[7]])),
})
} else {
Ok(DisplayDefinitionSegment {
page_id,
dds_version_number,
display_window_flag,
reserved,
display_width,
display_height,
display_window_horizontal_position_minimum: None,
display_window_horizontal_position_maximum: None,
display_window_vertical_position_minimum: None,
display_window_vertical_position_maximum: None,
})
}
}
}
impl Serialize for DisplayDefinitionSegment {
type Error = Error;
fn serialized_len(&self) -> usize {
HEADER_LEN
+ FIXED_BODY_LEN
+ if self.display_window_flag {
WINDOW_LEN
} else {
0
}
}
fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
let len = self.serialized_len();
need_out(buf, len)?;
buf[0] = 0x0F; buf[1] = SEGMENT_TYPE;
buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
buf[6] =
(broadcast_common::len::fit_mask(self.dds_version_number, 0x0F, "dds_version_number")?
<< 4)
| (u8::from(self.display_window_flag) << 3)
| broadcast_common::len::fit_mask(self.reserved, 0x07, "reserved")?;
buf[7..9].copy_from_slice(&self.display_width.to_be_bytes());
buf[9..11].copy_from_slice(&self.display_height.to_be_bytes());
if self.display_window_flag {
let wmin = self.display_window_horizontal_position_minimum.unwrap_or(0);
let wmax = self.display_window_horizontal_position_maximum.unwrap_or(0);
let vmin = self.display_window_vertical_position_minimum.unwrap_or(0);
let vmax = self.display_window_vertical_position_maximum.unwrap_or(0);
buf[11..13].copy_from_slice(&wmin.to_be_bytes());
buf[13..15].copy_from_slice(&wmax.to_be_bytes());
buf[15..17].copy_from_slice(&vmin.to_be_bytes());
buf[17..19].copy_from_slice(&vmax.to_be_bytes());
}
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use broadcast_common::{Parse, Serialize};
#[test]
fn round_trip_no_window() {
let bytes = [
0x0F, 0x14, 0x00, 0x01, 0x00, 0x05, 0x30, 0x02, 0xCF, 0x01, 0x1F,
];
let seg = DisplayDefinitionSegment::parse(&bytes).unwrap();
assert_eq!(seg.dds_version_number, 3);
assert!(!seg.display_window_flag);
assert_eq!(seg.display_width, 719);
assert_eq!(seg.display_height, 287);
let out = seg.try_to_bytes().unwrap();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
seg2.display_width = 100;
let out2 = seg2.try_to_bytes().unwrap();
assert_ne!(out2, bytes);
let reparse = DisplayDefinitionSegment::parse(&out2).unwrap();
assert_eq!(reparse.display_width, 100);
}
#[test]
fn rejects_bad_sync_byte() {
let bytes = [
0x00, 0x14, 0x00, 0x01, 0x00, 0x05, 0x30, 0x02, 0xCF, 0x01, 0x1F,
];
assert!(matches!(
DisplayDefinitionSegment::parse(&bytes),
Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
found: 0x00,
..
}))
));
}
#[test]
fn round_trip_with_window() {
let bytes = [
0x0F, 0x14, 0x00, 0x01, 0x00, 0x0D, 0x48, 0x02, 0xCF, 0x01, 0x1F, 0x00, 0x32, 0x02,
0x9E, 0x00, 0x14, 0x01, 0x0C,
];
let seg = DisplayDefinitionSegment::parse(&bytes).unwrap();
assert_eq!(seg.dds_version_number, 4);
assert!(seg.display_window_flag);
assert_eq!(seg.display_window_horizontal_position_minimum, Some(50));
assert_eq!(seg.display_window_horizontal_position_maximum, Some(670));
let out = seg.try_to_bytes().unwrap();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
seg2.display_window_flag = false;
let out2 = seg2.try_to_bytes().unwrap();
assert_ne!(out2.len(), out.len());
let reparse = DisplayDefinitionSegment::parse(&out2).unwrap();
assert!(!reparse.display_window_flag);
}
}