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