1use crate::error::{Error, Result};
34use broadcast_common::{Parse, Serialize};
35
36pub const TABLE_ID: u8 = 0x75;
38
39const HEADER_LEN: usize = 3;
45
46const EXTENSION_HEADER_LEN: usize = 5;
49
50use super::CRC_LEN;
51
52const MIN_LEN: usize = HEADER_LEN + EXTENSION_HEADER_LEN + CRC_LEN;
54
55#[derive(Debug, Clone, PartialEq, Eq)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize))]
61#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
62#[non_exhaustive]
63pub struct ContainerSection<'a> {
64 pub section_syntax_indicator: bool,
68 pub private_indicator: bool,
70 pub container_id: u16,
72 pub version_number: u8,
74 pub current_next_indicator: bool,
76 pub section_number: u8,
78 pub last_section_number: u8,
80 pub container_data: &'a [u8],
83 #[cfg_attr(feature = "serde", serde(skip))]
85 pub reserved_b1: u8,
86 #[cfg_attr(feature = "serde", serde(skip))]
88 pub reserved_b5: u8,
89}
90
91impl<'a> ContainerSection<'a> {
92 #[must_use]
94 pub fn new(
95 private_indicator: bool,
96 container_id: u16,
97 version_number: u8,
98 current_next_indicator: bool,
99 section_number: u8,
100 last_section_number: u8,
101 container_data: &'a [u8],
102 ) -> Self {
103 Self {
104 section_syntax_indicator: true,
105 private_indicator,
106 container_id,
107 version_number,
108 current_next_indicator,
109 section_number,
110 last_section_number,
111 container_data,
112 reserved_b1: super::RESERVED_2,
113 reserved_b5: super::RESERVED_2,
114 }
115 }
116}
117
118impl<'a> Parse<'a> for ContainerSection<'a> {
119 type Error = crate::error::Error;
120
121 fn parse(bytes: &'a [u8]) -> Result<Self> {
122 if bytes.len() < MIN_LEN {
123 return Err(Error::BufferTooShort {
124 need: MIN_LEN,
125 have: bytes.len(),
126 what: "ContainerSection",
127 });
128 }
129
130 if bytes[0] != TABLE_ID {
131 return Err(Error::UnexpectedTableId {
132 table_id: bytes[0],
133 what: "ContainerSection",
134 expected: &[TABLE_ID],
135 });
136 }
137
138 let section_length = super::section_length_of(bytes);
142 let total = super::check_section_length(bytes.len(), HEADER_LEN, section_length, MIN_LEN)?;
143
144 let private_indicator = (bytes[1] & super::SECTION_B1_PRIVATE_INDICATOR) != 0;
145
146 let container_id = crate::wire::be_u16(bytes, 3, "container")?;
148 let version_number = super::version_number_of(bytes[5]);
150 let current_next_indicator = super::current_next_of(bytes[5]);
151 let section_number = bytes[6];
152 let last_section_number = bytes[7];
153
154 let data_start = HEADER_LEN + EXTENSION_HEADER_LEN;
156 let data_end = total - CRC_LEN;
157 let container_data = &bytes[data_start..data_end];
158
159 Ok(ContainerSection {
160 section_syntax_indicator: bytes[1] & super::SECTION_B1_SSI != 0,
161 private_indicator,
162 container_id,
163 version_number,
164 current_next_indicator,
165 section_number,
166 last_section_number,
167 container_data,
168 reserved_b1: super::b1_reserved_of(bytes[1]),
169 reserved_b5: super::b5_reserved_of(bytes[5]),
170 })
171 }
172}
173
174impl Serialize for ContainerSection<'_> {
175 type Error = crate::error::Error;
176
177 fn serialized_len(&self) -> usize {
178 HEADER_LEN + EXTENSION_HEADER_LEN + self.container_data.len() + CRC_LEN
179 }
180
181 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
182 let len = self.serialized_len();
183 if buf.len() < len {
184 return Err(Error::OutputBufferTooSmall {
185 need: len,
186 have: buf.len(),
187 });
188 }
189
190 buf[0] = TABLE_ID;
192 buf[1] = super::b1_of(
195 self.section_syntax_indicator,
196 self.private_indicator,
197 self.reserved_b1,
198 )?;
199 super::write_section_length(buf, len - HEADER_LEN)?;
201
202 buf[3..5].copy_from_slice(&self.container_id.to_be_bytes());
204 buf[5] = super::b5_of(
205 self.reserved_b5,
206 self.version_number,
207 self.current_next_indicator,
208 )?;
209 buf[6] = self.section_number;
210 buf[7] = self.last_section_number;
211
212 let data_start = HEADER_LEN + EXTENSION_HEADER_LEN;
214 let data_end = data_start + self.container_data.len();
215 buf[data_start..data_end].copy_from_slice(self.container_data);
216
217 super::write_crc(buf, data_end);
219
220 Ok(len)
221 }
222}
223impl<'a> crate::traits::TableDef<'a> for ContainerSection<'a> {
224 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
225 const NAME: &'static str = "CONTAINER";
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 fn build_container(
235 container_id: u16,
236 version: u8,
237 current_next: bool,
238 section_number: u8,
239 last_section_number: u8,
240 container_data: &[u8],
241 ) -> Vec<u8> {
242 let c = ContainerSection::new(
243 true,
244 container_id,
245 version,
246 current_next,
247 section_number,
248 last_section_number,
249 container_data,
250 );
251 let mut buf = vec![0u8; c.serialized_len()];
252 c.serialize_into(&mut buf).unwrap();
253 buf
254 }
255
256 #[test]
257 fn parse_happy_path() {
258 let data = [0x00u8, 0xDE, 0xAD, 0xBE, 0xEF];
259 let bytes = build_container(0x1234, 7, true, 1, 3, &data);
260 let c = ContainerSection::parse(&bytes).unwrap();
261 assert!(c.private_indicator);
262 assert_eq!(c.container_id, 0x1234);
263 assert_eq!(c.version_number, 7);
264 assert!(c.current_next_indicator);
265 assert_eq!(c.section_number, 1);
266 assert_eq!(c.last_section_number, 3);
267 assert_eq!(c.container_data, &data[..]);
268 }
269
270 #[test]
271 fn parse_empty_container_data() {
272 let bytes = build_container(0x0000, 0, false, 0, 0, &[]);
273 let c = ContainerSection::parse(&bytes).unwrap();
274 assert_eq!(c.container_id, 0x0000);
275 assert_eq!(c.version_number, 0);
276 assert!(!c.current_next_indicator);
277 assert!(c.container_data.is_empty());
278 }
279
280 #[test]
281 fn parse_rejects_wrong_tag() {
282 let mut bytes = build_container(0x0001, 0, true, 0, 0, &[]);
283 bytes[0] = 0x70; assert!(matches!(
285 ContainerSection::parse(&bytes).unwrap_err(),
286 Error::UnexpectedTableId { table_id: 0x70, .. }
287 ));
288 }
289
290 #[test]
291 fn parse_rejects_short_buffer() {
292 assert!(matches!(
293 ContainerSection::parse(&[0x75, 0x80]).unwrap_err(),
294 Error::BufferTooShort { .. }
295 ));
296 }
297
298 #[test]
299 fn parse_rejects_section_length_overflow() {
300 let mut bytes = build_container(0x0001, 0, true, 0, 0, &[]);
301 let fake_sl: u16 = (bytes.len() as u16) + 100 - HEADER_LEN as u16;
302 bytes[1] = (bytes[1] & 0xF0) | ((fake_sl >> 8) as u8 & 0x0F);
303 bytes[2] = (fake_sl & 0xFF) as u8;
304 assert!(matches!(
305 ContainerSection::parse(&bytes).unwrap_err(),
306 Error::SectionLengthOverflow { .. }
307 ));
308 }
309
310 #[test]
311 fn serialize_round_trip() {
312 let data = [0x01u8, 0x02, 0x03];
313 let original = ContainerSection::new(false, 0xABCD, 15, false, 2, 4, &data);
314 let mut buf = vec![0u8; original.serialized_len()];
315 original.serialize_into(&mut buf).unwrap();
316 assert_eq!(ContainerSection::parse(&buf).unwrap(), original);
317 }
318
319 #[test]
320 fn serialize_rejects_output_buffer_too_small() {
321 let c = ContainerSection::new(false, 0x0001, 0, true, 0, 0, &[]);
322 let mut buf = vec![0u8; 2];
323 assert!(matches!(
324 c.serialize_into(&mut buf).unwrap_err(),
325 Error::OutputBufferTooSmall { .. }
326 ));
327 }
328
329 #[test]
330 fn table_trait_constants() {
331 assert_eq!(TABLE_ID, 0x75);
332 }
333
334 #[test]
335 fn parse_rejects_zero_section_length() {
336 let mut buf = vec![0u8; 64];
337 buf[0] = TABLE_ID;
338 buf[1] = 0xF0;
339 buf[2] = 0x00;
340 for b in &mut buf[3..] {
341 *b = 0xFF;
342 }
343 assert!(matches!(
344 ContainerSection::parse(&buf).unwrap_err(),
345 Error::SectionLengthOverflow { .. }
346 ));
347 }
348
349 #[test]
353 fn serialize_rejects_container_data_exceeding_section_length() {
354 let data = vec![0xAAu8; 4087];
355 let c = ContainerSection::new(false, 1, 0, true, 0, 0, &data);
356 let mut buf = vec![0u8; c.serialized_len()];
357 assert!(matches!(
358 c.serialize_into(&mut buf).unwrap_err(),
359 Error::FieldOverflow(_)
360 ));
361 }
362
363 #[test]
364 fn serialize_accepts_container_data_at_section_length_boundary() {
365 let data = vec![0xAAu8; 4086];
366 let c = ContainerSection::new(false, 1, 0, true, 0, 0, &data);
367 let mut buf = vec![0u8; c.serialized_len()];
368 c.serialize_into(&mut buf).expect("boundary fits");
369 let reparsed = ContainerSection::parse(&buf).expect("reparse");
370 assert_eq!(c, reparsed);
371 }
372
373 #[cfg(feature = "serde")]
374 #[test]
375 fn serde_json_round_trip() {
376 let data = [0xCAu8, 0xFE];
381 let bytes = build_container(0xBEEF, 9, true, 0, 0, &data);
382 let c = ContainerSection::parse(&bytes).unwrap();
383 let v: serde_json::Value = serde_json::to_value(&c).unwrap();
384 assert_eq!(v["container_id"], 0xBEEF);
385 assert_eq!(v["version_number"], 9);
386 assert_eq!(v["current_next_indicator"], true);
387 assert_eq!(v["container_data"], serde_json::json!([0xCA, 0xFE]));
388 }
389}