use crate::error::{Error, Result};
use crate::traits::Descriptor;
use dvb_common::{Parse, Serialize};
pub const TAG: u8 = 0x63;
const HEADER_LEN: usize = 2;
const BODY_LEN: u8 = 8;
const MAX_22: u32 = (1 << 22) - 1;
const MAX_14: u16 = (1 << 14) - 1;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PartialTransportStreamDescriptor {
pub peak_rate: u32,
pub minimum_overall_smoothing_rate: u32,
pub maximum_overall_smoothing_buffer: u16,
}
impl<'a> Parse<'a> for PartialTransportStreamDescriptor {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < HEADER_LEN {
return Err(Error::BufferTooShort {
need: HEADER_LEN,
have: bytes.len(),
what: "PartialTransportStreamDescriptor header",
});
}
if bytes[0] != TAG {
return Err(Error::InvalidDescriptor {
tag: bytes[0],
reason: "unexpected tag for partial_transport_stream_descriptor",
});
}
let length = bytes[1] as usize;
let total = HEADER_LEN + length;
if bytes.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: bytes.len(),
what: "PartialTransportStreamDescriptor body",
});
}
if length != BODY_LEN as usize {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "partial_transport_stream_descriptor length must equal 8",
});
}
let b = HEADER_LEN;
let peak_rate = (u32::from(bytes[b] & 0x3F) << 16)
| (u32::from(bytes[b + 1]) << 8)
| u32::from(bytes[b + 2]);
let minimum_overall_smoothing_rate = (u32::from(bytes[b + 3] & 0x3F) << 16)
| (u32::from(bytes[b + 4]) << 8)
| u32::from(bytes[b + 5]);
let maximum_overall_smoothing_buffer =
(u16::from(bytes[b + 6] & 0x3F) << 8) | u16::from(bytes[b + 7]);
Ok(Self {
peak_rate,
minimum_overall_smoothing_rate,
maximum_overall_smoothing_buffer,
})
}
}
impl Serialize for PartialTransportStreamDescriptor {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
HEADER_LEN + BODY_LEN as usize
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if self.peak_rate > MAX_22 || self.minimum_overall_smoothing_rate > MAX_22 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "peak_rate / minimum_overall_smoothing_rate exceed 22 bits",
});
}
if self.maximum_overall_smoothing_buffer > MAX_14 {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "maximum_overall_smoothing_buffer exceeds 14 bits",
});
}
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
buf[0] = TAG;
buf[1] = BODY_LEN;
let b = HEADER_LEN;
buf[b] = 0xC0 | ((self.peak_rate >> 16) as u8 & 0x3F);
buf[b + 1] = (self.peak_rate >> 8) as u8;
buf[b + 2] = self.peak_rate as u8;
buf[b + 3] = 0xC0 | ((self.minimum_overall_smoothing_rate >> 16) as u8 & 0x3F);
buf[b + 4] = (self.minimum_overall_smoothing_rate >> 8) as u8;
buf[b + 5] = self.minimum_overall_smoothing_rate as u8;
buf[b + 6] = 0xC0 | ((self.maximum_overall_smoothing_buffer >> 8) as u8 & 0x3F);
buf[b + 7] = self.maximum_overall_smoothing_buffer as u8;
Ok(len)
}
}
impl<'a> Descriptor<'a> for PartialTransportStreamDescriptor {
const TAG: u8 = TAG;
fn descriptor_length(&self) -> u8 {
BODY_LEN
}
}
impl<'a> crate::traits::DescriptorDef<'a> for PartialTransportStreamDescriptor {
const TAG: u8 = TAG;
const NAME: &'static str = "PARTIAL_TRANSPORT_STREAM";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_extracts_fields_and_ignores_reserved() {
let bytes = [TAG, 8, 0xC1, 0x23, 0x45, 0xCA, 0xBC, 0xDE, 0xD2, 0x34];
let d = PartialTransportStreamDescriptor::parse(&bytes).unwrap();
assert_eq!(d.peak_rate, 0x01_2345);
assert_eq!(d.minimum_overall_smoothing_rate, 0x0A_BCDE);
assert_eq!(d.maximum_overall_smoothing_buffer, 0x1234);
}
#[test]
fn parse_rejects_wrong_tag() {
let err = PartialTransportStreamDescriptor::parse(&[0x64, 8, 0, 0, 0, 0, 0, 0, 0, 0])
.unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: 0x64, .. }));
}
#[test]
fn parse_rejects_short_buffer() {
let err = PartialTransportStreamDescriptor::parse(&[TAG]).unwrap_err();
assert!(matches!(err, Error::BufferTooShort { .. }));
}
#[test]
fn parse_rejects_truncated_body() {
let err = PartialTransportStreamDescriptor::parse(&[TAG, 8, 0, 0, 0, 0]).unwrap_err();
assert!(matches!(err, Error::BufferTooShort { .. }));
}
#[test]
fn parse_rejects_wrong_length() {
let err =
PartialTransportStreamDescriptor::parse(&[TAG, 7, 0, 0, 0, 0, 0, 0, 0]).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { .. }));
}
#[test]
fn serialize_round_trip() {
let d = PartialTransportStreamDescriptor {
peak_rate: 0x0F_FFFF,
minimum_overall_smoothing_rate: 0x00_0001,
maximum_overall_smoothing_buffer: 0x2ABC,
};
let mut buf = vec![0u8; d.serialized_len()];
d.serialize_into(&mut buf).unwrap();
let re = PartialTransportStreamDescriptor::parse(&buf).unwrap();
assert_eq!(d, re);
}
#[test]
fn serialize_emits_reserved_ones() {
let d = PartialTransportStreamDescriptor {
peak_rate: 0,
minimum_overall_smoothing_rate: 0,
maximum_overall_smoothing_buffer: 0,
};
let mut buf = vec![0u8; d.serialized_len()];
d.serialize_into(&mut buf).unwrap();
assert_eq!(buf[2] & 0xC0, 0xC0);
assert_eq!(buf[5] & 0xC0, 0xC0);
assert_eq!(buf[8] & 0xC0, 0xC0);
}
#[test]
fn serialize_rejects_too_small_buffer() {
let d = PartialTransportStreamDescriptor {
peak_rate: 1,
minimum_overall_smoothing_rate: 1,
maximum_overall_smoothing_buffer: 1,
};
let mut tiny = [0u8; 5];
let err = d.serialize_into(&mut tiny).unwrap_err();
assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
}
#[test]
fn serialize_rejects_over_range_22bit() {
let d = PartialTransportStreamDescriptor {
peak_rate: 1 << 22, minimum_overall_smoothing_rate: 0,
maximum_overall_smoothing_buffer: 0,
};
let mut buf = vec![0u8; d.serialized_len()];
let err = d.serialize_into(&mut buf).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
}
#[test]
fn serialize_rejects_over_range_14bit() {
let d = PartialTransportStreamDescriptor {
peak_rate: 0,
minimum_overall_smoothing_rate: 0,
maximum_overall_smoothing_buffer: 1 << 14, };
let mut buf = vec![0u8; d.serialized_len()];
let err = d.serialize_into(&mut buf).unwrap_err();
assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
}
#[test]
fn descriptor_length_matches_payload() {
let d = PartialTransportStreamDescriptor {
peak_rate: 0,
minimum_overall_smoothing_rate: 0,
maximum_overall_smoothing_buffer: 0,
};
assert_eq!(d.descriptor_length(), 8);
}
#[cfg(feature = "serde")]
#[test]
fn serde_round_trip() {
let d = PartialTransportStreamDescriptor {
peak_rate: 0x00_1234,
minimum_overall_smoothing_rate: 0x00_5678,
maximum_overall_smoothing_buffer: 0x09AB,
};
let json = serde_json::to_string(&d).unwrap();
let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
}
}