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
19#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct ClutEntry {
23 pub clut_entry_id: u8,
25 pub flag_2bit: bool,
27 pub flag_4bit: bool,
29 pub flag_8bit: bool,
31 pub reserved_flags: u8,
33 pub full_range_flag: bool,
35 pub y_value: u8,
37 pub cr_value: u8,
39 pub cb_value: u8,
41 pub t_value: u8,
43}
44
45impl ClutEntry {
46 fn serialized_len(&self) -> usize {
47 ENTRY_HEADER_LEN
48 + if self.full_range_flag {
49 ENTRY_FULL_LEN
50 } else {
51 ENTRY_REDUCED_LEN
52 }
53 }
54
55 fn serialize_into(&self, buf: &mut [u8]) {
56 buf[0] = self.clut_entry_id;
57 let mut flags: u8 = self.reserved_flags;
58 if self.flag_2bit {
59 flags |= 0x80;
60 }
61 if self.flag_4bit {
62 flags |= 0x40;
63 }
64 if self.flag_8bit {
65 flags |= 0x20;
66 }
67 if self.full_range_flag {
68 flags |= 0x01;
69 }
70 buf[1] = flags;
71 if self.full_range_flag {
72 buf[2] = self.y_value;
73 buf[3] = self.cr_value;
74 buf[4] = self.cb_value;
75 buf[5] = self.t_value;
76 } else {
77 buf[2] = (self.y_value << 2) | (self.cr_value >> 2);
78 buf[3] = (self.cr_value << 6) | (self.cb_value << 2) | self.t_value;
79 }
80 }
81
82 fn parse(bytes: &[u8]) -> Result<(Self, usize)> {
83 if bytes.len() < ENTRY_HEADER_LEN {
84 return Err(Error::BufferTooShort {
85 need: ENTRY_HEADER_LEN,
86 have: bytes.len(),
87 what: "CLUT_entry header",
88 });
89 }
90 let clut_entry_id = bytes[0];
91 let flag_2bit = (bytes[1] & 0x80) != 0;
92 let flag_4bit = (bytes[1] & 0x40) != 0;
93 let flag_8bit = (bytes[1] & 0x20) != 0;
94 let reserved_flags = bytes[1] & 0x1E;
95 let full_range_flag = (bytes[1] & 0x01) != 0;
96
97 let total = ENTRY_HEADER_LEN
98 + if full_range_flag {
99 ENTRY_FULL_LEN
100 } else {
101 ENTRY_REDUCED_LEN
102 };
103 if bytes.len() < total {
104 return Err(Error::BufferTooShort {
105 need: total,
106 have: bytes.len(),
107 what: "CLUT_entry data",
108 });
109 }
110 let (y, cr, cb, t) = if full_range_flag {
111 (bytes[2], bytes[3], bytes[4], bytes[5])
112 } else {
113 let y = bytes[2] >> 2;
114 let cr = ((bytes[2] & 0x03) << 2) | (bytes[3] >> 6);
115 let cb = (bytes[3] >> 2) & 0x0F;
116 let t = bytes[3] & 0x03;
117 (y, cr, cb, t)
118 };
119 Ok((
120 ClutEntry {
121 clut_entry_id,
122 flag_2bit,
123 flag_4bit,
124 flag_8bit,
125 reserved_flags,
126 full_range_flag,
127 y_value: y,
128 cr_value: cr,
129 cb_value: cb,
130 t_value: t,
131 },
132 total,
133 ))
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
139#[cfg_attr(feature = "serde", derive(serde::Serialize))]
140pub struct ClutDefinitionSegment {
141 pub page_id: u16,
143 pub clut_id: u8,
145 pub clut_version_number: u8,
147 pub reserved: u8,
149 pub entries: alloc::vec::Vec<ClutEntry>,
151 #[cfg_attr(feature = "serde", serde(skip))]
153 pub(crate) suffix: alloc::vec::Vec<u8>,
154}
155
156impl<'a> Parse<'a> for ClutDefinitionSegment {
157 type Error = Error;
158
159 fn parse(bytes: &'a [u8]) -> Result<Self> {
160 if bytes.len() < HEADER_LEN + FIXED_LEN {
161 return Err(Error::BufferTooShort {
162 need: HEADER_LEN + FIXED_LEN,
163 have: bytes.len(),
164 what: "CLUT_definition_segment",
165 });
166 }
167 if bytes[1] != SEGMENT_TYPE {
168 return Err(Error::UnknownSegmentType(bytes[1]));
169 }
170 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
171 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
172 let total = HEADER_LEN + segment_length;
173 if bytes.len() < total {
174 return Err(Error::BufferTooShort {
175 need: total,
176 have: bytes.len(),
177 what: "CLUT_definition_segment data",
178 });
179 }
180 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
181 if body.len() < FIXED_LEN {
182 return Err(Error::BufferTooShort {
183 need: FIXED_LEN,
184 have: body.len(),
185 what: "CLUT_definition_segment body",
186 });
187 }
188 let clut_id = body[0];
189 let clut_version_number = body[1] >> 4;
190 let reserved = body[1] & 0x0F;
191
192 let rest = &body[FIXED_LEN..];
193 let mut entries = alloc::vec::Vec::new();
194 let mut pos: usize = 0;
195 let mut suffix = alloc::vec::Vec::new();
196 while pos < rest.len() {
197 match ClutEntry::parse(&rest[pos..]) {
198 Ok((entry, entry_len)) => {
199 entries.push(entry);
200 pos += entry_len;
201 }
202 Err(_) => {
203 suffix.extend_from_slice(&rest[pos..]);
205 break;
206 }
207 }
208 }
209
210 Ok(ClutDefinitionSegment {
211 page_id,
212 clut_id,
213 clut_version_number,
214 reserved,
215 entries,
216 suffix,
217 })
218 }
219}
220
221impl Serialize for ClutDefinitionSegment {
222 type Error = Error;
223
224 fn serialized_len(&self) -> usize {
225 HEADER_LEN
226 + FIXED_LEN
227 + self
228 .entries
229 .iter()
230 .map(|e| e.serialized_len())
231 .sum::<usize>()
232 + self.suffix.len()
233 }
234
235 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
236 let len = self.serialized_len();
237 if buf.len() < len {
238 return Err(Error::BufferTooShort {
239 need: len,
240 have: buf.len(),
241 what: "CLUT_definition_segment serialize",
242 });
243 }
244 buf[0] = 0x0F;
245 buf[1] = SEGMENT_TYPE;
246 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
247 let seg_len = (len - HEADER_LEN) as u16;
248 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
249
250 buf[6] = self.clut_id;
251 buf[7] = (self.clut_version_number << 4) | (self.reserved & 0x0F);
252
253 let mut off = HEADER_LEN + FIXED_LEN;
254 for entry in &self.entries {
255 entry.serialize_into(&mut buf[off..]);
256 off += entry.serialized_len();
257 }
258 buf[off..off + self.suffix.len()].copy_from_slice(&self.suffix);
259 Ok(len)
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use broadcast_common::{Parse, Serialize};
267
268 #[test]
269 fn round_trip_full_range() {
270 let bytes = [
271 0x0F, 0x12, 0x00, 0x01, 0x00, 0x0E, 0x03, 0x10, 0x00, 0xA1, 0x80, 0x80, 0x80, 0x80,
272 0x01, 0x61, 0xFF, 0xFF, 0xFF, 0x00,
273 ];
274 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
275 assert_eq!(seg.clut_id, 3);
276 assert_eq!(seg.entries.len(), 2);
277 assert_eq!(seg.entries[0].clut_entry_id, 0);
278 assert!(seg.entries[0].flag_8bit);
279 assert!(seg.entries[0].full_range_flag);
280 assert_eq!(seg.entries[0].y_value, 128);
281 assert_eq!(seg.entries[1].clut_entry_id, 1);
282 assert!(seg.entries[1].flag_4bit);
283 let out = seg.to_bytes();
284 assert_eq!(out, bytes);
285
286 let mut seg2 = seg.clone();
288 seg2.clut_id = 5;
289 let out2 = seg2.to_bytes();
290 assert_ne!(out2, bytes);
291 let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
292 assert_eq!(reparse.clut_id, 5);
293 }
294
295 #[test]
296 fn round_trip_reduced_range() {
297 let bytes = [
298 0x0F, 0x12, 0x00, 0x01, 0x00, 0x0A, 0x03, 0x10, 0x00, 0x80, 0x00, 0x00, 0x01, 0x40,
299 0xFD, 0xFC,
300 ];
301 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
302 assert_eq!(seg.entries.len(), 2);
303 assert!(seg.entries[0].flag_2bit);
304 assert!(!seg.entries[0].full_range_flag);
305 assert_eq!(seg.entries[0].y_value, 0);
306 let out = seg.to_bytes();
307 assert_eq!(out, bytes);
308
309 let mut seg2 = seg.clone();
311 seg2.entries[0].flag_2bit = false;
312 seg2.entries[0].flag_8bit = true;
313 let out2 = seg2.to_bytes();
314 assert_ne!(out2, bytes);
315 let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
316 assert!(reparse.entries[0].flag_8bit);
317 }
318
319 #[test]
320 fn tolerates_nonzero_reserved_bits() {
321 let bytes = [
322 0x0F, 0x12, 0x00, 0x01, 0x00,
323 0x06, 0x03, 0x1A, 0x00, 0x8A, 0x00,
326 0x00, ];
328 let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
329 assert_eq!(seg.reserved, 0x0A);
330 assert_eq!(seg.entries.len(), 1);
331 assert_eq!(seg.entries[0].reserved_flags, 0x0A);
332 assert!(seg.entries[0].flag_2bit);
333 let out = seg.to_bytes();
334 assert_eq!(out, bytes);
335 let seg2 = ClutDefinitionSegment::parse(&out).unwrap();
336 assert_eq!(seg2.reserved, 0x0A);
337 assert_eq!(seg2.entries[0].reserved_flags, 0x0A);
338 }
339}