use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const SEGMENT_TYPE: u8 = 0x12;
pub const HEADER_LEN: usize = 6;
pub const FIXED_LEN: usize = 2;
pub const ENTRY_HEADER_LEN: usize = 2;
pub const ENTRY_FULL_LEN: usize = 4;
pub const ENTRY_REDUCED_LEN: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ClutEntry {
pub clut_entry_id: u8,
pub flag_2bit: bool,
pub flag_4bit: bool,
pub flag_8bit: bool,
pub reserved_flags: u8,
pub full_range_flag: bool,
pub y_value: u8,
pub cr_value: u8,
pub cb_value: u8,
pub t_value: u8,
}
impl ClutEntry {
fn serialized_len(&self) -> usize {
ENTRY_HEADER_LEN
+ if self.full_range_flag {
ENTRY_FULL_LEN
} else {
ENTRY_REDUCED_LEN
}
}
fn serialize_into(&self, buf: &mut [u8]) {
buf[0] = self.clut_entry_id;
let mut flags: u8 = self.reserved_flags;
if self.flag_2bit {
flags |= 0x80;
}
if self.flag_4bit {
flags |= 0x40;
}
if self.flag_8bit {
flags |= 0x20;
}
if self.full_range_flag {
flags |= 0x01;
}
buf[1] = flags;
if self.full_range_flag {
buf[2] = self.y_value;
buf[3] = self.cr_value;
buf[4] = self.cb_value;
buf[5] = self.t_value;
} else {
buf[2] = (self.y_value << 2) | (self.cr_value >> 2);
buf[3] = (self.cr_value << 6) | (self.cb_value << 2) | self.t_value;
}
}
fn parse(bytes: &[u8]) -> Result<(Self, usize)> {
if bytes.len() < ENTRY_HEADER_LEN {
return Err(Error::BufferTooShort {
need: ENTRY_HEADER_LEN,
have: bytes.len(),
what: "CLUT_entry header",
});
}
let clut_entry_id = bytes[0];
let flag_2bit = (bytes[1] & 0x80) != 0;
let flag_4bit = (bytes[1] & 0x40) != 0;
let flag_8bit = (bytes[1] & 0x20) != 0;
let reserved_flags = bytes[1] & 0x1E;
let full_range_flag = (bytes[1] & 0x01) != 0;
let total = ENTRY_HEADER_LEN
+ if full_range_flag {
ENTRY_FULL_LEN
} else {
ENTRY_REDUCED_LEN
};
if bytes.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: bytes.len(),
what: "CLUT_entry data",
});
}
let (y, cr, cb, t) = if full_range_flag {
(bytes[2], bytes[3], bytes[4], bytes[5])
} else {
let y = bytes[2] >> 2;
let cr = ((bytes[2] & 0x03) << 2) | (bytes[3] >> 6);
let cb = (bytes[3] >> 2) & 0x0F;
let t = bytes[3] & 0x03;
(y, cr, cb, t)
};
Ok((
ClutEntry {
clut_entry_id,
flag_2bit,
flag_4bit,
flag_8bit,
reserved_flags,
full_range_flag,
y_value: y,
cr_value: cr,
cb_value: cb,
t_value: t,
},
total,
))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ClutDefinitionSegment {
pub page_id: u16,
pub clut_id: u8,
pub clut_version_number: u8,
pub reserved: u8,
pub entries: alloc::vec::Vec<ClutEntry>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) suffix: alloc::vec::Vec<u8>,
}
impl<'a> Parse<'a> for ClutDefinitionSegment {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < HEADER_LEN + FIXED_LEN {
return Err(Error::BufferTooShort {
need: HEADER_LEN + FIXED_LEN,
have: bytes.len(),
what: "CLUT_definition_segment",
});
}
if bytes[1] != SEGMENT_TYPE {
return Err(Error::UnknownSegmentType(bytes[1]));
}
let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
let total = HEADER_LEN + segment_length;
if bytes.len() < total {
return Err(Error::BufferTooShort {
need: total,
have: bytes.len(),
what: "CLUT_definition_segment data",
});
}
let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
if body.len() < FIXED_LEN {
return Err(Error::BufferTooShort {
need: FIXED_LEN,
have: body.len(),
what: "CLUT_definition_segment body",
});
}
let clut_id = body[0];
let clut_version_number = body[1] >> 4;
let reserved = body[1] & 0x0F;
let rest = &body[FIXED_LEN..];
let mut entries = alloc::vec::Vec::new();
let mut pos: usize = 0;
let mut suffix = alloc::vec::Vec::new();
while pos < rest.len() {
match ClutEntry::parse(&rest[pos..]) {
Ok((entry, entry_len)) => {
entries.push(entry);
pos += entry_len;
}
Err(_) => {
suffix.extend_from_slice(&rest[pos..]);
break;
}
}
}
Ok(ClutDefinitionSegment {
page_id,
clut_id,
clut_version_number,
reserved,
entries,
suffix,
})
}
}
impl Serialize for ClutDefinitionSegment {
type Error = Error;
fn serialized_len(&self) -> usize {
HEADER_LEN
+ FIXED_LEN
+ self
.entries
.iter()
.map(|e| e.serialized_len())
.sum::<usize>()
+ self.suffix.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::BufferTooShort {
need: len,
have: buf.len(),
what: "CLUT_definition_segment serialize",
});
}
buf[0] = 0x0F;
buf[1] = SEGMENT_TYPE;
buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
let seg_len = (len - HEADER_LEN) as u16;
buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
buf[6] = self.clut_id;
buf[7] = (self.clut_version_number << 4) | (self.reserved & 0x0F);
let mut off = HEADER_LEN + FIXED_LEN;
for entry in &self.entries {
entry.serialize_into(&mut buf[off..]);
off += entry.serialized_len();
}
buf[off..off + self.suffix.len()].copy_from_slice(&self.suffix);
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use broadcast_common::{Parse, Serialize};
#[test]
fn round_trip_full_range() {
let bytes = [
0x0F, 0x12, 0x00, 0x01, 0x00, 0x0E, 0x03, 0x10, 0x00, 0xA1, 0x80, 0x80, 0x80, 0x80,
0x01, 0x61, 0xFF, 0xFF, 0xFF, 0x00,
];
let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
assert_eq!(seg.clut_id, 3);
assert_eq!(seg.entries.len(), 2);
assert_eq!(seg.entries[0].clut_entry_id, 0);
assert!(seg.entries[0].flag_8bit);
assert!(seg.entries[0].full_range_flag);
assert_eq!(seg.entries[0].y_value, 128);
assert_eq!(seg.entries[1].clut_entry_id, 1);
assert!(seg.entries[1].flag_4bit);
let out = seg.to_bytes();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
seg2.clut_id = 5;
let out2 = seg2.to_bytes();
assert_ne!(out2, bytes);
let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
assert_eq!(reparse.clut_id, 5);
}
#[test]
fn round_trip_reduced_range() {
let bytes = [
0x0F, 0x12, 0x00, 0x01, 0x00, 0x0A, 0x03, 0x10, 0x00, 0x80, 0x00, 0x00, 0x01, 0x40,
0xFD, 0xFC,
];
let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
assert_eq!(seg.entries.len(), 2);
assert!(seg.entries[0].flag_2bit);
assert!(!seg.entries[0].full_range_flag);
assert_eq!(seg.entries[0].y_value, 0);
let out = seg.to_bytes();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
seg2.entries[0].flag_2bit = false;
seg2.entries[0].flag_8bit = true;
let out2 = seg2.to_bytes();
assert_ne!(out2, bytes);
let reparse = ClutDefinitionSegment::parse(&out2).unwrap();
assert!(reparse.entries[0].flag_8bit);
}
#[test]
fn tolerates_nonzero_reserved_bits() {
let bytes = [
0x0F, 0x12, 0x00, 0x01, 0x00,
0x06, 0x03, 0x1A, 0x00, 0x8A, 0x00,
0x00, ];
let seg = ClutDefinitionSegment::parse(&bytes).unwrap();
assert_eq!(seg.reserved, 0x0A);
assert_eq!(seg.entries.len(), 1);
assert_eq!(seg.entries[0].reserved_flags, 0x0A);
assert!(seg.entries[0].flag_2bit);
let out = seg.to_bytes();
assert_eq!(out, bytes);
let seg2 = ClutDefinitionSegment::parse(&out).unwrap();
assert_eq!(seg2.reserved, 0x0A);
assert_eq!(seg2.entries[0].reserved_flags, 0x0A);
}
}