1use crate::error::{Error, Result};
7use crate::traits::Table;
8use dvb_common::{Parse, Serialize};
9
10pub const TABLE_ID: u8 = 0x03;
12pub const PID: u16 = 0x0002;
14
15const MIN_HEADER_LEN: usize = 3;
16const EXTENSION_HEADER_LEN: usize = 5;
17const CRC_LEN: usize = 4;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct Tsdt {
23 pub table_id_extension: u16,
25 pub version_number: u8,
27 pub current_next_indicator: bool,
29 pub section_number: u8,
31 pub last_section_number: u8,
33 pub descriptors: Vec<u8>,
35}
36
37impl<'a> Parse<'a> for Tsdt {
38 type Error = crate::error::Error;
39
40 fn parse(bytes: &'a [u8]) -> Result<Self> {
41 let min_len = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + CRC_LEN;
42 if bytes.len() < min_len {
43 return Err(Error::BufferTooShort {
44 need: min_len,
45 have: bytes.len(),
46 what: "Tsdt",
47 });
48 }
49
50 if bytes[0] != TABLE_ID {
51 return Err(Error::UnexpectedTableId {
52 table_id: bytes[0],
53 what: "Tsdt",
54 expected: &[TABLE_ID],
55 });
56 }
57
58 let section_length = ((bytes[1] & 0x0F) as u16) << 8 | bytes[2] as u16;
59 let total = MIN_HEADER_LEN + section_length as usize;
60 if bytes.len() < total {
61 return Err(Error::SectionLengthOverflow {
62 declared: section_length as usize,
63 available: bytes.len() - MIN_HEADER_LEN,
64 });
65 }
66
67 let table_id_extension = u16::from_be_bytes([bytes[3], bytes[4]]);
68 let version_number = (bytes[5] >> 1) & 0x1F;
69 let current_next_indicator = (bytes[5] & 0x01) != 0;
70 let section_number = bytes[6];
71 let last_section_number = bytes[7];
72
73 let desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN;
76 let desc_end = total - CRC_LEN;
77
78 Ok(Tsdt {
79 table_id_extension,
80 version_number,
81 current_next_indicator,
82 section_number,
83 last_section_number,
84 descriptors: bytes[desc_start..desc_end].to_vec(),
85 })
86 }
87}
88
89impl Serialize for Tsdt {
90 type Error = crate::error::Error;
91
92 fn serialized_len(&self) -> usize {
93 MIN_HEADER_LEN + EXTENSION_HEADER_LEN + self.descriptors.len() + CRC_LEN
94 }
95
96 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
97 let len = self.serialized_len();
98 if buf.len() < len {
99 return Err(Error::OutputBufferTooSmall {
100 need: len,
101 have: buf.len(),
102 });
103 }
104
105 let section_length: u16 = (len - MIN_HEADER_LEN) as u16;
106 buf[0] = TABLE_ID;
107 buf[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
108 buf[2] = (section_length & 0xFF) as u8;
109 buf[3..5].copy_from_slice(&self.table_id_extension.to_be_bytes());
110 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
111 buf[6] = self.section_number;
112 buf[7] = self.last_section_number;
113
114 let desc_start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN;
115 buf[desc_start..desc_start + self.descriptors.len()].copy_from_slice(&self.descriptors);
116
117 let crc_pos = len - CRC_LEN;
118 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
119 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
120
121 Ok(len)
122 }
123}
124
125impl<'a> Table<'a> for Tsdt {
126 const TABLE_ID: u8 = TABLE_ID;
127 const PID: u16 = PID;
128}
129
130impl<'a> crate::traits::TableDef<'a> for Tsdt {
131 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
132 const NAME: &'static str = "TRANSPORT_STREAM_DESCRIPTION";
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 fn build_tsdt(table_id_extension: u16, version: u8, descriptors: &[u8]) -> Vec<u8> {
140 let section_length: u16 = (EXTENSION_HEADER_LEN + descriptors.len() + CRC_LEN) as u16;
141 let mut v = Vec::new();
142 v.push(TABLE_ID);
143 v.push(0xB0 | ((section_length >> 8) as u8 & 0x0F));
144 v.push((section_length & 0xFF) as u8);
145 v.extend_from_slice(&table_id_extension.to_be_bytes());
146 v.push(0xC0 | ((version & 0x1F) << 1) | 0x01);
147 v.push(0x00);
148 v.push(0x00);
149 v.extend_from_slice(descriptors);
150 v.extend_from_slice(&[0, 0, 0, 0]);
151 v
152 }
153
154 #[test]
155 fn parse_rejects_wrong_tag() {
156 let mut bytes = build_tsdt(0x1234, 5, &[]);
157 bytes[0] = 0x02;
158 assert!(matches!(
159 Tsdt::parse(&bytes).unwrap_err(),
160 Error::UnexpectedTableId { table_id: 0x02, .. }
161 ));
162 }
163
164 #[test]
165 fn parse_rejects_short_buffer() {
166 let err = Tsdt::parse(&[0x03, 0xB0]).unwrap_err();
167 assert!(matches!(err, Error::BufferTooShort { .. }));
168 }
169
170 #[test]
171 fn parse_empty_descriptor_loop() {
172 let bytes = build_tsdt(0x1234, 5, &[]);
173 let tsdt = Tsdt::parse(&bytes).unwrap();
174 assert_eq!(tsdt.table_id_extension, 0x1234);
175 assert_eq!(tsdt.version_number, 5);
176 assert!(tsdt.current_next_indicator);
177 assert_eq!(tsdt.section_number, 0);
178 assert_eq!(tsdt.last_section_number, 0);
179 assert!(tsdt.descriptors.is_empty());
180 }
181
182 #[test]
183 fn parse_with_descriptors() {
184 let descriptors = [0x01, 0x03, 0xAA, 0xBB, 0xCC];
185 let bytes = build_tsdt(0xABCD, 7, &descriptors);
186 let tsdt = Tsdt::parse(&bytes).unwrap();
187 assert_eq!(tsdt.table_id_extension, 0xABCD);
188 assert_eq!(tsdt.version_number, 7);
189 assert_eq!(tsdt.descriptors, &descriptors[..]);
190 }
191
192 #[test]
193 fn serialize_round_trip() {
194 let descriptors = [0x4D, 0x02, 0x01, 0x02];
195 let bytes = build_tsdt(0xCAFE, 3, &descriptors);
196 let tsdt = Tsdt::parse(&bytes).unwrap();
197 let mut buf = vec![0u8; tsdt.serialized_len()];
198 tsdt.serialize_into(&mut buf).unwrap();
199 let re = Tsdt::parse(&buf).unwrap();
200 assert_eq!(tsdt, re);
201 }
202
203 #[test]
204 fn serialize_round_trip_empty() {
205 let bytes = build_tsdt(0x0001, 0, &[]);
206 let tsdt = Tsdt::parse(&bytes).unwrap();
207 let mut buf = vec![0u8; tsdt.serialized_len()];
208 tsdt.serialize_into(&mut buf).unwrap();
209 let re = Tsdt::parse(&buf).unwrap();
210 assert_eq!(tsdt, re);
211 }
212
213 #[test]
214 fn table_trait_constants() {
215 assert_eq!(<Tsdt as Table>::TABLE_ID, 0x03);
216 assert_eq!(<Tsdt as Table>::PID, 0x0002);
217 }
218
219 #[cfg(feature = "serde")]
220 #[test]
221 fn tsdt_round_trips_via_json() {
222 let descriptors = [0x4D, 0x02, 0x01, 0x02];
223 let bytes = build_tsdt(0xDEAD, 9, &descriptors);
224 let tsdt = Tsdt::parse(&bytes).unwrap();
225 let j = serde_json::to_string(&tsdt).unwrap();
226 let back: Tsdt = serde_json::from_str(&j).unwrap();
227 assert_eq!(tsdt, back);
228 }
229}