use crate::descriptors::mjd_time::{MJD_BCD_LEN, MjdBcdTime};
use crate::descriptors::{descriptor_body, write_descriptor_header};
use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const TAG: u8 = 0x40;
const HEADER_LEN: usize = 2;
const FIXED_LEN: usize = 2 * MJD_BCD_LEN + 1 + 2 + 2 + 2 + 1;
const SCHEDULE_LEN: usize = MJD_BCD_LEN + 2;
const WEIGHTING_SHIFT: u8 = 2;
const WEIGHTING_MASK: u8 = 0x3F;
const COMPLETE_FLAG: u8 = 0x02;
const SCHEDULED_FLAG: u8 = 0x01;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RarDownloadSchedule {
pub download_start_time: MjdBcdTime,
pub download_period_duration: u8,
pub download_cycle_time: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct RarOverDvbStreamDescriptor<'a> {
pub first_valid_date: MjdBcdTime,
pub last_valid_date: MjdBcdTime,
pub weighting: u8,
pub complete_flag: bool,
pub transport_stream_id: u16,
pub original_network_id: u16,
pub service_id: u16,
pub component_tag: u8,
pub schedule: Option<RarDownloadSchedule>,
pub trailing: &'a [u8],
}
impl<'a> Parse<'a> for RarOverDvbStreamDescriptor<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = descriptor_body(
bytes,
TAG,
"RarOverDvbStreamDescriptor",
"unexpected tag for RAR_over_DVB_stream_descriptor",
)?;
if body.len() < FIXED_LEN {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "RAR_over_DVB_stream_descriptor body shorter than 18 bytes",
});
}
let flags = body[2 * MJD_BCD_LEN];
let be16 = |o: usize| u16::from_be_bytes([body[o], body[o + 1]]);
let o = 2 * MJD_BCD_LEN + 1;
let (schedule, trailing_start) = if flags & SCHEDULED_FLAG != 0 {
if body.len() < FIXED_LEN + SCHEDULE_LEN {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "scheduled_flag set but the download schedule is truncated",
});
}
(
Some(RarDownloadSchedule {
download_start_time: MjdBcdTime::read(body, FIXED_LEN, "download_start_time")?,
download_period_duration: body[FIXED_LEN + MJD_BCD_LEN],
download_cycle_time: body[FIXED_LEN + MJD_BCD_LEN + 1],
}),
FIXED_LEN + SCHEDULE_LEN,
)
} else {
(None, FIXED_LEN)
};
Ok(Self {
first_valid_date: MjdBcdTime::read(body, 0, "first_valid_date")?,
last_valid_date: MjdBcdTime::read(body, MJD_BCD_LEN, "last_valid_date")?,
weighting: flags >> WEIGHTING_SHIFT,
complete_flag: flags & COMPLETE_FLAG != 0,
transport_stream_id: be16(o),
original_network_id: be16(o + 2),
service_id: be16(o + 4),
component_tag: body[o + 6],
schedule,
trailing: &body[trailing_start..],
})
}
}
impl Serialize for RarOverDvbStreamDescriptor<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
HEADER_LEN + FIXED_LEN + self.schedule.map_or(0, |_| SCHEDULE_LEN) + self.trailing.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
let weighting =
broadcast_common::len::fit_mask(self.weighting, WEIGHTING_MASK, "weighting")?;
write_descriptor_header(buf, TAG, len - HEADER_LEN)?;
let b = &mut buf[HEADER_LEN..];
self.first_valid_date.write(b, 0);
self.last_valid_date.write(b, MJD_BCD_LEN);
let mut flags = weighting << WEIGHTING_SHIFT;
if self.complete_flag {
flags |= COMPLETE_FLAG;
}
if self.schedule.is_some() {
flags |= SCHEDULED_FLAG;
}
let o = 2 * MJD_BCD_LEN;
b[o] = flags;
b[o + 1..o + 3].copy_from_slice(&self.transport_stream_id.to_be_bytes());
b[o + 3..o + 5].copy_from_slice(&self.original_network_id.to_be_bytes());
b[o + 5..o + 7].copy_from_slice(&self.service_id.to_be_bytes());
b[o + 7] = self.component_tag;
let mut end = FIXED_LEN;
if let Some(s) = self.schedule {
s.download_start_time.write(b, end);
b[end + MJD_BCD_LEN] = s.download_period_duration;
b[end + MJD_BCD_LEN + 1] = s.download_cycle_time;
end += SCHEDULE_LEN;
}
b[end..end + self.trailing.len()].copy_from_slice(self.trailing);
Ok(len)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for RarOverDvbStreamDescriptor<'a> {
const TAG: u8 = TAG;
const NAME: &'static str = "RAR_OVER_DVB_STREAM";
}
#[cfg(test)]
mod tests {
use super::*;
fn sample(scheduled: bool) -> Vec<u8> {
let mut v = vec![TAG, 0];
v.extend_from_slice(&[0xC0, 0x79, 0x12, 0x00, 0x00]); v.extend_from_slice(&[0xC0, 0x80, 0x23, 0x59, 0x59]); v.push((5 << 2) | 0x02 | u8::from(scheduled));
v.extend_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0x21]);
if scheduled {
v.extend_from_slice(&[0xC0, 0x79, 0x03, 0x30, 0x00, 10, 15]);
}
v[1] = (v.len() - 2) as u8;
v
}
#[test]
fn parse_and_round_trip_unscheduled() {
let bytes = sample(false);
let d = RarOverDvbStreamDescriptor::parse(&bytes).unwrap();
assert_eq!(d.weighting, 5);
assert!(d.complete_flag && d.schedule.is_none());
assert_eq!(
(
d.transport_stream_id,
d.original_network_id,
d.service_id,
d.component_tag
),
(0x1234, 0x5678, 0x9ABC, 0x21)
);
assert_eq!(
d.first_valid_date.parts(),
(0xC079, Some(12), Some(0), Some(0))
);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
#[test]
fn parse_and_round_trip_scheduled() {
let bytes = sample(true);
let d = RarOverDvbStreamDescriptor::parse(&bytes).unwrap();
let s = d.schedule.unwrap();
assert_eq!(
s.download_start_time.parts(),
(0xC079, Some(3), Some(30), Some(0))
);
assert_eq!(
(s.download_period_duration, s.download_cycle_time),
(10, 15)
);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
#[test]
fn trailing_round_trip_and_errors() {
let mut bytes = sample(false);
bytes.push(0xEE);
bytes[1] += 1;
let d = RarOverDvbStreamDescriptor::parse(&bytes).unwrap();
assert_eq!(d.trailing, &[0xEE]);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
let mut trunc = sample(true);
trunc.truncate(trunc.len() - 3);
trunc[1] -= 3;
assert!(RarOverDvbStreamDescriptor::parse(&trunc).is_err());
assert!(RarOverDvbStreamDescriptor::parse(&[TAG, 3, 0, 0, 0]).is_err());
assert!(RarOverDvbStreamDescriptor::parse(&[0x41, 0]).is_err());
}
#[test]
fn serialize_rejects_over_range_weighting() {
let bytes = sample(false);
let mut d = RarOverDvbStreamDescriptor::parse(&bytes).unwrap();
d.weighting = 64;
let mut out = vec![0u8; d.serialized_len()];
assert!(d.serialize_into(&mut out).is_err());
}
}