dvb_t2mi/payload/
arbitrary_cells.rs1use dvb_common::{Parse, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
17pub struct ArbitraryCellsPayload<'a> {
18 pub frame_idx: u8,
20 pub tx_identifier: u16,
22 pub start_cell_address: u32,
24 pub arbitrary_cell_data: &'a [u8],
26}
27
28const ARB_CELLS_HEADER_LEN: usize = 8;
29const CELL_ADDR_MASK: u32 = 0x003F_FFFF; impl<'a> Parse<'a> for ArbitraryCellsPayload<'a> {
32 type Error = crate::error::Error;
33
34 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
35 if bytes.len() < ARB_CELLS_HEADER_LEN {
36 return Err(crate::Error::BufferTooShort {
37 need: ARB_CELLS_HEADER_LEN,
38 have: bytes.len(),
39 what: "ArbitraryCellsPayload header",
40 });
41 }
42
43 let frame_idx = bytes[0];
44 let tx_identifier = u16::from_be_bytes([bytes[1], bytes[2]]);
45
46 if bytes[3] != 0 || bytes[4] != 0 || (bytes[5] & 0xC0 != 0) {
48 return Err(crate::Error::ReservedBitsViolation {
49 field: "18-bit RFU",
50 reason: "Must be zero (ETSI TS 102 773 §5.2.3)",
51 });
52 }
53
54 let start_cell_address =
56 ((bytes[5] as u32 & 0x3F) << 16) | ((bytes[6] as u32) << 8) | (bytes[7] as u32);
57
58 Ok(ArbitraryCellsPayload {
59 frame_idx,
60 tx_identifier,
61 start_cell_address,
62 arbitrary_cell_data: &bytes[ARB_CELLS_HEADER_LEN..],
63 })
64 }
65}
66
67impl<'a> crate::traits::PayloadDef<'a> for ArbitraryCellsPayload<'a> {
68 const PACKET_TYPE: u8 = 0x02;
69 const NAME: &'static str = "ARBITRARY_CELLS";
70}
71
72impl Serialize for ArbitraryCellsPayload<'_> {
73 type Error = crate::error::Error;
74
75 fn serialized_len(&self) -> usize {
76 ARB_CELLS_HEADER_LEN + self.arbitrary_cell_data.len()
77 }
78
79 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
80 if buf.len() < self.serialized_len() {
81 return Err(crate::Error::OutputBufferTooSmall {
82 need: self.serialized_len(),
83 have: buf.len(),
84 });
85 }
86
87 if self.start_cell_address > CELL_ADDR_MASK {
88 return Err(crate::Error::ReservedBitsViolation {
89 field: "start_cell_address",
90 reason: "Must fit in 22 bits",
91 });
92 }
93
94 buf[0] = self.frame_idx;
95 let tx_id = self.tx_identifier.to_be_bytes();
96 buf[1] = tx_id[0];
97 buf[2] = tx_id[1];
98 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;
102 buf[7] = (self.start_cell_address & 0xFF) as u8;
103
104 if !self.arbitrary_cell_data.is_empty() {
105 buf[ARB_CELLS_HEADER_LEN..ARB_CELLS_HEADER_LEN + self.arbitrary_cell_data.len()]
106 .copy_from_slice(self.arbitrary_cell_data);
107 }
108
109 Ok(self.serialized_len())
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn parse_extracts_fields() {
119 let data = [0x10, 0xCA, 0xFE];
120 let mut buf = vec![0x42u8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
121 buf.extend_from_slice(&data);
122
123 let result = ArbitraryCellsPayload::parse(&buf).unwrap();
124 assert_eq!(result.frame_idx, 0x42);
125 assert_eq!(result.tx_identifier, 0x0001);
126 assert_eq!(result.start_cell_address, 0);
127 assert_eq!(result.arbitrary_cell_data, &data[..]);
128 }
129
130 #[test]
131 fn parse_rejects_short_buffer() {
132 let buf = [0x00u8; 7];
133 assert!(ArbitraryCellsPayload::parse(&buf).is_err());
134 }
135
136 #[test]
137 fn parse_rejects_nonzero_rfu_byte_3() {
138 let buf = [0x00u8, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
139 assert!(ArbitraryCellsPayload::parse(&buf).is_err());
140 }
141
142 #[test]
143 fn parse_rejects_nonzero_rfu_byte_5_high_bits() {
144 let mut buf = [0x00u8; 8];
145 buf[5] = 0xC0; assert!(ArbitraryCellsPayload::parse(&buf).is_err());
147 }
148
149 #[test]
150 fn serialize_round_trip() {
151 let orig = ArbitraryCellsPayload {
152 frame_idx: 0xAB,
153 tx_identifier: 0x0005,
154 start_cell_address: 0x123456,
155 arbitrary_cell_data: &[0xDE, 0xAD],
156 };
157 let mut buf = vec![0u8; orig.serialized_len()];
158 orig.serialize_into(&mut buf).unwrap();
159 let parsed = ArbitraryCellsPayload::parse(&buf).unwrap();
160 assert_eq!(orig, parsed);
161 }
162
163 #[test]
164 fn tx_id_broadcast_accepted() {
165 let buf = [0x00u8; 8];
166 let result = ArbitraryCellsPayload::parse(&buf).unwrap();
167 assert_eq!(result.tx_identifier, 0x0000);
168 }
169
170 #[test]
171 fn cell_address_max_22bit() {
172 let max_addr: u32 = 0x3F_FFFF;
173 let buf = [0x00u8, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF];
174 let result = ArbitraryCellsPayload::parse(&buf).unwrap();
175 assert_eq!(result.start_cell_address, max_addr);
176 }
177
178 #[test]
179 fn serialize_rejects_too_small_buffer() {
180 let payload = ArbitraryCellsPayload {
181 frame_idx: 0,
182 tx_identifier: 0,
183 start_cell_address: 0,
184 arbitrary_cell_data: &[],
185 };
186 let mut buf = [0u8; 7];
187 assert!(payload.serialize_into(&mut buf).is_err());
188 }
189}