1use crate::error::{Error, Result};
4use broadcast_common::len::{need_in, need_out};
5use broadcast_common::{Parse, Serialize};
6
7pub const SEGMENT_TYPE: u8 = 0x12;
9pub const HEADER_LEN: usize = 6;
11pub const FIXED_LEN: usize = 2;
13pub const ENTRY_HEADER_LEN: usize = 2;
15pub const ENTRY_FULL_LEN: usize = 4;
17pub const ENTRY_REDUCED_LEN: usize = 2;
19
20const RESERVED_FLAGS_MASK: u8 = 0x1E;
22const REDUCED_Y_MAX: u8 = 0x3F;
24const REDUCED_CR_CB_MAX: u8 = 0x0F;
26const REDUCED_T_MAX: u8 = 0x03;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32pub struct ClutEntry {
33 pub clut_entry_id: u8,
35 pub flag_2bit: bool,
37 pub flag_4bit: bool,
39 pub flag_8bit: bool,
41 pub reserved_flags: u8,
43 pub full_range_flag: bool,
45 pub y_value: u8,
47 pub cr_value: u8,
49 pub cb_value: u8,
51 pub t_value: u8,
53}
54
55impl ClutEntry {
56 fn serialized_len(&self) -> usize {
57 ENTRY_HEADER_LEN
58 + if self.full_range_flag {
59 ENTRY_FULL_LEN
60 } else {
61 ENTRY_REDUCED_LEN
62 }
63 }
64
65 fn serialize_into(&self, buf: &mut [u8]) -> Result<()> {
66 if self.reserved_flags & !RESERVED_FLAGS_MASK != 0 {
69 return Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
70 field: "CLUT_entry.reserved",
71 value: u64::from(self.reserved_flags),
72 reason: "reserved bits must lie in bits [4:1] of the flags byte",
73 }));
74 }
75 if !self.full_range_flag {
79 let components = [
80 ("CLUT_entry Y_value", self.y_value, REDUCED_Y_MAX),
81 ("CLUT_entry Cr_value", self.cr_value, REDUCED_CR_CB_MAX),
82 ("CLUT_entry Cb_value", self.cb_value, REDUCED_CR_CB_MAX),
83 ("CLUT_entry T_value", self.t_value, REDUCED_T_MAX),
84 ];
85 if let Some((field, value, _)) = components.into_iter().find(|&(_, v, max)| v > max) {
86 return Err(Error::InvalidValue(broadcast_common::error::InvalidValue {
87 field,
88 value: u64::from(value),
89 reason: "reduced-range entry needs Y<=63, Cr<=15, Cb<=15, T<=3",
90 }));
91 }
92 }
93 buf[0] = self.clut_entry_id;
94 let mut flags: u8 = self.reserved_flags;
95 if self.flag_2bit {
96 flags |= 0x80;
97 }
98 if self.flag_4bit {
99 flags |= 0x40;
100 }
101 if self.flag_8bit {
102 flags |= 0x20;
103 }
104 if self.full_range_flag {
105 flags |= 0x01;
106 }
107 buf[1] = flags;
108 if self.full_range_flag {
109 buf[2] = self.y_value;
110 buf[3] = self.cr_value;
111 buf[4] = self.cb_value;
112 buf[5] = self.t_value;
113 } else {
114 buf[2] = (self.y_value << 2) | (self.cr_value >> 2);
115 buf[3] = (self.cr_value << 6) | (self.cb_value << 2) | self.t_value;
116 }
117 Ok(())
118 }
119
120 fn parse(bytes: &[u8]) -> Result<(Self, usize)> {
121 need_in(bytes, ENTRY_HEADER_LEN, "CLUT_entry header")?;
122 let clut_entry_id = bytes[0];
123 let flag_2bit = (bytes[1] & 0x80) != 0;
124 let flag_4bit = (bytes[1] & 0x40) != 0;
125 let flag_8bit = (bytes[1] & 0x20) != 0;
126 let reserved_flags = bytes[1] & RESERVED_FLAGS_MASK;
127 let full_range_flag = (bytes[1] & 0x01) != 0;
128
129 let total = ENTRY_HEADER_LEN
130 + if full_range_flag {
131 ENTRY_FULL_LEN
132 } else {
133 ENTRY_REDUCED_LEN
134 };
135 need_in(bytes, total, "CLUT_entry data")?;
136 let (y, cr, cb, t) = if full_range_flag {
137 (bytes[2], bytes[3], bytes[4], bytes[5])
138 } else {
139 let y = bytes[2] >> 2;
140 let cr = ((bytes[2] & 0x03) << 2) | (bytes[3] >> 6);
141 let cb = (bytes[3] >> 2) & 0x0F;
142 let t = bytes[3] & 0x03;
143 (y, cr, cb, t)
144 };
145 Ok((
146 ClutEntry {
147 clut_entry_id,
148 flag_2bit,
149 flag_4bit,
150 flag_8bit,
151 reserved_flags,
152 full_range_flag,
153 y_value: y,
154 cr_value: cr,
155 cb_value: cb,
156 t_value: t,
157 },
158 total,
159 ))
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize))]
166pub struct ClutDefinitionSegment {
167 pub page_id: u16,
169 pub clut_id: u8,
171 pub clut_version_number: u8,
173 pub reserved: u8,
175 pub entries: alloc::vec::Vec<ClutEntry>,
177 #[cfg_attr(feature = "serde", serde(skip))]
179 pub(crate) suffix: alloc::vec::Vec<u8>,
180}
181
182impl<'a> Parse<'a> for ClutDefinitionSegment {
183 type Error = Error;
184
185 fn parse(bytes: &'a [u8]) -> Result<Self> {
186 need_in(bytes, HEADER_LEN + FIXED_LEN, "CLUT_definition_segment")?;
187 if bytes[0] != crate::pes_data_field::SYNC_BYTE {
188 return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
189 what: "sync_byte",
190 expected: u32::from(crate::pes_data_field::SYNC_BYTE),
191 found: u32::from(bytes[0]),
192 }));
193 }
194 if bytes[1] != SEGMENT_TYPE {
195 return Err(Error::UnknownSegmentType(bytes[1]));
196 }
197 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
198 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
199 let total = HEADER_LEN + segment_length;
200 need_in(bytes, total, "CLUT_definition_segment data")?;
201 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
202 need_in(body, FIXED_LEN, "CLUT_definition_segment body")?;
203 let clut_id = body[0];
204 let clut_version_number = body[1] >> 4;
205 let reserved = body[1] & 0x0F;
206
207 let rest = &body[FIXED_LEN..];
208 let mut entries = alloc::vec::Vec::new();
209 let mut pos: usize = 0;
210 let mut suffix = alloc::vec::Vec::new();
211 while pos < rest.len() {
212 match ClutEntry::parse(&rest[pos..]) {
213 Ok((entry, entry_len)) => {
214 entries.push(entry);
215 pos += entry_len;
216 }
217 Err(_) => {
218 suffix.extend_from_slice(&rest[pos..]);
220 break;
221 }
222 }
223 }
224
225 Ok(ClutDefinitionSegment {
226 page_id,
227 clut_id,
228 clut_version_number,
229 reserved,
230 entries,
231 suffix,
232 })
233 }
234}
235
236impl Serialize for ClutDefinitionSegment {
237 type Error = Error;
238
239 fn serialized_len(&self) -> usize {
240 HEADER_LEN
241 + FIXED_LEN
242 + self
243 .entries
244 .iter()
245 .map(|e| e.serialized_len())
246 .sum::<usize>()
247 + self.suffix.len()
248 }
249
250 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
251 let len = self.serialized_len();
252 need_out(buf, len)?;
253 buf[0] = 0x0F;
254 buf[1] = SEGMENT_TYPE;
255 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
256 let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
257 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
258
259 buf[6] = self.clut_id;
260 buf[7] = (broadcast_common::len::fit_mask(
261 self.clut_version_number,
262 0x0F,
263 "clut_version_number",
264 )? << 4)
265 | broadcast_common::len::fit_mask(self.reserved, 0x0F, "reserved")?;
266
267 let mut off = HEADER_LEN + FIXED_LEN;
268 for entry in &self.entries {
269 entry.serialize_into(&mut buf[off..])?;
270 off += entry.serialized_len();
271 }
272 buf[off..off + self.suffix.len()].copy_from_slice(&self.suffix);
273 Ok(len)
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use broadcast_common::{Parse, Serialize};
281
282 #[test]
286 fn reduced_range_overwide_components_are_rejected() {
287 let entry = |y, cr, cb, t| ClutEntry {
288 clut_entry_id: 1,
289 flag_2bit: true,
290 flag_4bit: false,
291 flag_8bit: false,
292 reserved_flags: 0,
293 full_range_flag: false,
294 y_value: y,
295 cr_value: cr,
296 cb_value: cb,
297 t_value: t,
298 };
299 let seg = |e: ClutEntry| ClutDefinitionSegment {
300 page_id: 1,
301 clut_id: 0,
302 clut_version_number: 0,
303 reserved: 0,
304 entries: alloc::vec![e],
305 suffix: alloc::vec::Vec::new(),
306 };
307 let max = seg(entry(0x3F, 0x0F, 0x0F, 0x03));
308 let bytes = max.try_to_bytes().unwrap();
309 assert_eq!(ClutDefinitionSegment::parse(&bytes).unwrap(), max);
310 for bad in [
311 entry(0x40, 0, 0, 0),
312 entry(0, 0x10, 0, 0),
313 entry(0, 0, 0x10, 0),
314 entry(0, 0, 0, 0x04),
315 ] {
316 assert!(matches!(
317 seg(bad).try_to_bytes(),
318 Err(Error::InvalidValue(..))
319 ));
320 }
321 let mut full = entry(0xFF, 0xFF, 0xFF, 0xFF);
323 full.full_range_flag = true;
324 assert!(seg(full).try_to_bytes().is_ok());
325 }
326
327 #[test]
328 fn round_trip_full_range() {
329 let bytes = [
330 0x0F, 0x12, 0x00, 0x01, 0x00, 0x0E, 0x03, 0x10, 0x00, 0xA1, 0x80, 0x80, 0x80, 0x80,
331 0x01, 0x61, 0xFF, 0xFF, 0xFF, 0x00,
332 ];
333 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
334 assert_eq!(seg.clut_id, 3);
335 assert_eq!(seg.entries.len(), 2);
336 assert_eq!(seg.entries[0].clut_entry_id, 0);
337 assert!(seg.entries[0].flag_8bit);
338 assert!(seg.entries[0].full_range_flag);
339 assert_eq!(seg.entries[0].y_value, 128);
340 assert_eq!(seg.entries[1].clut_entry_id, 1);
341 assert!(seg.entries[1].flag_4bit);
342 let out = seg.try_to_bytes().unwrap();
343 assert_eq!(out, bytes);
344
345 let mut seg2 = seg.clone();
347 seg2.clut_id = 5;
348 let out2 = seg2.try_to_bytes().unwrap();
349 assert_ne!(out2, bytes);
350 let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
351 assert_eq!(reparse.clut_id, 5);
352 }
353
354 #[test]
356 fn rejects_bad_sync_byte() {
357 let bytes = [
358 0x00, 0x12, 0x00, 0x01, 0x00, 0x0E, 0x03, 0x10, 0x00, 0xA1, 0x80, 0x80, 0x80, 0x80,
359 0x01, 0x61, 0xFF, 0xFF, 0xFF, 0x00,
360 ];
361 assert!(matches!(
362 ClutDefinitionSegment::parse(&bytes),
363 Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
364 found: 0x00,
365 ..
366 }))
367 ));
368 }
369
370 #[test]
371 fn round_trip_reduced_range() {
372 let bytes = [
373 0x0F, 0x12, 0x00, 0x01, 0x00, 0x0A, 0x03, 0x10, 0x00, 0x80, 0x00, 0x00, 0x01, 0x40,
374 0xFD, 0xFC,
375 ];
376 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
377 assert_eq!(seg.entries.len(), 2);
378 assert!(seg.entries[0].flag_2bit);
379 assert!(!seg.entries[0].full_range_flag);
380 assert_eq!(seg.entries[0].y_value, 0);
381 let out = seg.try_to_bytes().unwrap();
382 assert_eq!(out, bytes);
383
384 let mut seg2 = seg.clone();
386 seg2.entries[0].flag_2bit = false;
387 seg2.entries[0].flag_8bit = true;
388 let out2 = seg2.try_to_bytes().unwrap();
389 assert_ne!(out2, bytes);
390 let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
391 assert!(reparse.entries[0].flag_8bit);
392 }
393
394 #[test]
395 fn tolerates_nonzero_reserved_bits() {
396 let bytes = [
397 0x0F, 0x12, 0x00, 0x01, 0x00,
398 0x06, 0x03, 0x1A, 0x00, 0x8A, 0x00,
401 0x00, ];
403 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
404 assert_eq!(seg.reserved, 0x0A);
405 assert_eq!(seg.entries.len(), 1);
406 assert_eq!(seg.entries[0].reserved_flags, 0x0A);
407 assert!(seg.entries[0].flag_2bit);
408 let out = seg.try_to_bytes().unwrap();
409 assert_eq!(out, bytes);
410 let seg2 = ClutDefinitionSegment::parse(&out).unwrap();
411 assert_eq!(seg2.reserved, 0x0A);
412 assert_eq!(seg2.entries[0].reserved_flags, 0x0A);
413 }
414}