use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const SEGMENT_TYPE: u8 = 0x13;
pub const HEADER_LEN: usize = 6;
pub const FIXED_LEN: usize = 3;
pub const INTERLACE_LEN_LEN: usize = 4;
pub const PROGRESSIVE_HEADER_LEN: usize = 6;
const RUN_LENGTH_7BIT_MASK: u8 = 0x7F;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[repr(u8)]
#[non_exhaustive]
pub enum ObjectCodingMethod {
Pixels = 0x00,
Characters = 0x01,
ProgressivePixels = 0x02,
Reserved(u8),
}
impl ObjectCodingMethod {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Pixels => "pixels",
Self::Characters => "characters",
Self::ProgressivePixels => "progressive_pixels",
Self::Reserved(_) => "reserved",
}
}
fn to_bits(self) -> u8 {
match self {
Self::Pixels => 0x00,
Self::Characters => 0x01,
Self::ProgressivePixels => 0x02,
Self::Reserved(v) => v & 0x03,
}
}
}
broadcast_common::impl_spec_display!(ObjectCodingMethod, Reserved);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[repr(u8)]
#[non_exhaustive]
pub enum DataType {
CodeString2Bit = 0x10,
CodeString4Bit = 0x11,
CodeString8Bit = 0x12,
MapTable2To4 = 0x20,
MapTable2To8 = 0x21,
MapTable4To8 = 0x22,
EndOfLine = 0xF0,
Reserved(u8),
}
impl DataType {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::CodeString2Bit => "2-bit_code_string",
Self::CodeString4Bit => "4-bit_code_string",
Self::CodeString8Bit => "8-bit_code_string",
Self::MapTable2To4 => "2_to_4_map_table",
Self::MapTable2To8 => "2_to_8_map_table",
Self::MapTable4To8 => "4_to_8_map_table",
Self::EndOfLine => "end_of_line",
Self::Reserved(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(DataType, Reserved);
const MAP_TABLE_2TO4_BYTES: usize = 2;
const MAP_TABLE_2TO8_BYTES: usize = 4;
const MAP_TABLE_4TO8_BYTES: usize = 16;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PixelDataSubBlock<'a> {
pub data_type: DataType,
#[cfg_attr(feature = "serde", serde(skip))]
pub data: &'a [u8],
}
impl<'a> PixelDataSubBlock<'a> {
#[must_use]
pub fn runs(&self) -> Option<PixelRunIter<'a>> {
let depth = match self.data_type {
DataType::CodeString2Bit => CodeStringDepth::Two,
DataType::CodeString4Bit => CodeStringDepth::Four,
DataType::CodeString8Bit => CodeStringDepth::Eight,
DataType::MapTable2To4
| DataType::MapTable2To8
| DataType::MapTable4To8
| DataType::EndOfLine
| DataType::Reserved(_) => return None,
};
Some(PixelRunIter {
data: self.data,
bitpos: 0,
depth,
done: false,
})
}
fn serialized_len(&self) -> usize {
1 + self.data.len()
}
fn serialize_into(&self, buf: &mut [u8]) {
buf[0] = match self.data_type {
DataType::CodeString2Bit => 0x10,
DataType::CodeString4Bit => 0x11,
DataType::CodeString8Bit => 0x12,
DataType::MapTable2To4 => 0x20,
DataType::MapTable2To8 => 0x21,
DataType::MapTable4To8 => 0x22,
DataType::EndOfLine => 0xF0,
DataType::Reserved(v) => v,
};
buf[1..1 + self.data.len()].copy_from_slice(self.data);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct InterlacedPixelsData<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub top_sub_blocks: alloc::vec::Vec<PixelDataSubBlock<'a>>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub bottom_sub_blocks: alloc::vec::Vec<PixelDataSubBlock<'a>>,
#[cfg_attr(feature = "serde", serde(skip))]
pub stuffing: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ProgressivePixelBlock<'a> {
pub bitmap_width: u16,
pub bitmap_height: u16,
#[cfg_attr(feature = "serde", serde(skip))]
pub compressed_data: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ObjectDataPayload<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
InterlacedPixels(InterlacedPixelsData<'a>),
Characters {
character_codes: alloc::vec::Vec<u16>,
},
#[cfg_attr(feature = "serde", serde(borrow))]
ProgressivePixels(ProgressivePixelBlock<'a>),
Reserved {
coding_method: u8,
#[cfg_attr(feature = "serde", serde(skip))]
data: &'a [u8],
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ObjectDataSegment<'a> {
pub page_id: u16,
pub object_id: u16,
pub object_version_number: u8,
pub object_coding_method: ObjectCodingMethod,
pub non_modifying_colour_flag: bool,
#[cfg_attr(feature = "serde", serde(borrow))]
pub payload: ObjectDataPayload<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelRun {
pub pixel_code: u8,
pub run_length: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CodeStringDepth {
Two,
Four,
Eight,
}
#[derive(Debug, Clone)]
pub struct PixelRunIter<'a> {
data: &'a [u8],
bitpos: usize,
depth: CodeStringDepth,
done: bool,
}
impl PixelRunIter<'_> {
fn bits(&self, bp: usize, n: usize) -> u8 {
let byte_idx = bp / 8;
let bit_idx = bp % 8;
if byte_idx >= self.data.len() {
return 0;
}
if bit_idx + n <= 8 {
(self.data[byte_idx] >> (8 - bit_idx - n)) & ((1u16 << n) - 1) as u8
} else {
let first_bits = 8 - bit_idx;
let v1 = ((self.data[byte_idx] & ((1u8 << first_bits) - 1)) as u16) << (n - first_bits);
let v2 = if byte_idx + 1 < self.data.len() {
(self.data[byte_idx + 1] >> (8 - (n - first_bits))) as u16
} else {
0
};
(v1 | v2) as u8
}
}
fn next_2bit(&mut self) -> Option<PixelRun> {
let b2 = self.bits(self.bitpos, 2);
self.bitpos += 2;
if b2 != 0 {
return Some(PixelRun {
pixel_code: b2,
run_length: 1,
});
}
let s1 = self.bits(self.bitpos, 1);
self.bitpos += 1;
if s1 == 1 {
let n = self.bits(self.bitpos, 3);
self.bitpos += 3;
let code = self.bits(self.bitpos, 2);
self.bitpos += 2;
return Some(PixelRun {
pixel_code: code,
run_length: u32::from(n) + 3,
});
}
let s2 = self.bits(self.bitpos, 1);
self.bitpos += 1;
if s2 == 1 {
return Some(PixelRun {
pixel_code: 0,
run_length: 1,
});
}
let s3 = self.bits(self.bitpos, 2);
self.bitpos += 2;
match s3 {
0b01 => Some(PixelRun {
pixel_code: 0,
run_length: 2,
}),
0b10 => {
let n = self.bits(self.bitpos, 4);
self.bitpos += 4;
let code = self.bits(self.bitpos, 2);
self.bitpos += 2;
Some(PixelRun {
pixel_code: code,
run_length: u32::from(n) + 12,
})
}
0b11 => {
let n = self.bits(self.bitpos, 8);
self.bitpos += 8;
let code = self.bits(self.bitpos, 2);
self.bitpos += 2;
Some(PixelRun {
pixel_code: code,
run_length: u32::from(n) + 29,
})
}
_ => {
self.done = true;
None
}
}
}
fn next_4bit(&mut self) -> Option<PixelRun> {
let b4 = self.bits(self.bitpos, 4);
self.bitpos += 4;
if b4 != 0 {
return Some(PixelRun {
pixel_code: b4,
run_length: 1,
});
}
let s1 = self.bits(self.bitpos, 1);
self.bitpos += 1;
if s1 == 0 {
let n3 = self.bits(self.bitpos, 3);
self.bitpos += 3;
if n3 == 0 {
self.done = true;
return None;
}
return Some(PixelRun {
pixel_code: 0,
run_length: u32::from(n3) + 2,
});
}
let s2 = self.bits(self.bitpos, 1);
self.bitpos += 1;
if s2 == 0 {
let n2 = self.bits(self.bitpos, 2);
self.bitpos += 2;
let code = self.bits(self.bitpos, 4);
self.bitpos += 4;
return Some(PixelRun {
pixel_code: code,
run_length: u32::from(n2) + 4,
});
}
let s3 = self.bits(self.bitpos, 2);
self.bitpos += 2;
match s3 {
0b00 => Some(PixelRun {
pixel_code: 0,
run_length: 1,
}),
0b01 => Some(PixelRun {
pixel_code: 0,
run_length: 2,
}),
0b10 => {
let n = self.bits(self.bitpos, 4);
self.bitpos += 4;
let code = self.bits(self.bitpos, 4);
self.bitpos += 4;
Some(PixelRun {
pixel_code: code,
run_length: u32::from(n) + 9,
})
}
_ => {
let n = self.bits(self.bitpos, 8);
self.bitpos += 8;
let code = self.bits(self.bitpos, 4);
self.bitpos += 4;
Some(PixelRun {
pixel_code: code,
run_length: u32::from(n) + 25,
})
}
}
}
fn next_8bit(&mut self) -> Option<PixelRun> {
let pos = self.bitpos / 8;
if pos >= self.data.len() {
self.done = true;
return None;
}
let b0 = self.data[pos];
if b0 != 0 {
self.bitpos += 8;
return Some(PixelRun {
pixel_code: b0,
run_length: 1,
});
}
if pos + 1 >= self.data.len() {
self.done = true;
return None;
}
let b1 = self.data[pos + 1];
if (b1 >> 7) & 1 == 0 {
let rl = b1 & RUN_LENGTH_7BIT_MASK;
self.bitpos += 16;
if rl == 0 {
self.done = true;
return None;
}
return Some(PixelRun {
pixel_code: 0,
run_length: u32::from(rl),
});
}
if pos + 2 >= self.data.len() {
self.done = true;
return None;
}
let rl = b1 & RUN_LENGTH_7BIT_MASK;
let code = self.data[pos + 2];
self.bitpos += 24;
Some(PixelRun {
pixel_code: code,
run_length: u32::from(rl),
})
}
}
impl Iterator for PixelRunIter<'_> {
type Item = PixelRun;
fn next(&mut self) -> Option<PixelRun> {
if self.done {
return None;
}
match self.depth {
CodeStringDepth::Two => self.next_2bit(),
CodeStringDepth::Four => self.next_4bit(),
CodeStringDepth::Eight => self.next_8bit(),
}
}
}
fn scan_2bit_code_string(data: &[u8]) -> usize {
let mut bitpos: usize = 0;
let bits = |bp: usize, n: usize| -> u8 {
let byte_idx = bp / 8;
let bit_idx = bp % 8;
if byte_idx >= data.len() {
return 0;
}
if bit_idx + n <= 8 {
(data[byte_idx] >> (8 - bit_idx - n)) & ((1u16 << n) - 1) as u8
} else {
let first_bits = 8 - bit_idx;
let v1 = ((data[byte_idx] & ((1u8 << first_bits) - 1)) as u16) << (n - first_bits);
let v2 = if byte_idx + 1 < data.len() {
(data[byte_idx + 1] >> (8 - (n - first_bits))) as u16
} else {
0
};
(v1 | v2) as u8
}
};
loop {
let b2 = bits(bitpos, 2);
bitpos += 2;
if b2 != 0 {
continue;
}
let s1 = bits(bitpos, 1);
bitpos += 1;
if s1 == 1 {
bitpos += 3 + 2;
continue;
}
let s2 = bits(bitpos, 1);
bitpos += 1;
if s2 == 1 {
continue;
}
let s3 = bits(bitpos, 2);
bitpos += 2;
match s3 {
0b00 => break, 0b01 => continue, 0b10 => {
bitpos += 4 + 2; }
0b11 => {
bitpos += 8 + 2; }
_ => {}
}
}
let byte_len = bitpos.div_ceil(8);
byte_len.min(data.len())
}
fn scan_4bit_code_string(data: &[u8]) -> usize {
let mut bitpos: usize = 0;
let bits = |bp: usize, n: usize| -> u8 {
let byte_idx = bp / 8;
let bit_idx = bp % 8;
if byte_idx >= data.len() {
return 0;
}
if bit_idx + n <= 8 {
(data[byte_idx] >> (8 - bit_idx - n)) & ((1u16 << n) - 1) as u8
} else {
let first_bits = 8 - bit_idx;
let v1 = ((data[byte_idx] & ((1u8 << first_bits) - 1)) as u16) << (n - first_bits);
let v2 = if byte_idx + 1 < data.len() {
(data[byte_idx + 1] >> (8 - (n - first_bits))) as u16
} else {
0
};
(v1 | v2) as u8
}
};
loop {
let b4 = bits(bitpos, 4);
bitpos += 4;
if b4 != 0 {
continue;
}
let s1 = bits(bitpos, 1);
bitpos += 1;
if s1 == 0 {
let n3 = bits(bitpos, 3);
bitpos += 3;
if n3 == 0 {
break;
}
continue;
}
let s2 = bits(bitpos, 1);
bitpos += 1;
if s2 == 0 {
bitpos += 2 + 4;
continue;
}
let s3 = bits(bitpos, 2);
bitpos += 2;
match s3 {
0b00 | 0b01 => continue, 0b10 => {
bitpos += 4 + 4; }
0b11 => {
bitpos += 8 + 4; }
_ => {}
}
}
let byte_len = bitpos.div_ceil(8);
byte_len.min(data.len())
}
fn scan_8bit_code_string(data: &[u8]) -> usize {
let mut pos: usize = 0;
loop {
if pos >= data.len() {
break;
}
if data[pos] != 0x00 {
pos += 1;
continue;
}
if pos + 1 >= data.len() {
pos += 1;
break;
}
let b = data[pos + 1];
let s1 = (b >> 7) & 1;
if s1 == 0 {
let rl = b & RUN_LENGTH_7BIT_MASK;
if rl == 0 {
pos += 2;
break;
}
pos += 2;
} else {
pos += 3;
}
}
pos.min(data.len())
}
fn parse_pixel_sub_blocks<'a>(
bytes: &'a [u8],
field_len: usize,
what: &'static str,
) -> Result<alloc::vec::Vec<PixelDataSubBlock<'a>>> {
let end = field_len.min(bytes.len());
let field = &bytes[..end];
let mut blocks = alloc::vec::Vec::new();
let mut pos: usize = 0;
while pos < field.len() {
if pos >= field.len() {
break;
}
let data_type_byte = field[pos];
let data_type = match data_type_byte {
0x10 => DataType::CodeString2Bit,
0x11 => DataType::CodeString4Bit,
0x12 => DataType::CodeString8Bit,
0x20 => DataType::MapTable2To4,
0x21 => DataType::MapTable2To8,
0x22 => DataType::MapTable4To8,
0xF0 => DataType::EndOfLine,
v => DataType::Reserved(v),
};
pos += 1;
let data_len = match data_type_byte {
0x20 => MAP_TABLE_2TO4_BYTES,
0x21 => MAP_TABLE_2TO8_BYTES,
0x22 => MAP_TABLE_4TO8_BYTES,
0xF0 => 0,
0x10 => scan_2bit_code_string(&field[pos..]),
0x11 => scan_4bit_code_string(&field[pos..]),
0x12 => scan_8bit_code_string(&field[pos..]),
_ => {
field.len() - pos
}
};
let block_end = (pos + data_len).min(field.len());
let block_data = &field[pos..block_end];
blocks.push(PixelDataSubBlock {
data_type,
data: block_data,
});
pos = block_end;
}
if pos != end {
return Err(Error::BufferTooShort {
need: end,
have: pos,
what,
});
}
Ok(blocks)
}
impl<'a> Parse<'a> for ObjectDataSegment<'a> {
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: "object_data_segment",
});
}
if bytes[0] != crate::pes_data_field::SYNC_BYTE {
return Err(Error::BadSyncByte(bytes[0]));
}
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: "object_data_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: "object_data_segment body",
});
}
let object_id = u16::from_be_bytes([body[0], body[1]]);
let object_version_number = body[2] >> 4;
let coding_method_bits = (body[2] >> 2) & 0x03;
let non_modifying_colour_flag = (body[2] & 0x02) != 0;
let reserved = body[2] & 0x01;
let _ = reserved;
let object_coding_method = match coding_method_bits {
0x00 => ObjectCodingMethod::Pixels,
0x01 => ObjectCodingMethod::Characters,
0x02 => ObjectCodingMethod::ProgressivePixels,
v => ObjectCodingMethod::Reserved(v),
};
let payload_data = &body[FIXED_LEN..];
let payload = match coding_method_bits {
0x00 => {
if payload_data.len() < INTERLACE_LEN_LEN {
return Err(Error::BufferTooShort {
need: INTERLACE_LEN_LEN,
have: payload_data.len(),
what: "object_data interlace lengths",
});
}
let top_len = u16::from_be_bytes([payload_data[0], payload_data[1]]) as usize;
let bottom_len = u16::from_be_bytes([payload_data[2], payload_data[3]]) as usize;
let top_start = INTERLACE_LEN_LEN;
let top_end = top_start + top_len;
let bottom_start = top_end;
let bottom_end = bottom_start + bottom_len;
if payload_data.len() < bottom_end {
return Err(Error::BufferTooShort {
need: bottom_end,
have: payload_data.len(),
what: "object_data pixel sub-blocks",
});
}
let stuffing = &payload_data[bottom_end..];
if let Some(&b) = stuffing.first()
&& b != 0x00
{
return Err(Error::BadStuffingByte(b));
}
let top_sub_blocks = parse_pixel_sub_blocks(
&payload_data[top_start..top_end],
top_len,
"top pixel sub-blocks",
)?;
let bottom_sub_blocks = if bottom_len > 0 {
parse_pixel_sub_blocks(
&payload_data[bottom_start..bottom_end],
bottom_len,
"bottom pixel sub-blocks",
)?
} else {
alloc::vec::Vec::new()
};
ObjectDataPayload::InterlacedPixels(InterlacedPixelsData {
top_sub_blocks,
bottom_sub_blocks,
stuffing,
})
}
0x01 => {
if payload_data.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: 0,
what: "character count",
});
}
let number_of_codes = payload_data[0] as usize;
let codes_len = 1 + number_of_codes * 2;
if payload_data.len() < codes_len {
return Err(Error::BufferTooShort {
need: codes_len,
have: payload_data.len(),
what: "character codes",
});
}
let mut codes = alloc::vec::Vec::with_capacity(number_of_codes);
for i in 0..number_of_codes {
let ci = 1 + i * 2;
codes.push(u16::from_be_bytes([payload_data[ci], payload_data[ci + 1]]));
}
ObjectDataPayload::Characters {
character_codes: codes,
}
}
0x02 => {
if payload_data.len() < PROGRESSIVE_HEADER_LEN {
return Err(Error::BufferTooShort {
need: PROGRESSIVE_HEADER_LEN,
have: payload_data.len(),
what: "progressive pixel block",
});
}
let bitmap_width = u16::from_be_bytes([payload_data[0], payload_data[1]]);
let bitmap_height = u16::from_be_bytes([payload_data[2], payload_data[3]]);
let compressed_len =
u16::from_be_bytes([payload_data[4], payload_data[5]]) as usize;
if payload_data.len() < PROGRESSIVE_HEADER_LEN + compressed_len {
return Err(Error::BufferTooShort {
need: PROGRESSIVE_HEADER_LEN + compressed_len,
have: payload_data.len(),
what: "compressed bitmap data",
});
}
ObjectDataPayload::ProgressivePixels(ProgressivePixelBlock {
bitmap_width,
bitmap_height,
compressed_data: &payload_data
[PROGRESSIVE_HEADER_LEN..PROGRESSIVE_HEADER_LEN + compressed_len],
})
}
_ => ObjectDataPayload::Reserved {
coding_method: coding_method_bits,
data: payload_data,
},
};
Ok(ObjectDataSegment {
page_id,
object_id,
object_version_number,
object_coding_method,
non_modifying_colour_flag,
payload,
})
}
}
impl Serialize for ObjectDataSegment<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
let body_len = FIXED_LEN
+ match &self.payload {
ObjectDataPayload::InterlacedPixels(ip) => {
let top_len: usize = ip.top_sub_blocks.iter().map(|s| s.serialized_len()).sum();
let bottom_len: usize = ip
.bottom_sub_blocks
.iter()
.map(|s| s.serialized_len())
.sum();
INTERLACE_LEN_LEN + top_len + bottom_len + ip.stuffing.len()
}
ObjectDataPayload::Characters {
character_codes, ..
} => 1 + character_codes.len() * 2,
ObjectDataPayload::ProgressivePixels(pp) => {
PROGRESSIVE_HEADER_LEN + pp.compressed_data.len()
}
ObjectDataPayload::Reserved { data, .. } => data.len(),
};
HEADER_LEN + body_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: "object_data_segment serialize",
});
}
buf[0] = 0x0F;
buf[1] = SEGMENT_TYPE;
buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
buf[6..8].copy_from_slice(&self.object_id.to_be_bytes());
buf[8] = (self.object_version_number << 4)
| (self.object_coding_method.to_bits() << 2)
| (u8::from(self.non_modifying_colour_flag) << 1);
let mut off = HEADER_LEN + FIXED_LEN;
match &self.payload {
ObjectDataPayload::InterlacedPixels(ip) => {
let top_len: usize = ip.top_sub_blocks.iter().map(|s| s.serialized_len()).sum();
let bottom_len: usize = ip
.bottom_sub_blocks
.iter()
.map(|s| s.serialized_len())
.sum();
let top_len = broadcast_common::len::fit_u16(top_len, "top_field_data_length")?;
let bottom_len =
broadcast_common::len::fit_u16(bottom_len, "bottom_field_data_length")?;
buf[off..off + 2].copy_from_slice(&top_len.to_be_bytes());
buf[off + 2..off + 4].copy_from_slice(&bottom_len.to_be_bytes());
off += INTERLACE_LEN_LEN;
for sub in &ip.top_sub_blocks {
sub.serialize_into(&mut buf[off..]);
off += sub.serialized_len();
}
for sub in &ip.bottom_sub_blocks {
sub.serialize_into(&mut buf[off..]);
off += sub.serialized_len();
}
buf[off..off + ip.stuffing.len()].copy_from_slice(ip.stuffing);
off += ip.stuffing.len();
}
ObjectDataPayload::Characters { character_codes } => {
buf[off] = broadcast_common::len::fit_u8(character_codes.len(), "number_of_codes")?;
off += 1;
for code in character_codes {
buf[off..off + 2].copy_from_slice(&code.to_be_bytes());
off += 2;
}
}
ObjectDataPayload::ProgressivePixels(pp) => {
buf[off..off + 2].copy_from_slice(&pp.bitmap_width.to_be_bytes());
buf[off + 2..off + 4].copy_from_slice(&pp.bitmap_height.to_be_bytes());
let clen = broadcast_common::len::fit_u16(
pp.compressed_data.len(),
"compressed_data_length",
)?;
buf[off + 4..off + 6].copy_from_slice(&clen.to_be_bytes());
off += PROGRESSIVE_HEADER_LEN;
buf[off..off + pp.compressed_data.len()].copy_from_slice(pp.compressed_data);
off += pp.compressed_data.len();
}
ObjectDataPayload::Reserved { data, .. } => {
buf[off..off + data.len()].copy_from_slice(data);
off += data.len();
}
}
debug_assert_eq!(off, len);
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use broadcast_common::{Parse, Serialize};
#[test]
fn round_trip_pixels_no_stuffing() {
let bytes = [
0x0F, 0x13, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0,
0x10, 0x0A,
];
let seg = ObjectDataSegment::parse(&bytes).unwrap();
assert_eq!(seg.object_id, 10);
assert_eq!(seg.object_coding_method, ObjectCodingMethod::Pixels);
match &seg.payload {
ObjectDataPayload::InterlacedPixels(ip) => {
assert_eq!(ip.top_sub_blocks.len(), 2);
assert_eq!(ip.top_sub_blocks[0].data_type, DataType::EndOfLine);
assert_eq!(ip.top_sub_blocks[1].data_type, DataType::CodeString2Bit);
}
_ => panic!("expected InterlacedPixels"),
}
let out = seg.to_bytes();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
seg2.object_id = 20;
let out2 = seg2.to_bytes();
assert_ne!(out2, bytes);
let reparse = ObjectDataSegment::parse(&out2).unwrap();
assert_eq!(reparse.object_id, 20);
}
#[test]
fn preserves_multi_byte_stuffing() {
let bytes = [
0x0F, 0x13, 0x00, 0x01, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
let seg = ObjectDataSegment::parse(&bytes).unwrap();
match &seg.payload {
ObjectDataPayload::InterlacedPixels(ip) => {
assert_eq!(ip.stuffing, &[0x00, 0x00]);
}
_ => panic!("expected InterlacedPixels"),
}
let out = seg.to_bytes();
assert_eq!(out, bytes, "2 trailing stuffing bytes must round-trip");
}
#[test]
fn rejects_bad_sync_byte() {
let bytes = [
0x00, 0x13, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0,
0x10, 0x0A,
];
assert!(matches!(
ObjectDataSegment::parse(&bytes),
Err(Error::BadSyncByte(0x00))
));
}
#[test]
fn round_trip_multiple_sub_blocks() {
let map_table: [u8; 2] = [0x00, 0x00];
let four_bit_data = [0x11, 0x11, 0x11, 0x00]; let body: alloc::vec::Vec<u8> = [
&[0x00u8, 0x0B, 0x00][..],
&[0x00u8, 0x09, 0x00, 0x00][..], &[0x20u8][..],
&map_table[..],
&[0xF0u8][..],
&[0x11u8][..],
&four_bit_data[..],
]
.concat();
let mut bytes = alloc::vec![0x0F, 0x13, 0x00, 0x01]; let body_len = body.len() as u16;
bytes.extend_from_slice(&body_len.to_be_bytes());
bytes.extend_from_slice(&body);
let seg = ObjectDataSegment::parse(&bytes).unwrap();
match &seg.payload {
ObjectDataPayload::InterlacedPixels(ip) => {
assert_eq!(ip.top_sub_blocks.len(), 3, "expected 3 sub-blocks");
assert_eq!(ip.top_sub_blocks[0].data_type, DataType::MapTable2To4);
assert_eq!(ip.top_sub_blocks[0].data.len(), 2);
assert_eq!(ip.top_sub_blocks[1].data_type, DataType::EndOfLine);
assert_eq!(ip.top_sub_blocks[2].data_type, DataType::CodeString4Bit);
assert_eq!(ip.top_sub_blocks[2].data.len(), 4);
let sub = &ip.top_sub_blocks[2];
let scanned_len = scan_4bit_code_string(sub.data);
let mut it = sub.runs().expect("code string sub-block has runs()");
let mut iterations = 0;
while it.next().is_some() {
iterations += 1;
assert!(iterations < 1_000, "runaway iterator");
}
assert_eq!(it.bitpos.div_ceil(8), scanned_len);
}
_ => panic!("expected InterlacedPixels"),
}
let out = seg.to_bytes();
assert_eq!(out, bytes);
}
#[test]
fn runs_is_none_for_non_code_string_data_types() {
let map = PixelDataSubBlock {
data_type: DataType::MapTable2To4,
data: &[0x00, 0x00],
};
assert!(map.runs().is_none());
let eol = PixelDataSubBlock {
data_type: DataType::EndOfLine,
data: &[],
};
assert!(eol.runs().is_none());
}
#[test]
fn pixel_run_iter_2bit_exact() {
let direct = PixelDataSubBlock {
data_type: DataType::CodeString2Bit,
data: &[0x80],
};
let mut it = direct.runs().unwrap();
assert_eq!(
it.next(),
Some(PixelRun {
pixel_code: 2,
run_length: 1
})
);
assert_eq!(it.next(), None);
let run = PixelDataSubBlock {
data_type: DataType::CodeString2Bit,
data: &[0x37],
};
let mut it = run.runs().unwrap();
assert_eq!(
it.next(),
Some(PixelRun {
pixel_code: 3,
run_length: 8
})
);
}
#[test]
fn pixel_run_iter_8bit_exact() {
let data = [0x05, 0x00, 0x03, 0x2A, 0x00, 0x00];
let sub = PixelDataSubBlock {
data_type: DataType::CodeString8Bit,
data: &data,
};
let mut it = sub.runs().unwrap();
assert_eq!(
it.next(),
Some(PixelRun {
pixel_code: 5,
run_length: 1
})
);
assert_eq!(
it.next(),
Some(PixelRun {
pixel_code: 0,
run_length: 3
})
);
assert_eq!(
it.next(),
Some(PixelRun {
pixel_code: 0x2A,
run_length: 1
})
);
assert_eq!(it.next(), None);
assert_eq!(scan_8bit_code_string(&data), it.bitpos.div_ceil(8));
}
#[test]
fn round_trip_characters() {
let bytes = [
0x0F, 0x13, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, 0x04, 0x02, 0x00, 0x41, 0x00, 0x42,
];
let seg = ObjectDataSegment::parse(&bytes).unwrap();
assert_eq!(seg.object_coding_method, ObjectCodingMethod::Characters);
match &seg.payload {
ObjectDataPayload::Characters { character_codes } => {
assert_eq!(character_codes.len(), 2);
assert_eq!(character_codes[0], 0x0041);
assert_eq!(character_codes[1], 0x0042);
}
_ => panic!("expected Characters"),
}
let out = seg.to_bytes();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
if let ObjectDataPayload::Characters { character_codes } = &mut seg2.payload {
character_codes.push(0x0043);
}
let out2 = seg2.to_bytes();
assert_ne!(out2, bytes);
assert_eq!(
out2[HEADER_LEN + FIXED_LEN],
3,
"wire number_of_codes must track the new length"
);
let reparse = ObjectDataSegment::parse(&out2).unwrap();
match &reparse.payload {
ObjectDataPayload::Characters { character_codes } => {
assert_eq!(character_codes.as_slice(), &[0x0041, 0x0042, 0x0043]);
}
_ => panic!("expected Characters"),
}
}
#[test]
fn round_trip_progressive() {
let bytes = [
0x0F, 0x13, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x0C, 0x08, 0x00, 0x64, 0x00, 0x32, 0x00,
0x06, 0x78, 0xDA, 0x63, 0x60, 0x60, 0x60,
];
let seg = ObjectDataSegment::parse(&bytes).unwrap();
assert_eq!(
seg.object_coding_method,
ObjectCodingMethod::ProgressivePixels
);
match &seg.payload {
ObjectDataPayload::ProgressivePixels(pp) => {
assert_eq!(pp.bitmap_width, 100);
assert_eq!(pp.bitmap_height, 50);
assert_eq!(pp.compressed_data.len(), 6);
}
_ => panic!("expected ProgressivePixels"),
}
let out = seg.to_bytes();
assert_eq!(out, bytes);
let mut seg2 = seg.clone();
if let ObjectDataPayload::ProgressivePixels(pp) = &mut seg2.payload {
pp.bitmap_width = 200;
}
let out2 = seg2.to_bytes();
assert_ne!(out2, bytes);
let reparse = ObjectDataSegment::parse(&out2).unwrap();
match &reparse.payload {
ObjectDataPayload::ProgressivePixels(pp) => {
assert_eq!(pp.bitmap_width, 200);
}
_ => panic!(),
}
}
#[test]
fn oversized_segment_is_rejected_not_wrapped() {
let big = alloc::vec![0u8; 65_536];
let seg = ObjectDataSegment {
page_id: 1,
object_id: 1,
object_version_number: 0,
object_coding_method: ObjectCodingMethod::ProgressivePixels,
non_modifying_colour_flag: false,
payload: ObjectDataPayload::ProgressivePixels(ProgressivePixelBlock {
bitmap_width: 1,
bitmap_height: 1,
compressed_data: &big,
}),
};
let mut buf = alloc::vec![0u8; 65_600];
assert_eq!(seg.serialize_into(&mut buf), Err(Error::SegmentTooLarge));
}
}