1use crate::error::{Error, Result};
17use broadcast_common::{Parse, Serialize};
18
19pub const TABLE_ID: u8 = 0x06;
21const MIN_HEADER_LEN: usize = 3;
22const EXTENSION_HEADER_LEN: usize = 5;
23use super::CRC_LEN;
24const MIN_SECTION_LEN: usize = MIN_HEADER_LEN + EXTENSION_HEADER_LEN + CRC_LEN;
25pub const RESERVED_DEFAULT: u8 = 0xFF;
27
28const B1_SSI: u8 = 0x80;
29const B1_PRIVATE: u8 = 0x40;
30const B1_RANDOM_ACCESS: u8 = 0x20;
31const B1_DECODER_CONFIG: u8 = 0x10;
32const FRAGMENT_SHIFT: u32 = 6;
33const FRAGMENT_MASK: u8 = 0x03;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38#[non_exhaustive]
39pub enum SectionFragmentIndication {
40 Complete,
42 First,
44 Last,
46 Middle,
48}
49
50impl SectionFragmentIndication {
51 #[must_use]
53 pub const fn from_u8(v: u8) -> Self {
54 match v & FRAGMENT_MASK {
55 0b11 => Self::Complete,
56 0b10 => Self::First,
57 0b01 => Self::Last,
58 _ => Self::Middle,
59 }
60 }
61
62 #[must_use]
64 pub const fn to_u8(self) -> u8 {
65 match self {
66 Self::Complete => 0b11,
67 Self::First => 0b10,
68 Self::Last => 0b01,
69 Self::Middle => 0b00,
70 }
71 }
72
73 #[must_use]
75 pub fn name(&self) -> &'static str {
76 match self {
77 Self::Complete => "complete metadata Access Unit",
78 Self::First => "first section",
79 Self::Last => "last section",
80 Self::Middle => "neither first nor last section",
81 }
82 }
83}
84broadcast_common::impl_spec_display!(SectionFragmentIndication);
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize))]
89#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
90#[non_exhaustive]
91pub struct MetadataSection<'a> {
92 pub section_syntax_indicator: bool,
94 pub private_indicator: bool,
96 pub random_access_indicator: bool,
98 pub decoder_config_flag: bool,
100 pub metadata_service_id: u8,
102 pub reserved: u8,
104 pub section_fragment_indication: SectionFragmentIndication,
106 pub version_number: u8,
108 pub current_next_indicator: bool,
110 pub section_number: u8,
112 pub last_section_number: u8,
114 pub metadata_bytes: &'a [u8],
117}
118
119impl<'a> MetadataSection<'a> {
120 #[must_use]
122 pub fn new(
123 metadata_service_id: u8,
124 section_fragment_indication: SectionFragmentIndication,
125 version_number: u8,
126 section_number: u8,
127 last_section_number: u8,
128 metadata_bytes: &'a [u8],
129 ) -> Self {
130 Self {
131 section_syntax_indicator: true,
132 private_indicator: false,
133 random_access_indicator: false,
134 decoder_config_flag: false,
135 metadata_service_id,
136 reserved: RESERVED_DEFAULT,
137 section_fragment_indication,
138 version_number,
139 current_next_indicator: true,
140 section_number,
141 last_section_number,
142 metadata_bytes,
143 }
144 }
145}
146
147impl<'a> Parse<'a> for MetadataSection<'a> {
148 type Error = Error;
149
150 fn parse(bytes: &'a [u8]) -> Result<Self> {
151 if bytes.len() < MIN_SECTION_LEN {
152 return Err(Error::BufferTooShort {
153 need: MIN_SECTION_LEN,
154 have: bytes.len(),
155 what: "MetadataSection",
156 });
157 }
158 if bytes[0] != TABLE_ID {
159 return Err(Error::UnexpectedTableId {
160 table_id: bytes[0],
161 what: "MetadataSection",
162 expected: &[TABLE_ID],
163 });
164 }
165 let total = super::check_section_length(
166 bytes.len(),
167 MIN_HEADER_LEN,
168 super::section_length_of(bytes),
169 MIN_SECTION_LEN,
170 )?;
171 let b1 = bytes[1];
172 Ok(Self {
173 section_syntax_indicator: b1 & B1_SSI != 0,
174 private_indicator: b1 & B1_PRIVATE != 0,
175 random_access_indicator: b1 & B1_RANDOM_ACCESS != 0,
176 decoder_config_flag: b1 & B1_DECODER_CONFIG != 0,
177 metadata_service_id: bytes[3],
178 reserved: bytes[4],
179 section_fragment_indication: SectionFragmentIndication::from_u8(
180 bytes[5] >> FRAGMENT_SHIFT,
181 ),
182 version_number: super::version_number_of(bytes[5]),
183 current_next_indicator: super::current_next_of(bytes[5]),
184 section_number: bytes[6],
185 last_section_number: bytes[7],
186 metadata_bytes: &bytes[MIN_HEADER_LEN + EXTENSION_HEADER_LEN..total - CRC_LEN],
187 })
188 }
189}
190
191impl Serialize for MetadataSection<'_> {
192 type Error = Error;
193
194 fn serialized_len(&self) -> usize {
195 MIN_SECTION_LEN + self.metadata_bytes.len()
196 }
197
198 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
199 let len = self.serialized_len();
200 if buf.len() < len {
201 return Err(Error::OutputBufferTooSmall {
202 need: len,
203 have: buf.len(),
204 });
205 }
206 buf[0] = TABLE_ID;
207 buf[1] = (u8::from(self.section_syntax_indicator) * B1_SSI)
208 | (u8::from(self.private_indicator) * B1_PRIVATE)
209 | (u8::from(self.random_access_indicator) * B1_RANDOM_ACCESS)
210 | (u8::from(self.decoder_config_flag) * B1_DECODER_CONFIG);
211 super::write_section_length(buf, len - MIN_HEADER_LEN)?;
212 buf[3] = self.metadata_service_id;
213 buf[4] = self.reserved;
214 buf[5] = super::b5_of(
217 self.section_fragment_indication.to_u8(),
218 self.version_number,
219 self.current_next_indicator,
220 )?;
221 buf[6] = self.section_number;
222 buf[7] = self.last_section_number;
223 let start = MIN_HEADER_LEN + EXTENSION_HEADER_LEN;
224 buf[start..start + self.metadata_bytes.len()].copy_from_slice(self.metadata_bytes);
225 super::write_crc(buf, len - CRC_LEN);
226 Ok(len)
227 }
228}
229
230impl<'a> crate::traits::TableDef<'a> for MetadataSection<'a> {
231 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
232 const NAME: &'static str = "METADATA";
233}
234
235#[cfg(test)]
236#[allow(clippy::unusual_byte_groupings)] mod tests {
238 use super::*;
239
240 fn rt(s: &MetadataSection<'_>) -> Vec<u8> {
241 let mut out = vec![0u8; s.serialized_len()];
242 s.serialize_into(&mut out).unwrap();
243 let p = MetadataSection::parse(&out).unwrap();
244 assert_eq!(&p, s);
245 out
246 }
247
248 #[test]
249 fn spec_vector_table_2_158() {
250 let s = MetadataSection {
253 random_access_indicator: true,
254 ..MetadataSection::new(0x21, SectionFragmentIndication::First, 5, 0, 2, &[1, 2, 3])
255 };
256 let b = rt(&s);
257 assert_eq!(&b[..8], &[0x06, 0xA0, 0x0C, 0x21, 0xFF, 0b10_00101_1, 0, 2]);
259 assert_eq!(&b[8..11], &[1, 2, 3]);
260 assert_eq!(broadcast_common::crc32_mpeg2::compute(&b), 0);
261 }
262
263 #[test]
264 fn all_flags_reserved_and_fragments_preserved() {
265 for f in [
266 SectionFragmentIndication::Complete,
267 SectionFragmentIndication::First,
268 SectionFragmentIndication::Last,
269 SectionFragmentIndication::Middle,
270 ] {
271 let mut s = MetadataSection::new(1, f, 31, 4, 9, &[]);
272 s.private_indicator = true;
273 s.decoder_config_flag = true;
274 s.reserved = 0x00;
275 s.current_next_indicator = false;
276 let b = rt(&s);
277 assert_eq!(MetadataSection::parse(&b).unwrap().reserved, 0);
278 }
279 }
280
281 #[test]
282 fn rejects_bad_input() {
283 assert!(MetadataSection::parse(&[0x06, 0xB0, 0x00]).is_err());
284 let mut b = vec![0u8; 12];
285 b[0] = 0x05;
286 assert!(matches!(
287 MetadataSection::parse(&b),
288 Err(Error::UnexpectedTableId { .. })
289 ));
290 let s = MetadataSection::new(1, SectionFragmentIndication::Complete, 32, 0, 0, &[]);
291 assert!(s.serialize_into(&mut [0u8; 16]).is_err());
292 let big = vec![0u8; 4090];
293 let s = MetadataSection::new(1, SectionFragmentIndication::Complete, 0, 0, 0, &big);
294 assert!(s.serialize_into(&mut vec![0u8; 5000]).is_err());
295 }
296}