2020-04-29 08:22:03 +02:00
|
|
|
|
pub(crate) mod input;
|
2019-07-14 17:23:31 +01:00
|
|
|
|
pub mod squeezer;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
2020-04-29 08:22:03 +02:00
|
|
|
|
pub use input::*;
|
|
|
|
|
|
|
2019-07-14 21:31:51 +01:00
|
|
|
|
use std::io::{self, Read, Write};
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
|
|
|
|
|
use ansi_term::Color;
|
|
|
|
|
|
use ansi_term::Color::Fixed;
|
|
|
|
|
|
|
2019-07-17 15:13:20 +01:00
|
|
|
|
use crate::squeezer::{SqueezeAction, Squeezer};
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
2019-07-14 21:31:51 +01:00
|
|
|
|
const BUFFER_SIZE: usize = 256;
|
|
|
|
|
|
|
2019-07-14 17:38:30 +01:00
|
|
|
|
const COLOR_NULL: Color = Fixed(242); // grey
|
|
|
|
|
|
const COLOR_OFFSET: Color = Fixed(242); // grey
|
|
|
|
|
|
const COLOR_ASCII_PRINTABLE: Color = Color::Cyan;
|
|
|
|
|
|
const COLOR_ASCII_WHITESPACE: Color = Color::Green;
|
|
|
|
|
|
const COLOR_ASCII_OTHER: Color = Color::Purple;
|
|
|
|
|
|
const COLOR_NONASCII: Color = Color::Yellow;
|
|
|
|
|
|
|
|
|
|
|
|
pub enum ByteCategory {
|
|
|
|
|
|
Null,
|
|
|
|
|
|
AsciiPrintable,
|
|
|
|
|
|
AsciiWhitespace,
|
|
|
|
|
|
AsciiOther,
|
|
|
|
|
|
NonAscii,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
|
|
struct Byte(u8);
|
|
|
|
|
|
|
|
|
|
|
|
impl Byte {
|
|
|
|
|
|
fn category(self) -> ByteCategory {
|
|
|
|
|
|
if self.0 == 0x00 {
|
|
|
|
|
|
ByteCategory::Null
|
2020-06-07 15:26:07 +02:00
|
|
|
|
} else if self.0.is_ascii_graphic() {
|
2019-07-14 17:38:30 +01:00
|
|
|
|
ByteCategory::AsciiPrintable
|
|
|
|
|
|
} else if self.0.is_ascii_whitespace() {
|
|
|
|
|
|
ByteCategory::AsciiWhitespace
|
|
|
|
|
|
} else if self.0.is_ascii() {
|
|
|
|
|
|
ByteCategory::AsciiOther
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ByteCategory::NonAscii
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn color(self) -> &'static Color {
|
|
|
|
|
|
use crate::ByteCategory::*;
|
|
|
|
|
|
|
|
|
|
|
|
match self.category() {
|
|
|
|
|
|
Null => &COLOR_NULL,
|
|
|
|
|
|
AsciiPrintable => &COLOR_ASCII_PRINTABLE,
|
|
|
|
|
|
AsciiWhitespace => &COLOR_ASCII_WHITESPACE,
|
|
|
|
|
|
AsciiOther => &COLOR_ASCII_OTHER,
|
|
|
|
|
|
NonAscii => &COLOR_NONASCII,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn as_char(self) -> char {
|
|
|
|
|
|
use crate::ByteCategory::*;
|
|
|
|
|
|
|
|
|
|
|
|
match self.category() {
|
|
|
|
|
|
Null => '0',
|
|
|
|
|
|
AsciiPrintable => self.0 as char,
|
|
|
|
|
|
AsciiWhitespace if self.0 == 0x20 => ' ',
|
|
|
|
|
|
AsciiWhitespace => '_',
|
|
|
|
|
|
AsciiOther => '•',
|
|
|
|
|
|
NonAscii => '×',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct BorderElements {
|
|
|
|
|
|
left_corner: char,
|
|
|
|
|
|
horizontal_line: char,
|
|
|
|
|
|
column_separator: char,
|
|
|
|
|
|
right_corner: char,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-17 09:46:15 -04:00
|
|
|
|
#[derive(Clone, Copy)]
|
2019-07-14 17:38:30 +01:00
|
|
|
|
pub enum BorderStyle {
|
|
|
|
|
|
Unicode,
|
|
|
|
|
|
Ascii,
|
|
|
|
|
|
None,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl BorderStyle {
|
|
|
|
|
|
fn header_elems(&self) -> Option<BorderElements> {
|
|
|
|
|
|
match self {
|
|
|
|
|
|
BorderStyle::Unicode => Some(BorderElements {
|
|
|
|
|
|
left_corner: '┌',
|
|
|
|
|
|
horizontal_line: '─',
|
|
|
|
|
|
column_separator: '┬',
|
|
|
|
|
|
right_corner: '┐',
|
|
|
|
|
|
}),
|
|
|
|
|
|
BorderStyle::Ascii => Some(BorderElements {
|
|
|
|
|
|
left_corner: '+',
|
|
|
|
|
|
horizontal_line: '-',
|
|
|
|
|
|
column_separator: '+',
|
|
|
|
|
|
right_corner: '+',
|
|
|
|
|
|
}),
|
|
|
|
|
|
BorderStyle::None => None,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn footer_elems(&self) -> Option<BorderElements> {
|
|
|
|
|
|
match self {
|
|
|
|
|
|
BorderStyle::Unicode => Some(BorderElements {
|
|
|
|
|
|
left_corner: '└',
|
|
|
|
|
|
horizontal_line: '─',
|
|
|
|
|
|
column_separator: '┴',
|
|
|
|
|
|
right_corner: '┘',
|
|
|
|
|
|
}),
|
|
|
|
|
|
BorderStyle::Ascii => Some(BorderElements {
|
|
|
|
|
|
left_corner: '+',
|
|
|
|
|
|
horizontal_line: '-',
|
|
|
|
|
|
column_separator: '+',
|
|
|
|
|
|
right_corner: '+',
|
|
|
|
|
|
}),
|
|
|
|
|
|
BorderStyle::None => None,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn outer_sep(&self) -> char {
|
|
|
|
|
|
match self {
|
|
|
|
|
|
BorderStyle::Unicode => '│',
|
|
|
|
|
|
BorderStyle::Ascii => '|',
|
|
|
|
|
|
BorderStyle::None => ' ',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn inner_sep(&self) -> char {
|
|
|
|
|
|
match self {
|
|
|
|
|
|
BorderStyle::Unicode => '┊',
|
|
|
|
|
|
BorderStyle::Ascii => '|',
|
|
|
|
|
|
BorderStyle::None => ' ',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-17 09:46:15 -04:00
|
|
|
|
pub struct PrinterBuilder<'a, Writer: Write> {
|
|
|
|
|
|
writer: &'a mut Writer,
|
|
|
|
|
|
show_color: bool,
|
|
|
|
|
|
show_char_panel: bool,
|
|
|
|
|
|
show_position_panel: bool,
|
|
|
|
|
|
border_style: BorderStyle,
|
|
|
|
|
|
use_squeeze: bool,
|
|
|
|
|
|
panels: u16,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|
|
|
|
|
pub fn new(writer: &'a mut Writer) -> Self {
|
|
|
|
|
|
PrinterBuilder {
|
|
|
|
|
|
writer,
|
|
|
|
|
|
show_color: true,
|
|
|
|
|
|
show_char_panel: true,
|
|
|
|
|
|
show_position_panel: true,
|
|
|
|
|
|
border_style: BorderStyle::Unicode,
|
|
|
|
|
|
use_squeeze: true,
|
|
|
|
|
|
panels: 2,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn show_color(mut self, show_color: bool) -> Self {
|
|
|
|
|
|
self.show_color = show_color;
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn show_char_panel(mut self, show_char_panel: bool) -> Self {
|
|
|
|
|
|
self.show_char_panel = show_char_panel;
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn show_position_panel(mut self, show_position_panel: bool) -> Self {
|
|
|
|
|
|
self.show_position_panel = show_position_panel;
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn with_border_style(mut self, border_style: BorderStyle) -> Self {
|
|
|
|
|
|
self.border_style = border_style;
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-03 23:07:52 -04:00
|
|
|
|
pub fn enable_squeezing(mut self, enable: bool) -> Self {
|
|
|
|
|
|
self.use_squeeze = enable;
|
2022-09-17 09:46:15 -04:00
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-04 08:49:19 -04:00
|
|
|
|
pub fn num_panels(mut self, num: u16) -> Self {
|
|
|
|
|
|
self.panels = num;
|
2022-09-17 09:46:15 -04:00
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn build(self) -> Printer<'a, Writer> {
|
|
|
|
|
|
Printer::new(
|
|
|
|
|
|
self.writer,
|
|
|
|
|
|
self.show_color,
|
|
|
|
|
|
self.show_char_panel,
|
|
|
|
|
|
self.show_position_panel,
|
|
|
|
|
|
self.border_style,
|
|
|
|
|
|
self.use_squeeze,
|
|
|
|
|
|
self.panels,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 18:18:13 +01:00
|
|
|
|
pub struct Printer<'a, Writer: Write> {
|
2020-06-01 12:31:21 -06:00
|
|
|
|
idx: u64,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
/// The raw bytes used as input for the current line.
|
|
|
|
|
|
raw_line: Vec<u8>,
|
2019-07-14 18:18:13 +01:00
|
|
|
|
/// The buffered line built with each byte, ready to print to writer.
|
2019-07-14 17:38:30 +01:00
|
|
|
|
buffer_line: Vec<u8>,
|
2019-07-14 18:18:13 +01:00
|
|
|
|
writer: &'a mut Writer,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
show_color: bool,
|
2022-05-22 14:14:07 -05:00
|
|
|
|
show_char_panel: bool,
|
|
|
|
|
|
show_position_panel: bool,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
border_style: BorderStyle,
|
|
|
|
|
|
header_was_printed: bool,
|
2022-05-22 14:14:07 -05:00
|
|
|
|
byte_hex_panel: Vec<String>,
|
|
|
|
|
|
byte_char_panel: Vec<String>,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
squeezer: Squeezer,
|
2020-06-01 12:31:21 -06:00
|
|
|
|
display_offset: u64,
|
2022-07-12 11:39:24 -04:00
|
|
|
|
/// The number of panels to draw.
|
2022-09-05 09:51:34 -04:00
|
|
|
|
panels: u16,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 18:18:13 +01:00
|
|
|
|
impl<'a, Writer: Write> Printer<'a, Writer> {
|
2022-09-17 09:46:15 -04:00
|
|
|
|
fn new(
|
2019-07-14 18:18:13 +01:00
|
|
|
|
writer: &'a mut Writer,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
show_color: bool,
|
2022-05-22 14:14:07 -05:00
|
|
|
|
show_char_panel: bool,
|
|
|
|
|
|
show_position_panel: bool,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
border_style: BorderStyle,
|
|
|
|
|
|
use_squeeze: bool,
|
2022-09-05 09:51:34 -04:00
|
|
|
|
panels: u16,
|
2019-07-14 18:18:13 +01:00
|
|
|
|
) -> Printer<'a, Writer> {
|
2019-07-14 17:38:30 +01:00
|
|
|
|
Printer {
|
|
|
|
|
|
idx: 1,
|
|
|
|
|
|
raw_line: vec![],
|
|
|
|
|
|
buffer_line: vec![],
|
2019-07-14 18:18:13 +01:00
|
|
|
|
writer,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
show_color,
|
2022-05-22 14:14:07 -05:00
|
|
|
|
show_char_panel,
|
|
|
|
|
|
show_position_panel,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
border_style,
|
|
|
|
|
|
header_was_printed: false,
|
2022-07-12 11:39:24 -04:00
|
|
|
|
byte_hex_panel: (0u8..=u8::MAX)
|
2019-07-14 17:38:30 +01:00
|
|
|
|
.map(|i| {
|
|
|
|
|
|
let byte_hex = format!("{:02x} ", i);
|
|
|
|
|
|
if show_color {
|
|
|
|
|
|
Byte(i).color().paint(byte_hex).to_string()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
byte_hex
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect(),
|
2022-05-22 14:14:07 -05:00
|
|
|
|
byte_char_panel: show_char_panel
|
2022-04-22 15:33:26 -05:00
|
|
|
|
.then(|| {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
(0u8..=u8::MAX)
|
2022-04-22 15:33:26 -05:00
|
|
|
|
.map(|i| {
|
|
|
|
|
|
let byte_char = format!("{}", Byte(i).as_char());
|
|
|
|
|
|
if show_color {
|
|
|
|
|
|
Byte(i).color().paint(byte_char).to_string()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
byte_char
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect()
|
2019-07-14 17:38:30 +01:00
|
|
|
|
})
|
2022-04-22 15:33:26 -05:00
|
|
|
|
.unwrap_or_default(),
|
2022-09-05 09:51:34 -04:00
|
|
|
|
squeezer: Squeezer::new(use_squeeze, 8 * panels as u64),
|
2019-10-07 19:20:02 +01:00
|
|
|
|
display_offset: 0,
|
2022-09-05 09:51:34 -04:00
|
|
|
|
panels,
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-01 12:31:21 -06:00
|
|
|
|
pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
|
2019-10-07 19:20:02 +01:00
|
|
|
|
self.display_offset = display_offset;
|
|
|
|
|
|
self
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-22 15:33:26 -05:00
|
|
|
|
fn write_border(&mut self, border_elements: BorderElements) {
|
|
|
|
|
|
let h = border_elements.horizontal_line;
|
|
|
|
|
|
let c = border_elements.column_separator;
|
|
|
|
|
|
let l = border_elements.left_corner;
|
|
|
|
|
|
let r = border_elements.right_corner;
|
|
|
|
|
|
let h8 = h.to_string().repeat(8);
|
|
|
|
|
|
let h25 = h.to_string().repeat(25);
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
if self.show_position_panel {
|
2022-04-22 15:33:26 -05:00
|
|
|
|
write!(self.writer, "{l}{h8}{c}", l = l, c = c, h8 = h8).ok();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
write!(self.writer, "{}", l).ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..self.panels - 1 {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(self.writer, "{h25}{c}", h25 = h25, c = c).ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.show_char_panel {
|
|
|
|
|
|
write!(self.writer, "{h25}{c}", h25 = h25, c = c).ok();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
write!(self.writer, "{h25}", h25 = h25).ok();
|
|
|
|
|
|
}
|
2022-04-22 15:33:26 -05:00
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
if self.show_char_panel {
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..self.panels - 1 {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(self.writer, "{h8}{c}", h8 = h8, c = c).ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
writeln!(self.writer, "{h8}{r}", h8 = h8, r = r).ok();
|
2022-04-22 15:33:26 -05:00
|
|
|
|
} else {
|
|
|
|
|
|
writeln!(self.writer, "{r}", r = r).ok();
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
pub fn print_header(&mut self) {
|
|
|
|
|
|
if self.header_was_printed {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-04-22 15:48:54 -05:00
|
|
|
|
if let Some(e) = self.border_style.header_elems() {
|
|
|
|
|
|
self.write_border(e)
|
|
|
|
|
|
}
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.header_was_printed = true;
|
2022-04-22 15:33:26 -05:00
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
pub fn print_footer(&mut self) {
|
2022-04-22 15:48:54 -05:00
|
|
|
|
if let Some(e) = self.border_style.footer_elems() {
|
|
|
|
|
|
self.write_border(e)
|
|
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
fn print_position_panel(&mut self) {
|
|
|
|
|
|
if !self.show_position_panel {
|
2022-04-22 15:33:26 -05:00
|
|
|
|
write!(&mut self.buffer_line, "{} ", self.border_style.outer_sep()).ok();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
|
|
|
|
|
let style = COLOR_OFFSET.normal();
|
2020-06-01 12:31:21 -06:00
|
|
|
|
let byte_index = format!("{:08x}", self.idx - 1 + self.display_offset);
|
2019-07-14 17:38:30 +01:00
|
|
|
|
let formatted_string = if self.show_color {
|
|
|
|
|
|
format!("{}", style.paint(byte_index))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
byte_index
|
|
|
|
|
|
};
|
|
|
|
|
|
let _ = write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{}{}{} ",
|
|
|
|
|
|
self.border_style.outer_sep(),
|
|
|
|
|
|
formatted_string,
|
|
|
|
|
|
self.border_style.outer_sep()
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
pub fn print_char_panel(&mut self) {
|
|
|
|
|
|
if !self.show_char_panel {
|
|
|
|
|
|
// just write newline if character panel is hidden
|
2022-04-22 15:48:54 -05:00
|
|
|
|
writeln!(&mut self.buffer_line).ok();
|
2022-04-22 15:33:26 -05:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
let len = self.raw_line.len();
|
|
|
|
|
|
|
2022-04-22 15:33:26 -05:00
|
|
|
|
let mut idx = 1;
|
|
|
|
|
|
for &b in self.raw_line.iter() {
|
|
|
|
|
|
let _ = write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{}",
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.byte_char_panel[b as usize]
|
2022-04-22 15:33:26 -05:00
|
|
|
|
);
|
|
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if idx % 8 == 0 && idx % (u64::from(self.panels) * 8) != 0 {
|
2022-04-22 15:33:26 -05:00
|
|
|
|
let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
idx += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if len < usize::from(8 * self.panels) {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
let _ = write!(&mut self.buffer_line, "{0:1$}", "", 8 - len % 8);
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..(usize::from(8 * self.panels) - (len + (8 - len % 8))) / 8 {
|
2022-07-12 11:49:50 -04:00
|
|
|
|
let _ = write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{2}{0:1$}",
|
|
|
|
|
|
"",
|
|
|
|
|
|
8,
|
|
|
|
|
|
self.border_style.inner_sep()
|
|
|
|
|
|
);
|
2022-07-12 11:39:24 -04:00
|
|
|
|
}
|
2022-04-22 15:33:26 -05:00
|
|
|
|
}
|
2022-07-12 11:39:24 -04:00
|
|
|
|
let _ = writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep());
|
2022-04-22 15:33:26 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 17:38:30 +01:00
|
|
|
|
pub fn print_byte(&mut self, b: u8) -> io::Result<()> {
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if self.idx % (u64::from(self.panels) * 8) == 1 {
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.print_header();
|
|
|
|
|
|
self.print_position_panel();
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-22 14:14:07 -05:00
|
|
|
|
write!(&mut self.buffer_line, "{}", self.byte_hex_panel[b as usize])?;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
self.raw_line.push(b);
|
|
|
|
|
|
|
|
|
|
|
|
self.squeezer.process(b, self.idx);
|
|
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if self.idx % (u64::from(self.panels) * 8) == 0 {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
self.print_textline()?;
|
|
|
|
|
|
} else if self.idx % 8 == 0 {
|
|
|
|
|
|
let _ = write!(&mut self.buffer_line, "{} ", self.border_style.inner_sep());
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.idx += 1;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn print_textline(&mut self) -> io::Result<()> {
|
|
|
|
|
|
let len = self.raw_line.len();
|
|
|
|
|
|
|
|
|
|
|
|
if len == 0 {
|
|
|
|
|
|
if self.squeezer.active() {
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.print_position_panel();
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{0:1$}", "", 24)?;
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..self.panels - 1 {
|
2022-07-12 11:49:50 -04:00
|
|
|
|
write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{2}{0:1$}",
|
|
|
|
|
|
"",
|
|
|
|
|
|
25,
|
|
|
|
|
|
self.border_style.inner_sep()
|
|
|
|
|
|
)?;
|
2022-07-12 11:39:24 -04:00
|
|
|
|
}
|
2022-07-12 11:49:50 -04:00
|
|
|
|
write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{2}{0:1$}",
|
|
|
|
|
|
"",
|
|
|
|
|
|
8,
|
|
|
|
|
|
self.border_style.outer_sep()
|
|
|
|
|
|
)?;
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..self.panels - 1 {
|
2022-07-12 11:49:50 -04:00
|
|
|
|
write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{2}{0:1$}",
|
|
|
|
|
|
"",
|
|
|
|
|
|
8,
|
|
|
|
|
|
self.border_style.inner_sep()
|
|
|
|
|
|
)?;
|
2022-07-12 11:39:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
|
2019-07-14 18:18:13 +01:00
|
|
|
|
self.writer.write_all(&self.buffer_line)?;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-08 19:53:26 +02:00
|
|
|
|
let squeeze_action = self.squeezer.action();
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
2022-07-12 11:39:24 -04:00
|
|
|
|
// print empty space on last line
|
2019-10-08 19:53:26 +02:00
|
|
|
|
if squeeze_action != SqueezeAction::Delete {
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if len < usize::from(8 * self.panels) {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{0:1$}", "", 3 * (8 - len % 8))?;
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for _ in 0..(usize::from(8 * self.panels) - (len + (8 - len % 8))) / 8 {
|
2022-07-12 11:49:50 -04:00
|
|
|
|
write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{2}{0:1$}",
|
|
|
|
|
|
"",
|
|
|
|
|
|
1 + 3 * 8,
|
|
|
|
|
|
self.border_style.inner_sep()
|
|
|
|
|
|
)?;
|
2022-07-12 11:39:24 -04:00
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.print_char_panel();
|
2022-04-22 15:33:26 -05:00
|
|
|
|
|
2019-10-08 19:53:26 +02:00
|
|
|
|
match squeeze_action {
|
2019-07-14 17:38:30 +01:00
|
|
|
|
SqueezeAction::Print => {
|
|
|
|
|
|
self.buffer_line.clear();
|
|
|
|
|
|
let style = COLOR_OFFSET.normal();
|
|
|
|
|
|
let asterisk = if self.show_color {
|
|
|
|
|
|
format!("{}", style.paint("*"))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
String::from("*")
|
|
|
|
|
|
};
|
2022-07-12 11:39:24 -04:00
|
|
|
|
|
2022-07-12 11:49:50 -04:00
|
|
|
|
write!(
|
|
|
|
|
|
&mut self.buffer_line,
|
|
|
|
|
|
"{3}{0}{1:2$}{3}",
|
|
|
|
|
|
asterisk,
|
|
|
|
|
|
"",
|
|
|
|
|
|
7,
|
|
|
|
|
|
self.border_style.outer_sep()
|
|
|
|
|
|
)?;
|
2022-07-12 11:39:24 -04:00
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for i in 0..self.panels {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{0:1$}", "", 25)?;
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if i != self.panels - 1 {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{}", self.border_style.inner_sep())?;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
write!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-05 09:51:34 -04:00
|
|
|
|
for i in 0..self.panels {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{0:1$}", "", 8)?;
|
2022-09-05 09:51:34 -04:00
|
|
|
|
if i != self.panels - 1 {
|
2022-07-12 11:39:24 -04:00
|
|
|
|
write!(&mut self.buffer_line, "{}", self.border_style.inner_sep())?;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
SqueezeAction::Delete => self.buffer_line.clear(),
|
|
|
|
|
|
SqueezeAction::Ignore => (),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-14 18:18:13 +01:00
|
|
|
|
self.writer.write_all(&self.buffer_line)?;
|
2019-07-14 17:38:30 +01:00
|
|
|
|
|
|
|
|
|
|
self.raw_line.clear();
|
|
|
|
|
|
self.buffer_line.clear();
|
|
|
|
|
|
|
2019-10-08 19:53:26 +02:00
|
|
|
|
self.squeezer.advance();
|
|
|
|
|
|
|
2019-07-14 17:38:30 +01:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn header_was_printed(&self) -> bool {
|
|
|
|
|
|
self.header_was_printed
|
|
|
|
|
|
}
|
2019-07-14 21:31:51 +01:00
|
|
|
|
|
|
|
|
|
|
/// Loop through the given `Reader`, printing until the `Reader` buffer
|
2020-03-09 20:20:12 +01:00
|
|
|
|
/// is exhausted.
|
2019-07-14 21:31:51 +01:00
|
|
|
|
pub fn print_all<Reader: Read>(
|
|
|
|
|
|
&mut self,
|
2020-04-29 08:22:03 +02:00
|
|
|
|
mut reader: Reader,
|
2020-06-10 13:00:10 -06:00
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
2019-07-14 21:31:51 +01:00
|
|
|
|
let mut buffer = [0; BUFFER_SIZE];
|
|
|
|
|
|
'mainloop: loop {
|
|
|
|
|
|
let size = reader.read(&mut buffer)?;
|
|
|
|
|
|
if size == 0 {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for b in &buffer[..size] {
|
|
|
|
|
|
let res = self.print_byte(*b);
|
|
|
|
|
|
|
|
|
|
|
|
if res.is_err() {
|
|
|
|
|
|
// Broken pipe
|
|
|
|
|
|
break 'mainloop;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Finish last line
|
|
|
|
|
|
self.print_textline().ok();
|
2020-10-30 19:32:45 +01:00
|
|
|
|
|
2019-07-14 21:31:51 +01:00
|
|
|
|
if !self.header_was_printed() {
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.print_header();
|
|
|
|
|
|
if self.show_position_panel {
|
2022-04-22 15:33:26 -05:00
|
|
|
|
write!(self.writer, "{0:9}", "│").ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
write!(
|
2020-11-01 21:04:39 +01:00
|
|
|
|
self.writer,
|
2022-04-22 15:33:26 -05:00
|
|
|
|
"{0:2}{1:24}{0}{0:>26}",
|
|
|
|
|
|
"│", "No content to print"
|
2020-11-01 21:04:39 +01:00
|
|
|
|
)
|
|
|
|
|
|
.ok();
|
2022-05-22 14:14:07 -05:00
|
|
|
|
if self.show_char_panel {
|
2022-04-22 15:33:26 -05:00
|
|
|
|
write!(self.writer, "{0:>9}{0:>9}", "│").ok();
|
|
|
|
|
|
}
|
2022-04-22 15:48:54 -05:00
|
|
|
|
writeln!(self.writer).ok();
|
2019-07-14 21:31:51 +01:00
|
|
|
|
}
|
2022-05-22 14:14:07 -05:00
|
|
|
|
self.print_footer();
|
2019-07-14 21:31:51 +01:00
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use std::io;
|
|
|
|
|
|
use std::str;
|
|
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
2022-04-22 15:59:10 -05:00
|
|
|
|
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) {
|
2019-07-14 21:31:51 +01:00
|
|
|
|
let mut output = vec![];
|
2022-07-12 11:49:50 -04:00
|
|
|
|
let mut printer = Printer::new(
|
|
|
|
|
|
&mut output,
|
|
|
|
|
|
false,
|
|
|
|
|
|
true,
|
|
|
|
|
|
true,
|
|
|
|
|
|
BorderStyle::Unicode,
|
|
|
|
|
|
true,
|
|
|
|
|
|
2,
|
|
|
|
|
|
);
|
2019-07-14 21:31:51 +01:00
|
|
|
|
|
2020-03-09 20:23:46 +01:00
|
|
|
|
printer.print_all(input).unwrap();
|
2019-07-14 21:31:51 +01:00
|
|
|
|
|
|
|
|
|
|
let actual_string: &str = str::from_utf8(&output).unwrap();
|
2022-04-22 15:33:26 -05:00
|
|
|
|
assert_eq!(actual_string, expected_string,)
|
2019-07-14 21:31:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn empty_file_passes() {
|
|
|
|
|
|
let input = io::empty();
|
2020-05-28 10:21:04 -06:00
|
|
|
|
let expected_string = "\
|
|
|
|
|
|
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
2020-10-30 19:32:45 +01:00
|
|
|
|
│ │ No content to print │ │ │ │
|
2019-07-14 21:31:51 +01:00
|
|
|
|
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
2019-10-07 19:20:02 +01:00
|
|
|
|
"
|
2020-05-28 10:21:04 -06:00
|
|
|
|
.to_owned();
|
2019-07-14 21:31:51 +01:00
|
|
|
|
assert_print_all_output(input, expected_string);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn short_input_passes() {
|
|
|
|
|
|
let input = io::Cursor::new(b"spam");
|
2020-05-28 10:21:04 -06:00
|
|
|
|
let expected_string = "\
|
|
|
|
|
|
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
2019-10-01 13:00:09 -04:00
|
|
|
|
│00000000│ 73 70 61 6d ┊ │spam ┊ │
|
2019-07-14 21:31:51 +01:00
|
|
|
|
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
2019-10-07 19:20:02 +01:00
|
|
|
|
"
|
2020-05-28 10:21:04 -06:00
|
|
|
|
.to_owned();
|
2019-07-14 21:31:51 +01:00
|
|
|
|
assert_print_all_output(input, expected_string);
|
|
|
|
|
|
}
|
2019-10-07 19:20:02 +01:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn display_offset() {
|
|
|
|
|
|
let input = io::Cursor::new(b"spamspamspamspamspam");
|
2020-05-28 10:21:04 -06:00
|
|
|
|
let expected_string = "\
|
|
|
|
|
|
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
2019-10-07 19:20:02 +01:00
|
|
|
|
│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
|
|
|
|
|
|
│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
|
|
|
|
|
|
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
|
|
|
|
|
"
|
2020-05-28 10:21:04 -06:00
|
|
|
|
.to_owned();
|
2019-10-07 19:20:02 +01:00
|
|
|
|
|
|
|
|
|
|
let mut output = vec![];
|
2022-07-12 11:49:50 -04:00
|
|
|
|
let mut printer: Printer<Vec<u8>> = Printer::new(
|
|
|
|
|
|
&mut output,
|
|
|
|
|
|
false,
|
|
|
|
|
|
true,
|
|
|
|
|
|
true,
|
|
|
|
|
|
BorderStyle::Unicode,
|
|
|
|
|
|
true,
|
|
|
|
|
|
2,
|
|
|
|
|
|
);
|
2019-10-07 19:20:02 +01:00
|
|
|
|
printer.display_offset(0xdeadbeef);
|
|
|
|
|
|
|
2020-03-09 20:23:46 +01:00
|
|
|
|
printer.print_all(input).unwrap();
|
2019-10-07 19:20:02 +01:00
|
|
|
|
|
|
|
|
|
|
let actual_string: &str = str::from_utf8(&output).unwrap();
|
|
|
|
|
|
assert_eq!(actual_string, expected_string)
|
|
|
|
|
|
}
|
2022-07-17 13:37:25 -04:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
2022-09-05 09:51:34 -04:00
|
|
|
|
fn multiple_panels() {
|
2022-07-17 13:37:25 -04:00
|
|
|
|
let input = io::Cursor::new(b"supercalifragilisticexpialidocioussupercalifragilisticexpialidocioussupercalifragilisticexpialidocious");
|
|
|
|
|
|
let expected_string = "\
|
|
|
|
|
|
┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┬────────┐
|
|
|
|
|
|
│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│
|
|
|
|
|
|
│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│
|
|
|
|
|
|
│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│
|
|
|
|
|
|
│00000060│ 6f 63 69 6f 75 73 ┊ ┊ ┊ │ocious ┊ ┊ ┊ │
|
|
|
|
|
|
└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┴────────┘
|
|
|
|
|
|
"
|
|
|
|
|
|
.to_owned();
|
|
|
|
|
|
|
|
|
|
|
|
let mut output = vec![];
|
|
|
|
|
|
let mut printer: Printer<Vec<u8>> = Printer::new(
|
|
|
|
|
|
&mut output,
|
|
|
|
|
|
false,
|
|
|
|
|
|
true,
|
|
|
|
|
|
true,
|
|
|
|
|
|
BorderStyle::Unicode,
|
|
|
|
|
|
true,
|
|
|
|
|
|
4,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
printer.print_all(input).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let actual_string: &str = str::from_utf8(&output).unwrap();
|
|
|
|
|
|
assert_eq!(actual_string, expected_string)
|
|
|
|
|
|
}
|
2019-07-14 17:38:30 +01:00
|
|
|
|
}
|