1use crate::error::{Error, Result};
7use broadcast_common::{Parse, Serialize};
8
9pub const SEGMENT_TYPE: u8 = 0x13;
11pub const HEADER_LEN: usize = 6;
13pub const FIXED_LEN: usize = 3;
15pub const INTERLACE_LEN_LEN: usize = 4;
17pub const PROGRESSIVE_HEADER_LEN: usize = 6;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23#[repr(u8)]
24#[non_exhaustive]
25pub enum ObjectCodingMethod {
26 Pixels = 0x00,
28 Characters = 0x01,
30 ProgressivePixels = 0x02,
32 Reserved(u8),
34}
35
36impl ObjectCodingMethod {
37 #[must_use]
39 pub fn name(&self) -> &'static str {
40 match self {
41 Self::Pixels => "pixels",
42 Self::Characters => "characters",
43 Self::ProgressivePixels => "progressive_pixels",
44 Self::Reserved(_) => "reserved",
45 }
46 }
47
48 fn to_bits(self) -> u8 {
49 match self {
50 Self::Pixels => 0x00,
51 Self::Characters => 0x01,
52 Self::ProgressivePixels => 0x02,
53 Self::Reserved(v) => v & 0x03,
54 }
55 }
56}
57
58broadcast_common::impl_spec_display!(ObjectCodingMethod, Reserved);
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize))]
63#[repr(u8)]
64#[non_exhaustive]
65pub enum DataType {
66 CodeString2Bit = 0x10,
68 CodeString4Bit = 0x11,
70 CodeString8Bit = 0x12,
72 MapTable2To4 = 0x20,
74 MapTable2To8 = 0x21,
76 MapTable4To8 = 0x22,
78 EndOfLine = 0xF0,
80 Reserved(u8),
82}
83
84impl DataType {
85 #[must_use]
87 pub fn name(&self) -> &'static str {
88 match self {
89 Self::CodeString2Bit => "2-bit_code_string",
90 Self::CodeString4Bit => "4-bit_code_string",
91 Self::CodeString8Bit => "8-bit_code_string",
92 Self::MapTable2To4 => "2_to_4_map_table",
93 Self::MapTable2To8 => "2_to_8_map_table",
94 Self::MapTable4To8 => "4_to_8_map_table",
95 Self::EndOfLine => "end_of_line",
96 Self::Reserved(_) => "reserved",
97 }
98 }
99}
100
101broadcast_common::impl_spec_display!(DataType, Reserved);
102
103const MAP_TABLE_2TO4_BYTES: usize = 2;
105const MAP_TABLE_2TO8_BYTES: usize = 4;
107const MAP_TABLE_4TO8_BYTES: usize = 16;
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113pub struct PixelDataSubBlock<'a> {
114 pub data_type: DataType,
116 #[cfg_attr(feature = "serde", serde(skip))]
121 pub data: &'a [u8],
122}
123
124impl PixelDataSubBlock<'_> {
125 fn serialized_len(&self) -> usize {
126 1 + self.data.len()
127 }
128
129 fn serialize_into(&self, buf: &mut [u8]) {
130 buf[0] = match self.data_type {
131 DataType::CodeString2Bit => 0x10,
132 DataType::CodeString4Bit => 0x11,
133 DataType::CodeString8Bit => 0x12,
134 DataType::MapTable2To4 => 0x20,
135 DataType::MapTable2To8 => 0x21,
136 DataType::MapTable4To8 => 0x22,
137 DataType::EndOfLine => 0xF0,
138 DataType::Reserved(v) => v,
139 };
140 buf[1..1 + self.data.len()].copy_from_slice(self.data);
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize))]
147pub struct InterlacedPixelsData<'a> {
148 #[cfg_attr(feature = "serde", serde(borrow))]
150 pub top_sub_blocks: alloc::vec::Vec<PixelDataSubBlock<'a>>,
151 #[cfg_attr(feature = "serde", serde(borrow))]
153 pub bottom_sub_blocks: alloc::vec::Vec<PixelDataSubBlock<'a>>,
154 pub stuffing_byte: Option<u8>,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize))]
161pub struct ProgressivePixelBlock<'a> {
162 pub bitmap_width: u16,
164 pub bitmap_height: u16,
166 #[cfg_attr(feature = "serde", serde(skip))]
168 pub compressed_data: &'a [u8],
169}
170
171#[derive(Debug, Clone, PartialEq, Eq)]
173#[cfg_attr(feature = "serde", derive(serde::Serialize))]
174#[non_exhaustive]
175pub enum ObjectDataPayload<'a> {
176 #[cfg_attr(feature = "serde", serde(borrow))]
178 InterlacedPixels(InterlacedPixelsData<'a>),
179 Characters {
181 number_of_codes: u8,
183 character_codes: alloc::vec::Vec<u16>,
185 },
186 #[cfg_attr(feature = "serde", serde(borrow))]
188 ProgressivePixels(ProgressivePixelBlock<'a>),
189 Reserved {
191 coding_method: u8,
193 #[cfg_attr(feature = "serde", serde(skip))]
195 data: &'a [u8],
196 },
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize))]
202pub struct ObjectDataSegment<'a> {
203 pub page_id: u16,
205 pub object_id: u16,
207 pub object_version_number: u8,
209 pub object_coding_method: ObjectCodingMethod,
211 pub non_modifying_colour_flag: bool,
213 #[cfg_attr(feature = "serde", serde(borrow))]
215 pub payload: ObjectDataPayload<'a>,
216}
217
218fn scan_2bit_code_string(data: &[u8]) -> usize {
222 let mut bitpos: usize = 0;
223 let bits = |bp: usize, n: usize| -> u8 {
224 let byte_idx = bp / 8;
225 let bit_idx = bp % 8;
226 if byte_idx >= data.len() {
227 return 0;
228 }
229 if bit_idx + n <= 8 {
230 (data[byte_idx] >> (8 - bit_idx - n)) & ((1u16 << n) - 1) as u8
231 } else {
232 let first_bits = 8 - bit_idx;
233 let v1 = ((data[byte_idx] & ((1u8 << first_bits) - 1)) as u16) << (n - first_bits);
234 let v2 = if byte_idx + 1 < data.len() {
235 (data[byte_idx + 1] >> (8 - (n - first_bits))) as u16
236 } else {
237 0
238 };
239 (v1 | v2) as u8
240 }
241 };
242 loop {
243 let b2 = bits(bitpos, 2);
244 bitpos += 2;
245 if b2 != 0 {
246 continue;
247 }
248 let s1 = bits(bitpos, 1);
250 bitpos += 1;
251 if s1 == 1 {
252 bitpos += 3 + 2;
254 continue;
255 }
256 let s2 = bits(bitpos, 1);
257 bitpos += 1;
258 if s2 == 1 {
259 continue;
261 }
262 let s3 = bits(bitpos, 2);
263 bitpos += 2;
264 match s3 {
265 0b00 => break, 0b01 => continue, 0b10 => {
268 bitpos += 4 + 2; }
270 0b11 => {
271 bitpos += 8 + 2; }
273 _ => {}
274 }
275 }
276 let byte_len = bitpos.div_ceil(8);
278 byte_len.min(data.len())
279}
280
281fn scan_4bit_code_string(data: &[u8]) -> usize {
285 let mut bitpos: usize = 0;
286 let bits = |bp: usize, n: usize| -> u8 {
287 let byte_idx = bp / 8;
288 let bit_idx = bp % 8;
289 if byte_idx >= data.len() {
290 return 0;
291 }
292 if bit_idx + n <= 8 {
293 (data[byte_idx] >> (8 - bit_idx - n)) & ((1u16 << n) - 1) as u8
294 } else {
295 let first_bits = 8 - bit_idx;
296 let v1 = ((data[byte_idx] & ((1u8 << first_bits) - 1)) as u16) << (n - first_bits);
297 let v2 = if byte_idx + 1 < data.len() {
298 (data[byte_idx + 1] >> (8 - (n - first_bits))) as u16
299 } else {
300 0
301 };
302 (v1 | v2) as u8
303 }
304 };
305 loop {
306 let b4 = bits(bitpos, 4);
307 bitpos += 4;
308 if b4 != 0 {
309 continue;
310 }
311 let s1 = bits(bitpos, 1);
313 bitpos += 1;
314 if s1 == 0 {
315 let n3 = bits(bitpos, 3);
316 bitpos += 3;
317 if n3 == 0 {
318 break;
320 }
321 continue;
323 }
324 let s2 = bits(bitpos, 1);
325 bitpos += 1;
326 if s2 == 0 {
327 bitpos += 2 + 4;
329 continue;
330 }
331 let s3 = bits(bitpos, 2);
332 bitpos += 2;
333 match s3 {
334 0b00 | 0b01 => continue, 0b10 => {
336 bitpos += 4 + 4; }
338 0b11 => {
339 bitpos += 8 + 4; }
341 _ => {}
342 }
343 }
344 let byte_len = bitpos.div_ceil(8);
346 byte_len.min(data.len())
347}
348
349fn scan_8bit_code_string(data: &[u8]) -> usize {
352 let mut pos: usize = 0;
353 loop {
354 if pos >= data.len() {
355 break;
356 }
357 if data[pos] != 0x00 {
358 pos += 1;
359 continue;
360 }
361 if pos + 1 >= data.len() {
362 pos += 1;
363 break;
364 }
365 let b = data[pos + 1];
366 let s1 = (b >> 7) & 1;
367 if s1 == 0 {
368 let rl = b & 0x7F;
369 if rl == 0 {
370 pos += 2;
372 break;
373 }
374 pos += 2;
376 } else {
377 pos += 3;
379 }
380 }
381 pos.min(data.len())
382}
383
384fn parse_pixel_sub_blocks<'a>(
389 bytes: &'a [u8],
390 field_len: usize,
391 what: &'static str,
392) -> Result<alloc::vec::Vec<PixelDataSubBlock<'a>>> {
393 let end = field_len.min(bytes.len());
394 let field = &bytes[..end];
395 let mut blocks = alloc::vec::Vec::new();
396 let mut pos: usize = 0;
397 while pos < field.len() {
398 if pos >= field.len() {
399 break;
400 }
401 let data_type_byte = field[pos];
402 let data_type = match data_type_byte {
403 0x10 => DataType::CodeString2Bit,
404 0x11 => DataType::CodeString4Bit,
405 0x12 => DataType::CodeString8Bit,
406 0x20 => DataType::MapTable2To4,
407 0x21 => DataType::MapTable2To8,
408 0x22 => DataType::MapTable4To8,
409 0xF0 => DataType::EndOfLine,
410 v => DataType::Reserved(v),
411 };
412 pos += 1; let data_len = match data_type_byte {
415 0x20 => MAP_TABLE_2TO4_BYTES,
416 0x21 => MAP_TABLE_2TO8_BYTES,
417 0x22 => MAP_TABLE_4TO8_BYTES,
418 0xF0 => 0,
419 0x10 => scan_2bit_code_string(&field[pos..]),
420 0x11 => scan_4bit_code_string(&field[pos..]),
421 0x12 => scan_8bit_code_string(&field[pos..]),
422 _ => {
423 field.len() - pos
425 }
426 };
427
428 let block_end = (pos + data_len).min(field.len());
429 let block_data = &field[pos..block_end];
430
431 blocks.push(PixelDataSubBlock {
432 data_type,
433 data: block_data,
434 });
435 pos = block_end;
436 }
437 if pos != end {
439 return Err(Error::BufferTooShort {
440 need: end,
441 have: pos,
442 what,
443 });
444 }
445 Ok(blocks)
446}
447
448impl<'a> Parse<'a> for ObjectDataSegment<'a> {
449 type Error = Error;
450
451 fn parse(bytes: &'a [u8]) -> Result<Self> {
452 if bytes.len() < HEADER_LEN + FIXED_LEN {
453 return Err(Error::BufferTooShort {
454 need: HEADER_LEN + FIXED_LEN,
455 have: bytes.len(),
456 what: "object_data_segment",
457 });
458 }
459 if bytes[1] != SEGMENT_TYPE {
460 return Err(Error::UnknownSegmentType(bytes[1]));
461 }
462 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
463 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
464 let total = HEADER_LEN + segment_length;
465 if bytes.len() < total {
466 return Err(Error::BufferTooShort {
467 need: total,
468 have: bytes.len(),
469 what: "object_data_segment data",
470 });
471 }
472 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
473 if body.len() < FIXED_LEN {
474 return Err(Error::BufferTooShort {
475 need: FIXED_LEN,
476 have: body.len(),
477 what: "object_data_segment body",
478 });
479 }
480 let object_id = u16::from_be_bytes([body[0], body[1]]);
481 let object_version_number = body[2] >> 4;
482 let coding_method_bits = (body[2] >> 2) & 0x03;
483 let non_modifying_colour_flag = (body[2] & 0x02) != 0;
484 let reserved = body[2] & 0x01;
485 let _ = reserved;
487
488 let object_coding_method = match coding_method_bits {
489 0x00 => ObjectCodingMethod::Pixels,
490 0x01 => ObjectCodingMethod::Characters,
491 0x02 => ObjectCodingMethod::ProgressivePixels,
492 v => ObjectCodingMethod::Reserved(v),
493 };
494
495 let payload_data = &body[FIXED_LEN..];
496
497 let payload = match coding_method_bits {
498 0x00 => {
499 if payload_data.len() < INTERLACE_LEN_LEN {
500 return Err(Error::BufferTooShort {
501 need: INTERLACE_LEN_LEN,
502 have: payload_data.len(),
503 what: "object_data interlace lengths",
504 });
505 }
506 let top_len = u16::from_be_bytes([payload_data[0], payload_data[1]]) as usize;
507 let bottom_len = u16::from_be_bytes([payload_data[2], payload_data[3]]) as usize;
508
509 let top_start = INTERLACE_LEN_LEN;
510 let top_end = top_start + top_len;
511 let bottom_start = top_end;
512 let bottom_end = bottom_start + bottom_len;
513
514 if payload_data.len() < bottom_end {
515 return Err(Error::BufferTooShort {
516 need: bottom_end,
517 have: payload_data.len(),
518 what: "object_data pixel sub-blocks",
519 });
520 }
521
522 let stuffing_length = segment_length
527 .wrapping_sub(7)
528 .wrapping_sub(top_len)
529 .wrapping_sub(bottom_len);
530
531 let mut stuffing_byte = None;
532 if stuffing_length == 1 {
533 if bottom_end + 1 > payload_data.len() {
534 return Err(Error::BufferTooShort {
535 need: bottom_end + 1,
536 have: payload_data.len(),
537 what: "stuffing byte",
538 });
539 }
540 if payload_data[bottom_end] != 0x00 {
541 return Err(Error::BadStuffingByte(payload_data[bottom_end]));
542 }
543 stuffing_byte = Some(payload_data[bottom_end]);
544 let _ = stuffing_byte;
545 }
547
548 let top_sub_blocks = parse_pixel_sub_blocks(
549 &payload_data[top_start..top_end],
550 top_len,
551 "top pixel sub-blocks",
552 )?;
553 let bottom_sub_blocks = if bottom_len > 0 {
554 parse_pixel_sub_blocks(
555 &payload_data[bottom_start..bottom_end],
556 bottom_len,
557 "bottom pixel sub-blocks",
558 )?
559 } else {
560 alloc::vec::Vec::new()
561 };
562
563 ObjectDataPayload::InterlacedPixels(InterlacedPixelsData {
564 top_sub_blocks,
565 bottom_sub_blocks,
566 stuffing_byte,
567 })
568 }
569 0x01 => {
570 if payload_data.is_empty() {
571 return Err(Error::BufferTooShort {
572 need: 1,
573 have: 0,
574 what: "character count",
575 });
576 }
577 let number_of_codes = payload_data[0] as usize;
578 let codes_len = 1 + number_of_codes * 2;
579 if payload_data.len() < codes_len {
580 return Err(Error::BufferTooShort {
581 need: codes_len,
582 have: payload_data.len(),
583 what: "character codes",
584 });
585 }
586 let mut codes = alloc::vec::Vec::with_capacity(number_of_codes);
587 for i in 0..number_of_codes {
588 let ci = 1 + i * 2;
589 codes.push(u16::from_be_bytes([payload_data[ci], payload_data[ci + 1]]));
590 }
591 ObjectDataPayload::Characters {
592 number_of_codes: number_of_codes as u8,
593 character_codes: codes,
594 }
595 }
596 0x02 => {
597 if payload_data.len() < PROGRESSIVE_HEADER_LEN {
598 return Err(Error::BufferTooShort {
599 need: PROGRESSIVE_HEADER_LEN,
600 have: payload_data.len(),
601 what: "progressive pixel block",
602 });
603 }
604 let bitmap_width = u16::from_be_bytes([payload_data[0], payload_data[1]]);
605 let bitmap_height = u16::from_be_bytes([payload_data[2], payload_data[3]]);
606 let compressed_len =
607 u16::from_be_bytes([payload_data[4], payload_data[5]]) as usize;
608 if payload_data.len() < PROGRESSIVE_HEADER_LEN + compressed_len {
609 return Err(Error::BufferTooShort {
610 need: PROGRESSIVE_HEADER_LEN + compressed_len,
611 have: payload_data.len(),
612 what: "compressed bitmap data",
613 });
614 }
615 ObjectDataPayload::ProgressivePixels(ProgressivePixelBlock {
616 bitmap_width,
617 bitmap_height,
618 compressed_data: &payload_data
619 [PROGRESSIVE_HEADER_LEN..PROGRESSIVE_HEADER_LEN + compressed_len],
620 })
621 }
622 _ => ObjectDataPayload::Reserved {
623 coding_method: coding_method_bits,
624 data: payload_data,
625 },
626 };
627
628 Ok(ObjectDataSegment {
629 page_id,
630 object_id,
631 object_version_number,
632 object_coding_method,
633 non_modifying_colour_flag,
634 payload,
635 })
636 }
637}
638
639impl Serialize for ObjectDataSegment<'_> {
640 type Error = Error;
641
642 fn serialized_len(&self) -> usize {
643 let body_len = FIXED_LEN
644 + match &self.payload {
645 ObjectDataPayload::InterlacedPixels(ip) => {
646 let top_len: usize = ip.top_sub_blocks.iter().map(|s| s.serialized_len()).sum();
647 let bottom_len: usize = ip
648 .bottom_sub_blocks
649 .iter()
650 .map(|s| s.serialized_len())
651 .sum();
652 INTERLACE_LEN_LEN
653 + top_len
654 + bottom_len
655 + if ip.stuffing_byte.is_some() { 1 } else { 0 }
656 }
657 ObjectDataPayload::Characters {
658 character_codes, ..
659 } => 1 + character_codes.len() * 2,
660 ObjectDataPayload::ProgressivePixels(pp) => {
661 PROGRESSIVE_HEADER_LEN + pp.compressed_data.len()
662 }
663 ObjectDataPayload::Reserved { data, .. } => data.len(),
664 };
665 HEADER_LEN + body_len
666 }
667
668 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
669 let len = self.serialized_len();
670 if buf.len() < len {
671 return Err(Error::BufferTooShort {
672 need: len,
673 have: buf.len(),
674 what: "object_data_segment serialize",
675 });
676 }
677 buf[0] = 0x0F;
678 buf[1] = SEGMENT_TYPE;
679 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
680 let seg_len = (len - HEADER_LEN) as u16;
681 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
682
683 buf[6..8].copy_from_slice(&self.object_id.to_be_bytes());
684 buf[8] = (self.object_version_number << 4)
685 | (self.object_coding_method.to_bits() << 2)
686 | (u8::from(self.non_modifying_colour_flag) << 1);
687
688 let mut off = HEADER_LEN + FIXED_LEN;
689 match &self.payload {
690 ObjectDataPayload::InterlacedPixels(ip) => {
691 let top_len: u16 = ip
692 .top_sub_blocks
693 .iter()
694 .map(|s| s.serialized_len() as u16)
695 .sum();
696 let bottom_len: u16 = ip
697 .bottom_sub_blocks
698 .iter()
699 .map(|s| s.serialized_len() as u16)
700 .sum();
701 buf[off..off + 2].copy_from_slice(&top_len.to_be_bytes());
702 buf[off + 2..off + 4].copy_from_slice(&bottom_len.to_be_bytes());
703 off += INTERLACE_LEN_LEN;
704 for sub in &ip.top_sub_blocks {
705 sub.serialize_into(&mut buf[off..]);
706 off += sub.serialized_len();
707 }
708 for sub in &ip.bottom_sub_blocks {
709 sub.serialize_into(&mut buf[off..]);
710 off += sub.serialized_len();
711 }
712 if ip.stuffing_byte.is_some() {
713 buf[off] = 0x00;
714 off += 1;
715 }
716 }
717 ObjectDataPayload::Characters {
718 number_of_codes,
719 character_codes,
720 } => {
721 buf[off] = *number_of_codes;
722 off += 1;
723 for code in character_codes {
724 buf[off..off + 2].copy_from_slice(&code.to_be_bytes());
725 off += 2;
726 }
727 }
728 ObjectDataPayload::ProgressivePixels(pp) => {
729 buf[off..off + 2].copy_from_slice(&pp.bitmap_width.to_be_bytes());
730 buf[off + 2..off + 4].copy_from_slice(&pp.bitmap_height.to_be_bytes());
731 let clen = pp.compressed_data.len() as u16;
732 buf[off + 4..off + 6].copy_from_slice(&clen.to_be_bytes());
733 off += PROGRESSIVE_HEADER_LEN;
734 buf[off..off + pp.compressed_data.len()].copy_from_slice(pp.compressed_data);
735 off += pp.compressed_data.len();
736 }
737 ObjectDataPayload::Reserved { data, .. } => {
738 buf[off..off + data.len()].copy_from_slice(data);
739 off += data.len();
740 }
741 }
742 debug_assert_eq!(off, len);
743 Ok(len)
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750 use broadcast_common::{Parse, Serialize};
751
752 #[test]
753 fn round_trip_pixels_no_stuffing() {
754 let bytes = [
755 0x0F, 0x13, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0,
756 0x10, 0x0A,
757 ];
758 let seg = ObjectDataSegment::parse(&bytes).unwrap();
759 assert_eq!(seg.object_id, 10);
760 assert_eq!(seg.object_coding_method, ObjectCodingMethod::Pixels);
761 match &seg.payload {
762 ObjectDataPayload::InterlacedPixels(ip) => {
763 assert_eq!(ip.top_sub_blocks.len(), 2);
764 assert_eq!(ip.top_sub_blocks[0].data_type, DataType::EndOfLine);
765 assert_eq!(ip.top_sub_blocks[1].data_type, DataType::CodeString2Bit);
766 }
767 _ => panic!("expected InterlacedPixels"),
768 }
769 let out = seg.to_bytes();
770 assert_eq!(out, bytes);
771
772 let mut seg2 = seg.clone();
774 seg2.object_id = 20;
775 let out2 = seg2.to_bytes();
776 assert_ne!(out2, bytes);
777 let reparse = ObjectDataSegment::parse(&out2).unwrap();
778 assert_eq!(reparse.object_id, 20);
779 }
780
781 #[test]
782 fn round_trip_multiple_sub_blocks() {
783 let map_table: [u8; 2] = [0x00, 0x00];
785 let four_bit_data = [0x11, 0x11, 0x11, 0x00]; let body: alloc::vec::Vec<u8> = [
789 &[0x00u8, 0x0B, 0x00][..],
790 &[0x00u8, 0x09, 0x00, 0x00][..], &[0x20u8][..],
792 &map_table[..],
793 &[0xF0u8][..],
794 &[0x11u8][..],
795 &four_bit_data[..],
796 ]
797 .concat();
798
799 let mut bytes = alloc::vec![0x0F, 0x13, 0x00, 0x01]; let body_len = body.len() as u16;
801 bytes.extend_from_slice(&body_len.to_be_bytes());
802 bytes.extend_from_slice(&body);
803
804 let seg = ObjectDataSegment::parse(&bytes).unwrap();
805 match &seg.payload {
806 ObjectDataPayload::InterlacedPixels(ip) => {
807 assert_eq!(ip.top_sub_blocks.len(), 3, "expected 3 sub-blocks");
808 assert_eq!(ip.top_sub_blocks[0].data_type, DataType::MapTable2To4);
809 assert_eq!(ip.top_sub_blocks[0].data.len(), 2);
810 assert_eq!(ip.top_sub_blocks[1].data_type, DataType::EndOfLine);
811 assert_eq!(ip.top_sub_blocks[2].data_type, DataType::CodeString4Bit);
812 assert_eq!(ip.top_sub_blocks[2].data.len(), 4);
813 }
814 _ => panic!("expected InterlacedPixels"),
815 }
816 let out = seg.to_bytes();
817 assert_eq!(out, bytes);
818 }
819
820 #[test]
821 fn round_trip_characters() {
822 let bytes = [
823 0x0F, 0x13, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, 0x04, 0x02, 0x00, 0x41, 0x00, 0x42,
824 ];
825 let seg = ObjectDataSegment::parse(&bytes).unwrap();
826 assert_eq!(seg.object_coding_method, ObjectCodingMethod::Characters);
827 match &seg.payload {
828 ObjectDataPayload::Characters {
829 number_of_codes,
830 character_codes,
831 ..
832 } => {
833 assert_eq!(*number_of_codes, 2);
834 assert_eq!(character_codes[0], 0x0041);
835 assert_eq!(character_codes[1], 0x0042);
836 }
837 _ => panic!("expected Characters"),
838 }
839 let out = seg.to_bytes();
840 assert_eq!(out, bytes);
841
842 let mut seg2 = seg.clone();
844 if let ObjectDataPayload::Characters {
845 number_of_codes, ..
846 } = &mut seg2.payload
847 {
848 *number_of_codes = 1;
849 }
850 let out2 = seg2.to_bytes();
851 assert_ne!(out2, bytes);
852 }
853
854 #[test]
855 fn round_trip_progressive() {
856 let bytes = [
857 0x0F, 0x13, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x0C, 0x08, 0x00, 0x64, 0x00, 0x32, 0x00,
858 0x06, 0x78, 0xDA, 0x63, 0x60, 0x60, 0x60,
859 ];
860 let seg = ObjectDataSegment::parse(&bytes).unwrap();
861 assert_eq!(
862 seg.object_coding_method,
863 ObjectCodingMethod::ProgressivePixels
864 );
865 match &seg.payload {
866 ObjectDataPayload::ProgressivePixels(pp) => {
867 assert_eq!(pp.bitmap_width, 100);
868 assert_eq!(pp.bitmap_height, 50);
869 assert_eq!(pp.compressed_data.len(), 6);
870 }
871 _ => panic!("expected ProgressivePixels"),
872 }
873 let out = seg.to_bytes();
874 assert_eq!(out, bytes);
875
876 let mut seg2 = seg.clone();
878 if let ObjectDataPayload::ProgressivePixels(pp) = &mut seg2.payload {
879 pp.bitmap_width = 200;
880 }
881 let out2 = seg2.to_bytes();
882 assert_ne!(out2, bytes);
883 let reparse = ObjectDataSegment::parse(&out2).unwrap();
884 match &reparse.payload {
885 ObjectDataPayload::ProgressivePixels(pp) => {
886 assert_eq!(pp.bitmap_width, 200);
887 }
888 _ => panic!(),
889 }
890 }
891}