1pub(crate) mod colors;
2pub(crate) mod input;
3
4pub use colors::*;
5pub use input::*;
6
7use std::io::{self, BufReader, Read, Write};
8
9use clap::ValueEnum;
10
11pub enum Base {
12 Binary,
13 Octal,
14 Decimal,
15 Hexadecimal,
16}
17
18#[derive(Copy, Clone)]
19pub enum ByteCategory {
20 Null,
21 AsciiPrintable,
22 AsciiWhitespace,
23 AsciiOther,
24 NonAscii,
25}
26
27#[derive(Copy, Clone, Debug, Default, ValueEnum)]
28#[non_exhaustive]
29pub enum CharacterTable {
30 #[default]
34 Default,
35
36 Ascii,
38
39 #[value(name = "codepage-1047")]
41 CP1047,
42
43 #[value(name = "codepage-437")]
45 CP437,
46}
47
48#[derive(Copy, Clone, Debug, Default, ValueEnum)]
49pub enum Endianness {
50 Little,
52
53 #[default]
55 Big,
56}
57
58#[derive(PartialEq)]
59enum Squeezer {
60 Print,
61 Delete,
62 Ignore,
63 Disabled,
64}
65
66#[derive(Copy, Clone)]
67struct Byte(u8);
68
69impl Byte {
70 fn category(self) -> ByteCategory {
71 if self.0 == 0x00 {
72 ByteCategory::Null
73 } else if self.0.is_ascii_graphic() {
74 ByteCategory::AsciiPrintable
75 } else if self.0.is_ascii_whitespace() {
76 ByteCategory::AsciiWhitespace
77 } else if self.0.is_ascii() {
78 ByteCategory::AsciiOther
79 } else {
80 ByteCategory::NonAscii
81 }
82 }
83
84 fn color(self) -> &'static [u8] {
85 use crate::ByteCategory::*;
86 match self.category() {
87 Null => COLOR_NULL,
88 AsciiPrintable => COLOR_ASCII_PRINTABLE,
89 AsciiWhitespace => COLOR_ASCII_WHITESPACE,
90 AsciiOther => COLOR_ASCII_OTHER,
91 NonAscii => COLOR_NONASCII,
92 }
93 }
94
95 fn as_char(self, character_table: CharacterTable) -> char {
96 use crate::ByteCategory::*;
97 match character_table {
98 CharacterTable::Default => match self.category() {
99 Null => '⋄',
100 AsciiPrintable => self.0 as char,
101 AsciiWhitespace if self.0 == 0x20 => ' ',
102 AsciiWhitespace => '_',
103 AsciiOther => '•',
104 NonAscii => '×',
105 },
106 CharacterTable::Ascii => match self.category() {
107 Null => '.',
108 AsciiPrintable => self.0 as char,
109 AsciiWhitespace if self.0 == 0x20 => ' ',
110 AsciiWhitespace => '.',
111 AsciiOther => '.',
112 NonAscii => '.',
113 },
114 CharacterTable::CP1047 => CP1047[self.0 as usize],
115 CharacterTable::CP437 => CP437[self.0 as usize],
116 }
117 }
118}
119
120struct BorderElements {
121 left_corner: char,
122 horizontal_line: char,
123 column_separator: char,
124 right_corner: char,
125}
126
127#[derive(Clone, Copy, Debug, Default, ValueEnum)]
128pub enum BorderStyle {
129 #[default]
131 Unicode,
132
133 Ascii,
135
136 None,
138}
139
140impl BorderStyle {
141 fn header_elems(&self) -> Option<BorderElements> {
142 match self {
143 BorderStyle::Unicode => Some(BorderElements {
144 left_corner: '┌',
145 horizontal_line: '─',
146 column_separator: '┬',
147 right_corner: '┐',
148 }),
149 BorderStyle::Ascii => Some(BorderElements {
150 left_corner: '+',
151 horizontal_line: '-',
152 column_separator: '+',
153 right_corner: '+',
154 }),
155 BorderStyle::None => None,
156 }
157 }
158
159 fn footer_elems(&self) -> Option<BorderElements> {
160 match self {
161 BorderStyle::Unicode => Some(BorderElements {
162 left_corner: '└',
163 horizontal_line: '─',
164 column_separator: '┴',
165 right_corner: '┘',
166 }),
167 BorderStyle::Ascii => Some(BorderElements {
168 left_corner: '+',
169 horizontal_line: '-',
170 column_separator: '+',
171 right_corner: '+',
172 }),
173 BorderStyle::None => None,
174 }
175 }
176
177 fn outer_sep(&self) -> char {
178 match self {
179 BorderStyle::Unicode => '│',
180 BorderStyle::Ascii => '|',
181 BorderStyle::None => ' ',
182 }
183 }
184
185 fn inner_sep(&self) -> char {
186 match self {
187 BorderStyle::Unicode => '┊',
188 BorderStyle::Ascii => '|',
189 BorderStyle::None => ' ',
190 }
191 }
192}
193
194pub struct PrinterBuilder<'a, Writer: Write> {
195 writer: &'a mut Writer,
196 show_color: bool,
197 show_char_panel: bool,
198 show_position_panel: bool,
199 border_style: BorderStyle,
200 use_squeeze: bool,
201 panels: u64,
202 group_size: u8,
203 base: Base,
204 endianness: Endianness,
205 character_table: CharacterTable,
206}
207
208impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
209 pub fn new(writer: &'a mut Writer) -> Self {
210 PrinterBuilder {
211 writer,
212 show_color: true,
213 show_char_panel: true,
214 show_position_panel: true,
215 border_style: BorderStyle::Unicode,
216 use_squeeze: true,
217 panels: 2,
218 group_size: 1,
219 base: Base::Hexadecimal,
220 endianness: Endianness::Big,
221 character_table: CharacterTable::Default,
222 }
223 }
224
225 pub fn show_color(mut self, show_color: bool) -> Self {
226 self.show_color = show_color;
227 self
228 }
229
230 pub fn show_char_panel(mut self, show_char_panel: bool) -> Self {
231 self.show_char_panel = show_char_panel;
232 self
233 }
234
235 pub fn show_position_panel(mut self, show_position_panel: bool) -> Self {
236 self.show_position_panel = show_position_panel;
237 self
238 }
239
240 pub fn with_border_style(mut self, border_style: BorderStyle) -> Self {
241 self.border_style = border_style;
242 self
243 }
244
245 pub fn enable_squeezing(mut self, enable: bool) -> Self {
246 self.use_squeeze = enable;
247 self
248 }
249
250 pub fn num_panels(mut self, num: u64) -> Self {
251 self.panels = num;
252 self
253 }
254
255 pub fn group_size(mut self, num: u8) -> Self {
256 self.group_size = num;
257 self
258 }
259
260 pub fn with_base(mut self, base: Base) -> Self {
261 self.base = base;
262 self
263 }
264
265 pub fn endianness(mut self, endianness: Endianness) -> Self {
266 self.endianness = endianness;
267 self
268 }
269
270 pub fn character_table(mut self, character_table: CharacterTable) -> Self {
271 self.character_table = character_table;
272 self
273 }
274
275 pub fn build(self) -> Printer<'a, Writer> {
276 Printer::new(
277 self.writer,
278 self.show_color,
279 self.show_char_panel,
280 self.show_position_panel,
281 self.border_style,
282 self.use_squeeze,
283 self.panels,
284 self.group_size,
285 self.base,
286 self.endianness,
287 self.character_table,
288 )
289 }
290}
291
292pub struct Printer<'a, Writer: Write> {
293 idx: u64,
294 line_buf: Vec<u8>,
296 writer: &'a mut Writer,
297 show_char_panel: bool,
298 show_position_panel: bool,
299 show_color: bool,
300 curr_color: Option<&'static [u8]>,
301 border_style: BorderStyle,
302 byte_hex_panel: Vec<String>,
303 byte_char_panel: Vec<String>,
304 byte_hex_panel_g: Vec<String>,
306 squeezer: Squeezer,
307 display_offset: u64,
308 panels: u64,
310 squeeze_byte: usize,
311 group_size: u8,
313 base_digits: u8,
315 endianness: Endianness,
317}
318
319impl<'a, Writer: Write> Printer<'a, Writer> {
320 fn new(
321 writer: &'a mut Writer,
322 show_color: bool,
323 show_char_panel: bool,
324 show_position_panel: bool,
325 border_style: BorderStyle,
326 use_squeeze: bool,
327 panels: u64,
328 group_size: u8,
329 base: Base,
330 endianness: Endianness,
331 character_table: CharacterTable,
332 ) -> Printer<'a, Writer> {
333 Printer {
334 idx: 0,
335 line_buf: vec![0x0; 8 * panels as usize],
336 writer,
337 show_char_panel,
338 show_position_panel,
339 show_color,
340 curr_color: None,
341 border_style,
342 byte_hex_panel: (0u8..=u8::MAX)
343 .map(|i| match base {
344 Base::Binary => format!("{i:08b}"),
345 Base::Octal => format!("{i:03o}"),
346 Base::Decimal => format!("{i:03}"),
347 Base::Hexadecimal => format!("{i:02x}"),
348 })
349 .collect(),
350 byte_char_panel: (0u8..=u8::MAX)
351 .map(|i| format!("{}", Byte(i).as_char(character_table)))
352 .collect(),
353 byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(),
354 squeezer: if use_squeeze {
355 Squeezer::Ignore
356 } else {
357 Squeezer::Disabled
358 },
359 display_offset: 0,
360 panels,
361 squeeze_byte: 0x00,
362 group_size,
363 base_digits: match base {
364 Base::Binary => 8,
365 Base::Octal => 3,
366 Base::Decimal => 3,
367 Base::Hexadecimal => 2,
368 },
369 endianness,
370 }
371 }
372
373 pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
374 self.display_offset = display_offset;
375 self
376 }
377
378 fn panel_sz(&self) -> usize {
379 let group_sz = self.base_digits as usize * self.group_size as usize + 1;
381 let group_per_panel = 8 / self.group_size as usize;
382 1 + group_sz * group_per_panel
384 }
385
386 fn write_border(&mut self, border_elements: BorderElements) -> io::Result<()> {
387 let h = border_elements.horizontal_line;
388 let c = border_elements.column_separator;
389 let l = border_elements.left_corner;
390 let r = border_elements.right_corner;
391 let h8 = h.to_string().repeat(8);
392 let h_repeat = h.to_string().repeat(self.panel_sz());
393
394 if self.show_position_panel {
395 write!(self.writer, "{l}{h8}{c}")?;
396 } else {
397 write!(self.writer, "{l}")?;
398 }
399
400 for _ in 0..self.panels - 1 {
401 write!(self.writer, "{h_repeat}{c}")?;
402 }
403 if self.show_char_panel {
404 write!(self.writer, "{h_repeat}{c}")?;
405 } else {
406 write!(self.writer, "{h_repeat}")?;
407 }
408
409 if self.show_char_panel {
410 for _ in 0..self.panels - 1 {
411 write!(self.writer, "{h8}{c}")?;
412 }
413 writeln!(self.writer, "{h8}{r}")?;
414 } else {
415 writeln!(self.writer, "{r}")?;
416 }
417
418 Ok(())
419 }
420
421 pub fn print_header(&mut self) -> io::Result<()> {
422 if let Some(e) = self.border_style.header_elems() {
423 self.write_border(e)?
424 }
425 Ok(())
426 }
427
428 pub fn print_footer(&mut self) -> io::Result<()> {
429 if let Some(e) = self.border_style.footer_elems() {
430 self.write_border(e)?
431 }
432 Ok(())
433 }
434
435 fn print_position_panel(&mut self) -> io::Result<()> {
436 self.writer.write_all(
437 self.border_style
438 .outer_sep()
439 .encode_utf8(&mut [0; 4])
440 .as_bytes(),
441 )?;
442 if self.show_color {
443 self.writer.write_all(COLOR_OFFSET)?;
444 }
445 if self.show_position_panel {
446 match self.squeezer {
447 Squeezer::Print => {
448 self.writer.write_all(&[b'*'])?;
449 if self.show_color {
450 self.writer.write_all(COLOR_RESET)?;
451 }
452 self.writer.write_all(b" ")?;
453 }
454 Squeezer::Ignore | Squeezer::Disabled | Squeezer::Delete => {
455 let byte_index: [u8; 8] = (self.idx + self.display_offset).to_be_bytes();
456 let mut i = 0;
457 while byte_index[i] == 0x0 && i < 4 {
458 i += 1;
459 }
460 for &byte in byte_index.iter().skip(i) {
461 self.writer
462 .write_all(self.byte_hex_panel_g[byte as usize].as_bytes())?;
463 }
464 if self.show_color {
465 self.writer.write_all(COLOR_RESET)?;
466 }
467 }
468 }
469 self.writer.write_all(
470 self.border_style
471 .outer_sep()
472 .encode_utf8(&mut [0; 4])
473 .as_bytes(),
474 )?;
475 }
476 Ok(())
477 }
478
479 fn print_char(&mut self, i: u64) -> io::Result<()> {
480 match self.squeezer {
481 Squeezer::Print | Squeezer::Delete => self.writer.write_all(b" ")?,
482 Squeezer::Ignore | Squeezer::Disabled => {
483 if let Some(&b) = self.line_buf.get(i as usize) {
484 if self.show_color && self.curr_color != Some(Byte(b).color()) {
485 self.writer.write_all(Byte(b).color())?;
486 self.curr_color = Some(Byte(b).color());
487 }
488 self.writer
489 .write_all(self.byte_char_panel[b as usize].as_bytes())?;
490 } else {
491 self.squeezer = Squeezer::Print;
492 }
493 }
494 }
495 if i == 8 * self.panels - 1 {
496 if self.show_color {
497 self.writer.write_all(COLOR_RESET)?;
498 self.curr_color = None;
499 }
500 self.writer.write_all(
501 self.border_style
502 .outer_sep()
503 .encode_utf8(&mut [0; 4])
504 .as_bytes(),
505 )?;
506 } else if i % 8 == 7 {
507 if self.show_color {
508 self.writer.write_all(COLOR_RESET)?;
509 self.curr_color = None;
510 }
511 self.writer.write_all(
512 self.border_style
513 .inner_sep()
514 .encode_utf8(&mut [0; 4])
515 .as_bytes(),
516 )?;
517 }
518
519 Ok(())
520 }
521
522 pub fn print_char_panel(&mut self) -> io::Result<()> {
523 for i in 0..self.line_buf.len() {
524 self.print_char(i as u64)?;
525 }
526 Ok(())
527 }
528
529 fn print_byte(&mut self, i: usize, b: u8) -> io::Result<()> {
530 match self.squeezer {
531 Squeezer::Print => {
532 if !self.show_position_panel && i == 0 {
533 if self.show_color {
534 self.writer.write_all(COLOR_OFFSET)?;
535 }
536 self.writer
537 .write_all(self.byte_char_panel[b'*' as usize].as_bytes())?;
538 if self.show_color {
539 self.writer.write_all(COLOR_RESET)?;
540 }
541 } else if i % (self.group_size as usize) == 0 {
542 self.writer.write_all(b" ")?;
543 }
544 for _ in 0..self.base_digits {
545 self.writer.write_all(b" ")?;
546 }
547 }
548 Squeezer::Delete => self.writer.write_all(b" ")?,
549 Squeezer::Ignore | Squeezer::Disabled => {
550 if i % (self.group_size as usize) == 0 {
551 self.writer.write_all(b" ")?;
552 }
553 if self.show_color && self.curr_color != Some(Byte(b).color()) {
554 self.writer.write_all(Byte(b).color())?;
555 self.curr_color = Some(Byte(b).color());
556 }
557 self.writer
558 .write_all(self.byte_hex_panel[b as usize].as_bytes())?;
559 }
560 }
561 if i % 8 == 7 {
563 if self.show_color {
564 self.curr_color = None;
565 self.writer.write_all(COLOR_RESET)?;
566 }
567 self.writer.write_all(b" ")?;
568 if i as u64 % (8 * self.panels) == 8 * self.panels - 1 {
570 self.writer.write_all(
571 self.border_style
572 .outer_sep()
573 .encode_utf8(&mut [0; 4])
574 .as_bytes(),
575 )?;
576 } else {
577 self.writer.write_all(
578 self.border_style
579 .inner_sep()
580 .encode_utf8(&mut [0; 4])
581 .as_bytes(),
582 )?;
583 }
584 }
585 Ok(())
586 }
587
588 fn reorder_buffer_to_little_endian(&self, buf: &mut Vec<u8>) {
589 let n = buf.len();
590 let group_sz = self.group_size as usize;
591
592 for idx in (0..n).step_by(group_sz) {
593 let remaining = n - idx;
594 let total = remaining.min(group_sz);
595
596 buf[idx..idx + total].reverse();
597 }
598 }
599
600 pub fn print_bytes(&mut self) -> io::Result<()> {
601 let mut buf = self.line_buf.clone();
602
603 if matches!(self.endianness, Endianness::Little) {
604 self.reorder_buffer_to_little_endian(&mut buf);
605 };
606
607 for (i, &b) in buf.iter().enumerate() {
608 self.print_byte(i, b)?;
609 }
610 Ok(())
611 }
612
613 pub fn print_all<Reader: Read>(&mut self, reader: Reader) -> io::Result<()> {
616 let mut is_empty = true;
617
618 let mut buf = BufReader::new(reader);
619
620 let leftover = loop {
621 if let Ok(n) = buf.read(&mut self.line_buf) {
623 if n > 0 && n < 8 * self.panels as usize {
624 if is_empty {
626 self.print_header()?;
627 is_empty = false;
628 }
629 let mut leftover = n;
630 if let Some(s) = loop {
632 if let Ok(n) = buf.read(&mut self.line_buf[leftover..]) {
633 leftover += n;
634 if n == 0 {
636 self.line_buf.resize(leftover, 0);
637 break Some(leftover);
638 }
639 if leftover >= 8 * self.panels as usize {
641 break None;
642 }
643 }
644 } {
645 break Some(s);
646 };
647 } else if n == 0 {
648 if self.squeezer == Squeezer::Delete {
650 self.line_buf.clear();
652 break Some(0);
653 }
654 break None;
655 }
656 }
657 if is_empty {
658 self.print_header()?;
659 }
660
661 if matches!(self.squeezer, Squeezer::Print | Squeezer::Delete) {
664 if self
665 .line_buf
666 .chunks_exact(std::mem::size_of::<usize>())
667 .all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == self.squeeze_byte)
668 {
669 if self.squeezer == Squeezer::Delete {
670 self.idx += 8 * self.panels;
671 continue;
672 }
673 } else {
674 self.squeezer = Squeezer::Ignore;
675 }
676 }
677
678 self.print_position_panel()?;
680 self.print_bytes()?;
681 if self.show_char_panel {
682 self.print_char_panel()?;
683 }
684 self.writer.write_all(b"\n")?;
685
686 if is_empty {
687 self.writer.flush()?;
688 is_empty = false;
689 }
690
691 self.idx += 8 * self.panels;
693
694 if self.squeezer == Squeezer::Print {
696 self.squeezer = Squeezer::Delete;
697 }
698
699 let repeat_byte = (self.line_buf[0] as usize) * (usize::MAX / 255);
703 if !matches!(self.squeezer, Squeezer::Disabled | Squeezer::Delete)
704 && self
705 .line_buf
706 .chunks_exact(std::mem::size_of::<usize>())
707 .all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == repeat_byte)
708 {
709 self.squeezer = Squeezer::Print;
710 self.squeeze_byte = repeat_byte;
711 };
712 };
713
714 if is_empty {
717 self.base_digits = 2;
718 self.print_header()?;
719 if self.show_position_panel {
720 write!(self.writer, "{0:9}", "│")?;
721 }
722 write!(
723 self.writer,
724 "{0:2}{1:2$}{0}{0:>3$}",
725 "│",
726 "No content",
727 self.panel_sz() - 1,
728 self.panel_sz() + 1,
729 )?;
730 if self.show_char_panel {
731 write!(self.writer, "{0:>9}{0:>9}", "│")?;
732 }
733 writeln!(self.writer)?;
734 } else if let Some(n) = leftover {
735 self.print_position_panel()?;
737 self.squeezer = Squeezer::Ignore;
738 self.print_bytes()?;
739 self.squeezer = Squeezer::Print;
740 for i in n..8 * self.panels as usize {
741 self.print_byte(i, 0)?;
742 }
743 if self.show_char_panel {
744 self.squeezer = Squeezer::Ignore;
745 self.print_char_panel()?;
746 self.squeezer = Squeezer::Print;
747 for i in n..8 * self.panels as usize {
748 self.print_char(i as u64)?;
749 }
750 }
751 self.writer.write_all(b"\n")?;
752 }
753
754 self.print_footer()?;
755
756 self.writer.flush()?;
757
758 Ok(())
759 }
760}
761
762#[cfg(test)]
763mod tests {
764 use std::io;
765 use std::str;
766
767 use super::*;
768
769 fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) {
770 let mut output = vec![];
771 let mut printer = Printer::new(
772 &mut output,
773 false,
774 true,
775 true,
776 BorderStyle::Unicode,
777 true,
778 2,
779 1,
780 Base::Hexadecimal,
781 Endianness::Big,
782 CharacterTable::Default,
783 );
784
785 printer.print_all(input).unwrap();
786
787 let actual_string: &str = str::from_utf8(&output).unwrap();
788 assert_eq!(actual_string, expected_string,)
789 }
790
791 #[test]
792 fn empty_file_passes() {
793 let input = io::empty();
794 let expected_string = "\
795┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
796│ │ No content │ │ │ │
797└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
798"
799 .to_owned();
800 assert_print_all_output(input, expected_string);
801 }
802
803 #[test]
804 fn short_input_passes() {
805 let input = io::Cursor::new(b"spam");
806 let expected_string = "\
807┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
808│00000000│ 73 70 61 6d ┊ │spam ┊ │
809└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
810"
811 .to_owned();
812 assert_print_all_output(input, expected_string);
813 }
814
815 #[test]
816 fn display_offset() {
817 let input = io::Cursor::new(b"spamspamspamspamspam");
818 let expected_string = "\
819┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
820│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
821│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
822└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
823"
824 .to_owned();
825
826 let mut output = vec![];
827 let mut printer: Printer<Vec<u8>> = Printer::new(
828 &mut output,
829 false,
830 true,
831 true,
832 BorderStyle::Unicode,
833 true,
834 2,
835 1,
836 Base::Hexadecimal,
837 Endianness::Big,
838 CharacterTable::Default,
839 );
840 printer.display_offset(0xdeadbeef);
841
842 printer.print_all(input).unwrap();
843
844 let actual_string: &str = str::from_utf8(&output).unwrap();
845 assert_eq!(actual_string, expected_string)
846 }
847
848 #[test]
849 fn multiple_panels() {
850 let input = io::Cursor::new(b"supercalifragilisticexpialidocioussupercalifragilisticexpialidocioussupercalifragilisticexpialidocious");
851 let expected_string = "\
852┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┬────────┐
853│00000000│ 73 75 70 65 72 63 61 6c ┊ 69 66 72 61 67 69 6c 69 ┊ 73 74 69 63 65 78 70 69 ┊ 61 6c 69 64 6f 63 69 6f │supercal┊ifragili┊sticexpi┊alidocio│
854│00000020│ 75 73 73 75 70 65 72 63 ┊ 61 6c 69 66 72 61 67 69 ┊ 6c 69 73 74 69 63 65 78 ┊ 70 69 61 6c 69 64 6f 63 │ussuperc┊alifragi┊listicex┊pialidoc│
855│00000040│ 69 6f 75 73 73 75 70 65 ┊ 72 63 61 6c 69 66 72 61 ┊ 67 69 6c 69 73 74 69 63 ┊ 65 78 70 69 61 6c 69 64 │ioussupe┊rcalifra┊gilistic┊expialid│
856│00000060│ 6f 63 69 6f 75 73 ┊ ┊ ┊ │ocious ┊ ┊ ┊ │
857└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┴────────┘
858"
859 .to_owned();
860
861 let mut output = vec![];
862 let mut printer: Printer<Vec<u8>> = Printer::new(
863 &mut output,
864 false,
865 true,
866 true,
867 BorderStyle::Unicode,
868 true,
869 4,
870 1,
871 Base::Hexadecimal,
872 Endianness::Big,
873 CharacterTable::Default,
874 );
875
876 printer.print_all(input).unwrap();
877
878 let actual_string: &str = str::from_utf8(&output).unwrap();
879 assert_eq!(actual_string, expected_string)
880 }
881
882 #[test]
883 fn squeeze_works() {
884 let input = io::Cursor::new(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
885 let expected_string = "\
886┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
887│00000000│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
888│* │ ┊ │ ┊ │
889│00000020│ 00 ┊ │⋄ ┊ │
890└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
891"
892 .to_owned();
893 assert_print_all_output(input, expected_string);
894 }
895
896 #[test]
897 fn squeeze_nonzero() {
898 let input = io::Cursor::new(b"000000000000000000000000000000000");
899 let expected_string = "\
900┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
901│00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000│
902│* │ ┊ │ ┊ │
903│00000020│ 30 ┊ │0 ┊ │
904└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
905"
906 .to_owned();
907 assert_print_all_output(input, expected_string);
908 }
909
910 #[test]
911 fn squeeze_multiple_panels() {
912 let input = io::Cursor::new(b"0000000000000000000000000000000000000000000000000");
913 let expected_string = "\
914┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐
915│00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000┊00000000│
916│* │ ┊ ┊ │ ┊ ┊ │
917│00000030│ 30 ┊ ┊ │0 ┊ ┊ │
918└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘
919"
920 .to_owned();
921
922 let mut output = vec![];
923 let mut printer: Printer<Vec<u8>> = Printer::new(
924 &mut output,
925 false,
926 true,
927 true,
928 BorderStyle::Unicode,
929 true,
930 3,
931 1,
932 Base::Hexadecimal,
933 Endianness::Big,
934 CharacterTable::Default,
935 );
936
937 printer.print_all(input).unwrap();
938
939 let actual_string: &str = str::from_utf8(&output).unwrap();
940 assert_eq!(actual_string, expected_string)
941 }
942}