use dvb_common::{Parse, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct ArbitraryCellsPayload<'a> {
pub frame_idx: u8,
pub tx_identifier: u16,
pub start_cell_address: u32,
pub arbitrary_cell_data: &'a [u8],
}
const ARB_CELLS_HEADER_LEN: usize = 8;
const CELL_ADDR_MASK: u32 = 0x003F_FFFF;
impl<'a> Parse<'a> for ArbitraryCellsPayload<'a> {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
if bytes.len() < ARB_CELLS_HEADER_LEN {
return Err(crate::Error::BufferTooShort {
need: ARB_CELLS_HEADER_LEN,
have: bytes.len(),
what: "ArbitraryCellsPayload header",
});
}
let frame_idx = bytes[0];
let tx_identifier = u16::from_be_bytes([bytes[1], bytes[2]]);
if bytes[3] != 0 || bytes[4] != 0 || (bytes[5] & 0xC0 != 0) {
return Err(crate::Error::ReservedBitsViolation {
field: "18-bit RFU",
reason: "Must be zero (ETSI TS 102 773 §5.2.3)",
});
}
let start_cell_address =
((bytes[5] as u32 & 0x3F) << 16) | ((bytes[6] as u32) << 8) | (bytes[7] as u32);
Ok(ArbitraryCellsPayload {
frame_idx,
tx_identifier,
start_cell_address,
arbitrary_cell_data: &bytes[ARB_CELLS_HEADER_LEN..],
})
}
}
impl<'a> crate::traits::PayloadDef<'a> for ArbitraryCellsPayload<'a> {
const PACKET_TYPE: u8 = 0x02;
const NAME: &'static str = "ARBITRARY_CELLS";
}
impl Serialize for ArbitraryCellsPayload<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
ARB_CELLS_HEADER_LEN + self.arbitrary_cell_data.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
if buf.len() < self.serialized_len() {
return Err(crate::Error::OutputBufferTooSmall {
need: self.serialized_len(),
have: buf.len(),
});
}
if self.start_cell_address > CELL_ADDR_MASK {
return Err(crate::Error::ReservedBitsViolation {
field: "start_cell_address",
reason: "Must fit in 22 bits",
});
}
buf[0] = self.frame_idx;
let tx_id = self.tx_identifier.to_be_bytes();
buf[1] = tx_id[0];
buf[2] = tx_id[1];
buf[3] = 0; buf[4] = 0; buf[5] = ((self.start_cell_address >> 16) & 0x3F) as u8; buf[6] = ((self.start_cell_address >> 8) & 0xFF) as u8;
buf[7] = (self.start_cell_address & 0xFF) as u8;
if !self.arbitrary_cell_data.is_empty() {
buf[ARB_CELLS_HEADER_LEN..ARB_CELLS_HEADER_LEN + self.arbitrary_cell_data.len()]
.copy_from_slice(self.arbitrary_cell_data);
}
Ok(self.serialized_len())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_extracts_fields() {
let data = [0x10, 0xCA, 0xFE];
let mut buf = vec![0x42u8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
buf.extend_from_slice(&data);
let result = ArbitraryCellsPayload::parse(&buf).unwrap();
assert_eq!(result.frame_idx, 0x42);
assert_eq!(result.tx_identifier, 0x0001);
assert_eq!(result.start_cell_address, 0);
assert_eq!(result.arbitrary_cell_data, &data[..]);
}
#[test]
fn parse_rejects_short_buffer() {
let buf = [0x00u8; 7];
assert!(ArbitraryCellsPayload::parse(&buf).is_err());
}
#[test]
fn parse_rejects_nonzero_rfu_byte_3() {
let buf = [0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
assert!(ArbitraryCellsPayload::parse(&buf).is_err());
}
#[test]
fn parse_rejects_nonzero_rfu_byte_5_high_bits() {
let mut buf = [0x00u8; 8];
buf[5] = 0xC0; assert!(ArbitraryCellsPayload::parse(&buf).is_err());
}
#[test]
fn serialize_round_trip() {
let orig = ArbitraryCellsPayload {
frame_idx: 0xAB,
tx_identifier: 0x0005,
start_cell_address: 0x123456,
arbitrary_cell_data: &[0xDE, 0xAD],
};
let mut buf = vec![0u8; orig.serialized_len()];
orig.serialize_into(&mut buf).unwrap();
let parsed = ArbitraryCellsPayload::parse(&buf).unwrap();
assert_eq!(orig, parsed);
}
#[test]
fn tx_id_broadcast_accepted() {
let buf = [0x00u8; 8];
let result = ArbitraryCellsPayload::parse(&buf).unwrap();
assert_eq!(result.tx_identifier, 0x0000);
}
#[test]
fn cell_address_max_22bit() {
let max_addr: u32 = 0x3F_FFFF;
let buf = [0x00u8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF];
let result = ArbitraryCellsPayload::parse(&buf).unwrap();
assert_eq!(result.start_cell_address, max_addr);
}
#[test]
fn serialize_rejects_too_small_buffer() {
let payload = ArbitraryCellsPayload {
frame_idx: 0,
tx_identifier: 0,
start_cell_address: 0,
arbitrary_cell_data: &[],
};
let mut buf = [0u8; 7];
assert!(payload.serialize_into(&mut buf).is_err());
}
}