mirror of
https://github.com/sharkdp/hexyl.git
synced 2026-10-30 09:05:54 +00:00
Merge pull request #178 from sharifhsn/base
[feature] Implement alternate bases
This commit is contained in:
3 files changed
+150
-35
No files matched your search
+81
-28
@@ -19,7 +19,7 @@ use thiserror::Error as ThisError;
|
|||||||
|
|
||||||
use terminal_size::terminal_size;
|
use terminal_size::terminal_size;
|
||||||
|
|
||||||
use hexyl::{BorderStyle, Input, PrinterBuilder};
|
use hexyl::{Base, BorderStyle, Input, PrinterBuilder};
|
||||||
|
|
||||||
const DEFAULT_BLOCK_SIZE: i64 = 512;
|
const DEFAULT_BLOCK_SIZE: i64 = 512;
|
||||||
|
|
||||||
@@ -177,6 +177,18 @@ fn run() -> Result<(), AnyhowError> {
|
|||||||
The possible options are 1, 2, 4, 8. The default is 1",
|
The possible options are 1, 2, 4, 8. The default is 1",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("base")
|
||||||
|
.short('b')
|
||||||
|
.long("base")
|
||||||
|
.num_args(1)
|
||||||
|
.value_name("B")
|
||||||
|
.help(
|
||||||
|
"Sets the base used for the bytes. The possible options are \
|
||||||
|
binary, octal, decimal, and hexadecimal. The default base \
|
||||||
|
is hexadecimal."
|
||||||
|
)
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("terminal_width")
|
Arg::new("terminal_width")
|
||||||
.long("terminal-width")
|
.long("terminal-width")
|
||||||
@@ -310,9 +322,13 @@ fn run() -> Result<(), AnyhowError> {
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let max_panels_fn = |terminal_width: u64| {
|
let max_panels_fn = |terminal_width: u64, base_digits: u64, group_bytes: u64| {
|
||||||
let offset = if show_position_panel { 10 } else { 1 };
|
let offset = if show_position_panel { 10 } else { 1 };
|
||||||
let col_width = if show_char_panel { 35 } else { 26 };
|
let col_width = if show_char_panel {
|
||||||
|
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2 + 8
|
||||||
|
} else {
|
||||||
|
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2
|
||||||
|
};
|
||||||
if (terminal_width - offset) / col_width < 1 {
|
if (terminal_width - offset) / col_width < 1 {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
@@ -320,32 +336,36 @@ fn run() -> Result<(), AnyhowError> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let panels = if matches.get_one::<String>("panels").map(String::as_ref) == Some("auto") {
|
let base = if let Some(base) = matches.get_one::<String>("base")
|
||||||
max_panels_fn(terminal_size().ok_or_else(|| anyhow!("not a TTY"))?.0 .0 as u64)
|
.map(|s| {
|
||||||
} else if let Some(panels) = matches
|
if let Ok(base_num) = s.parse::<u8>() {
|
||||||
.get_one::<String>("panels")
|
match base_num {
|
||||||
.map(|s| {
|
2 => Ok(Base::Binary),
|
||||||
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
8 => Ok(Base::Octal),
|
||||||
"failed to parse `--panels` arg {:?} as unsigned nonzero integer",
|
10 => Ok(Base::Decimal),
|
||||||
s
|
16 => Ok(Base::Hexadecimal),
|
||||||
))
|
_ => Err(anyhow!("The number provided is not a valid base. Valid bases are 2, 8, 10, and 16.")),
|
||||||
})
|
}
|
||||||
.transpose()?
|
} else {
|
||||||
{
|
match s.as_str() {
|
||||||
panels
|
"b" | "bin" | "binary" => Ok(Base::Binary),
|
||||||
} else if let Some(terminal_width) = matches
|
"o" | "oct" | "octal" => Ok(Base::Octal),
|
||||||
.get_one::<String>("terminal_width")
|
"d" | "dec" | "decimal" => Ok(Base::Decimal),
|
||||||
.map(|s| {
|
"x" | "hex" | "hexadecimal" => Ok(Base::Hexadecimal),
|
||||||
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
_ => Err(anyhow!("The base provided is not valid. Valid bases are \"b\", \"o\", \"d\", and \"x\"."))
|
||||||
"failed to parse `--terminal-width` arg {:?} as unsigned nonzero integer",
|
}
|
||||||
s
|
}
|
||||||
))
|
}).transpose()? {
|
||||||
})
|
base
|
||||||
.transpose()?
|
|
||||||
{
|
|
||||||
max_panels_fn(terminal_width)
|
|
||||||
} else {
|
} else {
|
||||||
2
|
Base::Hexadecimal
|
||||||
|
};
|
||||||
|
|
||||||
|
let base_digits = match base {
|
||||||
|
Base::Binary => 8,
|
||||||
|
Base::Octal => 3,
|
||||||
|
Base::Decimal => 3,
|
||||||
|
Base::Hexadecimal => 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let group_bytes = if let Some(group_bytes) = matches
|
let group_bytes = if let Some(group_bytes) = matches
|
||||||
@@ -369,6 +389,38 @@ fn run() -> Result<(), AnyhowError> {
|
|||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let panels = if matches.get_one::<String>("panels").map(String::as_ref) == Some("auto") {
|
||||||
|
max_panels_fn(
|
||||||
|
terminal_size().ok_or_else(|| anyhow!("not a TTY"))?.0 .0 as u64,
|
||||||
|
base_digits,
|
||||||
|
group_bytes.into(),
|
||||||
|
)
|
||||||
|
} else if let Some(panels) = matches
|
||||||
|
.get_one::<String>("panels")
|
||||||
|
.map(|s| {
|
||||||
|
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
||||||
|
"failed to parse `--panels` arg {:?} as unsigned nonzero integer",
|
||||||
|
s
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
{
|
||||||
|
panels
|
||||||
|
} else if let Some(terminal_width) = matches
|
||||||
|
.get_one::<String>("terminal_width")
|
||||||
|
.map(|s| {
|
||||||
|
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
||||||
|
"failed to parse `--terminal-width` arg {:?} as unsigned nonzero integer",
|
||||||
|
s
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
{
|
||||||
|
max_panels_fn(terminal_width, base_digits, group_bytes.into())
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
let mut stdout_lock = BufWriter::new(stdout.lock());
|
let mut stdout_lock = BufWriter::new(stdout.lock());
|
||||||
|
|
||||||
@@ -380,6 +432,7 @@ fn run() -> Result<(), AnyhowError> {
|
|||||||
.enable_squeezing(squeeze)
|
.enable_squeezing(squeeze)
|
||||||
.num_panels(panels)
|
.num_panels(panels)
|
||||||
.num_group_bytes(group_bytes)
|
.num_group_bytes(group_bytes)
|
||||||
|
.with_base(base)
|
||||||
.build();
|
.build();
|
||||||
printer.display_offset(skip_offset + display_offset);
|
printer.display_offset(skip_offset + display_offset);
|
||||||
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
|
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
|
||||||
|
|||||||
+51
-7
@@ -6,6 +6,13 @@ use std::io::{self, BufReader, Read, Write};
|
|||||||
|
|
||||||
use anstyle::{AnsiColor, Reset};
|
use anstyle::{AnsiColor, Reset};
|
||||||
|
|
||||||
|
pub enum Base {
|
||||||
|
Binary,
|
||||||
|
Octal,
|
||||||
|
Decimal,
|
||||||
|
Hexadecimal,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
enum ByteColor {
|
enum ByteColor {
|
||||||
@@ -162,6 +169,7 @@ pub struct PrinterBuilder<'a, Writer: Write> {
|
|||||||
use_squeeze: bool,
|
use_squeeze: bool,
|
||||||
panels: u64,
|
panels: u64,
|
||||||
group_bytes: u8,
|
group_bytes: u8,
|
||||||
|
base: Base,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
||||||
@@ -175,6 +183,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
use_squeeze: true,
|
use_squeeze: true,
|
||||||
panels: 2,
|
panels: 2,
|
||||||
group_bytes: 1,
|
group_bytes: 1,
|
||||||
|
base: Base::Hexadecimal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +222,11 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_base(mut self, base: Base) -> Self {
|
||||||
|
self.base = base;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Printer<'a, Writer> {
|
pub fn build(self) -> Printer<'a, Writer> {
|
||||||
Printer::new(
|
Printer::new(
|
||||||
self.writer,
|
self.writer,
|
||||||
@@ -223,6 +237,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
self.use_squeeze,
|
self.use_squeeze,
|
||||||
self.panels,
|
self.panels,
|
||||||
self.group_bytes,
|
self.group_bytes,
|
||||||
|
self.base,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,6 +265,8 @@ pub struct Printer<'a, Writer: Write> {
|
|||||||
squeeze_byte: usize,
|
squeeze_byte: usize,
|
||||||
/// The number of octets per group.
|
/// The number of octets per group.
|
||||||
group_bytes: u8,
|
group_bytes: u8,
|
||||||
|
/// The number of digits used to write the base.
|
||||||
|
base_digits: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Writer: Write> Printer<'a, Writer> {
|
impl<'a, Writer: Write> Printer<'a, Writer> {
|
||||||
@@ -262,6 +279,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
use_squeeze: bool,
|
use_squeeze: bool,
|
||||||
panels: u64,
|
panels: u64,
|
||||||
group_bytes: u8,
|
group_bytes: u8,
|
||||||
|
base: Base,
|
||||||
) -> Printer<'a, Writer> {
|
) -> Printer<'a, Writer> {
|
||||||
Printer {
|
Printer {
|
||||||
idx: 0,
|
idx: 0,
|
||||||
@@ -281,11 +299,18 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
Reset.render().to_string(),
|
Reset.render().to_string(),
|
||||||
],
|
],
|
||||||
border_style,
|
border_style,
|
||||||
byte_hex_panel: (0u8..=u8::MAX).map(|i| format!("{:02x}", i)).collect(),
|
byte_hex_panel: (0u8..=u8::MAX)
|
||||||
|
.map(|i| match base {
|
||||||
|
Base::Binary => format!("{i:08b}"),
|
||||||
|
Base::Octal => format!("{i:03o}"),
|
||||||
|
Base::Decimal => format!("{i:03}"),
|
||||||
|
Base::Hexadecimal => format!("{i:02x}"),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
byte_char_panel: (0u8..=u8::MAX)
|
byte_char_panel: (0u8..=u8::MAX)
|
||||||
.map(|i| format!("{}", Byte(i).as_char()))
|
.map(|i| format!("{}", Byte(i).as_char()))
|
||||||
.collect(),
|
.collect(),
|
||||||
byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{:02x}", i)).collect(),
|
byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(),
|
||||||
byte_char_panel_g: (0u8..=u8::MAX)
|
byte_char_panel_g: (0u8..=u8::MAX)
|
||||||
.map(|i| format!("{}", Byte(i).as_char()))
|
.map(|i| format!("{}", Byte(i).as_char()))
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -298,6 +323,12 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
panels,
|
panels,
|
||||||
squeeze_byte: 0x00,
|
squeeze_byte: 0x00,
|
||||||
group_bytes,
|
group_bytes,
|
||||||
|
base_digits: match base {
|
||||||
|
Base::Binary => 8,
|
||||||
|
Base::Octal => 3,
|
||||||
|
Base::Decimal => 3,
|
||||||
|
Base::Hexadecimal => 2,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +339,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
|
|
||||||
fn panel_sz(&self) -> usize {
|
fn panel_sz(&self) -> usize {
|
||||||
// add one to include the trailing space of a group
|
// add one to include the trailing space of a group
|
||||||
let group_sz = 2 * self.group_bytes as usize + 1;
|
let group_sz = self.base_digits as usize * self.group_bytes as usize + 1;
|
||||||
let group_per_panel = 8 / self.group_bytes as usize;
|
let group_per_panel = 8 / self.group_bytes as usize;
|
||||||
// add one to include the leading space
|
// add one to include the leading space
|
||||||
1 + group_sz * group_per_panel
|
1 + group_sz * group_per_panel
|
||||||
@@ -483,7 +514,9 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
self.writer.write_all(b" ")?;
|
self.writer.write_all(b" ")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.writer.write_all(b" ")?;
|
for _ in 0..self.base_digits {
|
||||||
|
self.writer.write_all(b" ")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Squeezer::Delete => self.writer.write_all(b" ")?,
|
Squeezer::Delete => self.writer.write_all(b" ")?,
|
||||||
Squeezer::Ignore | Squeezer::Disabled => {
|
Squeezer::Ignore | Squeezer::Disabled => {
|
||||||
@@ -541,13 +574,15 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
|
|
||||||
let mut buf = BufReader::new(reader);
|
let mut buf = BufReader::new(reader);
|
||||||
|
|
||||||
self.print_header()?;
|
|
||||||
let leftover = loop {
|
let leftover = loop {
|
||||||
// read a maximum of 8 * self.panels bytes from the reader
|
// read a maximum of 8 * self.panels bytes from the reader
|
||||||
if let Ok(n) = buf.read(&mut self.line_buf) {
|
if let Ok(n) = buf.read(&mut self.line_buf) {
|
||||||
if n > 0 && n < 8 * self.panels as usize {
|
if n > 0 && n < 8 * self.panels as usize {
|
||||||
// if less are read, that indicates end of file after
|
// if less are read, that indicates end of file after
|
||||||
is_empty = false;
|
if is_empty {
|
||||||
|
self.print_header()?;
|
||||||
|
is_empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
// perform second check on read
|
// perform second check on read
|
||||||
if let Ok(0) = buf.read(&mut self.line_buf[n..]) {
|
if let Ok(0) = buf.read(&mut self.line_buf[n..]) {
|
||||||
@@ -564,7 +599,10 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
break None;
|
break None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_empty = false;
|
if is_empty {
|
||||||
|
self.print_header()?;
|
||||||
|
is_empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
// squeeze is active, check if the line is the same
|
// squeeze is active, check if the line is the same
|
||||||
// skip print if still squeezed, otherwise print and deactivate squeeze
|
// skip print if still squeezed, otherwise print and deactivate squeeze
|
||||||
@@ -617,6 +655,8 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
// special ending
|
// special ending
|
||||||
|
|
||||||
if is_empty {
|
if is_empty {
|
||||||
|
self.base_digits = 2;
|
||||||
|
self.print_header()?;
|
||||||
if self.show_position_panel {
|
if self.show_position_panel {
|
||||||
write!(self.writer, "{0:9}", "│")?;
|
write!(self.writer, "{0:9}", "│")?;
|
||||||
}
|
}
|
||||||
@@ -678,6 +718,7 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
|
Base::Hexadecimal,
|
||||||
);
|
);
|
||||||
|
|
||||||
printer.print_all(input).unwrap();
|
printer.print_all(input).unwrap();
|
||||||
@@ -731,6 +772,7 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
|
Base::Hexadecimal,
|
||||||
);
|
);
|
||||||
printer.display_offset(0xdeadbeef);
|
printer.display_offset(0xdeadbeef);
|
||||||
|
|
||||||
@@ -763,6 +805,7 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
4,
|
4,
|
||||||
1,
|
1,
|
||||||
|
Base::Hexadecimal,
|
||||||
);
|
);
|
||||||
|
|
||||||
printer.print_all(input).unwrap();
|
printer.print_all(input).unwrap();
|
||||||
@@ -821,6 +864,7 @@ mod tests {
|
|||||||
true,
|
true,
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
|
Base::Hexadecimal,
|
||||||
);
|
);
|
||||||
|
|
||||||
printer.print_all(input).unwrap();
|
printer.print_all(input).unwrap();
|
||||||
|
|||||||
@@ -551,3 +551,21 @@ mod group {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod base {
|
||||||
|
use super::hexyl;
|
||||||
|
use super::PrettyAssert;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn base2() {
|
||||||
|
hexyl()
|
||||||
|
.arg("ascii")
|
||||||
|
.arg("--plain")
|
||||||
|
.arg("--base=binary")
|
||||||
|
.assert()
|
||||||
|
.success()
|
||||||
|
.pretty_stdout(
|
||||||
|
" 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user