13 Commits
Author SHA1 Message Date
sharkdp 7e46a5d788 Bump version 2019-01-14 10:51:32 +01:00
sharkdp 19030470f9 Add proper Ctrl-C handling 2019-01-14 10:40:04 +01:00
sharkdp b27ab61608 Fix behavior on broken pipe 2019-01-14 10:17:14 +01:00
sharkdp 39a83021dc Use 16 colors instead of 256 2019-01-14 09:53:33 +01:00
sharkdp f6f41e76aa Use --color=always/auto/never instead of --no-color 2019-01-14 09:14:47 +01:00
Bennett Hardwick 74861353fa Add --no-color flag to disable ANSI sequences 2019-01-14 09:14:47 +01:00
Arnav Borborah 78d8cc516f Change exit code for help and version messages to 0 2019-01-13 18:35:15 +01:00
arnavb11 faebee7b8c Move duplicate code out of conditional 2019-01-13 18:03:07 +01:00
arnavb11 92c974d066 Handle clap errors and other errors separately 2019-01-13 18:03:07 +01:00
arnavb11 866feb2966 Refactor error handling into a if-let 2019-01-13 18:03:07 +01:00
arnavb11 952fff0ee4 Use instead of for consistency 2019-01-13 18:03:07 +01:00
Lily Ballard 1619cd5afe Bump the buffer size to 256 bytes
On my machine this produces an 11% speedup (benchmarked reading a 1.3MB
file from disk). Higher buffer sizes don't seem to make a difference.
2019-01-13 17:35:51 +01:00
Lily Ballard 20694bb628 Write lines into an in-memory buffer before printing to stdout
Running them through an in-memory buffer first produces a 25% speedup
(when tested against a 1.3MB file).
2019-01-13 17:35:51 +01:00
4 changed files with 169 additions and 55 deletions

No files matched your search

Generated
+44 -1
View File
@@ -21,6 +21,16 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.32.0"
@@ -36,12 +46,23 @@ dependencies = [
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctrlc"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexyl"
version = "0.3.1"
version = "0.4.0"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -58,6 +79,18 @@ name = "libc"
version = "0.2.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nix"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.50"
@@ -115,6 +148,11 @@ name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
@@ -148,9 +186,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
@@ -159,6 +201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+3 -1
View File
@@ -7,10 +7,12 @@ license = "MIT/Apache-2.0"
name = "hexyl"
readme = "README.md"
repository = "https://github.com/sharkdp/hexyl"
version = "0.3.1"
version = "0.4.0"
[dependencies]
ansi_term = "0.11"
atty = "0.2"
ctrlc = "3.1"
[dependencies.clap]
version = "2"
+2 -2
View File
@@ -22,8 +22,8 @@ of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, o
### On Debian-based systems
``` bash
wget "https://github.com/sharkdp/hexyl/releases/download/v0.3.1/hexyl_0.3.1_amd64.deb"
sudo dpkg -i hexyl_0.3.1_amd64.deb
wget "https://github.com/sharkdp/hexyl/releases/download/v0.4.0/hexyl_0.4.0_amd64.deb"
sudo dpkg -i hexyl_0.4.0_amd64.deb
```
### On Arch Linux
+120 -51
View File
@@ -1,23 +1,29 @@
#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate ctrlc;
use std::fs::File;
use std::io::{self, prelude::*, StdoutLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use clap::{App, AppSettings, Arg};
use ansi_term::Colour;
use ansi_term::Colour::Fixed;
use ansi_term::Color;
use ansi_term::Color::Fixed;
const BUFFER_SIZE: usize = 64;
use atty::Stream;
const COLOR_NULL: Colour = Fixed(242); // grey
const COLOR_OFFSET: Colour = Fixed(242); // grey
const COLOR_ASCII_PRINTABLE: Colour = Fixed(81); // cyan
const COLOR_ASCII_WHITESPACE: Colour = Fixed(148); // green
const COLOR_ASCII_OTHER: Colour = Fixed(197); // magenta
const COLOR_NONASCII: Colour = Fixed(208); // orange
const BUFFER_SIZE: usize = 256;
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;
enum ByteCategory {
Null,
@@ -48,7 +54,7 @@ impl Byte {
}
}
fn color(self) -> &'static Colour {
fn color(self) -> &'static Color {
use ByteCategory::*;
match self.category() {
@@ -76,27 +82,42 @@ impl Byte {
struct Printer<'a> {
idx: usize,
line: Vec<u8>,
/// The raw bytes used as input for the current line.
raw_line: Vec<u8>,
/// The buffered line built with each byte, ready to print to stdout.
buffer_line: Vec<u8>,
stdout: StdoutLock<'a>,
show_color: bool,
byte_hex_table: Vec<String>,
byte_char_table: Vec<String>,
}
impl<'a> Printer<'a> {
fn new(stdout: StdoutLock) -> Printer {
fn new(stdout: StdoutLock, show_color: bool) -> Printer {
Printer {
idx: 1,
line: vec![],
raw_line: vec![],
buffer_line: vec![],
stdout,
show_color,
byte_hex_table: (0u8..=u8::max_value())
.map(|i| format!("{} ", Byte(i).color().paint(format!("{:02x}", i))))
.map(|i| {
let byte_hex = format!("{:02x} ", i);
if show_color {
Byte(i).color().paint(byte_hex).to_string()
} else {
byte_hex
}
})
.collect(),
byte_char_table: (0u8..=u8::max_value())
.map(|i| {
format!(
"{}",
Byte(i).color().paint(format!("{}", Byte(i).as_char()))
)
let byte_char = format!("{}", Byte(i).as_char());
if show_color {
Byte(i).color().paint(byte_char).to_string()
} else {
byte_char
}
})
.collect(),
}
@@ -123,23 +144,23 @@ impl<'a> Printer<'a> {
fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % 16 == 1 {
let style = COLOR_OFFSET.normal();
write!(
self.stdout,
"│{}{:08x}{}│ ",
style.prefix(),
self.idx - 1,
style.suffix()
)?;
let byte_index = format!("{:08x}", self.idx - 1);
let formatted_string = if self.show_color {
format!("{}", style.paint(byte_index))
} else {
byte_index
};
let _ = write!(&mut self.buffer_line, "│{}│ ", formatted_string);
}
write!(self.stdout, "{}", self.byte_hex_table[b as usize])?;
self.line.push(b);
write!(&mut self.buffer_line, "{}", self.byte_hex_table[b as usize])?;
self.raw_line.push(b);
match self.idx % 16 {
8 => write!(self.stdout, "┊ ")?,
0 => {
self.print_textline()?;
8 => {
let _ = write!(&mut self.buffer_line, "┊ ");
}
0 => self.print_textline()?,
_ => {}
}
@@ -149,48 +170,54 @@ impl<'a> Printer<'a> {
}
fn print_textline(&mut self) -> io::Result<()> {
let len = self.line.len();
let len = self.raw_line.len();
if len == 0 {
return Ok(());
}
if len < 8 {
write!(
self.stdout,
let _ = write!(
&mut self.buffer_line,
"{0:1$}┊{0:2$}│",
"",
3 * (8 - len),
1 + 3 * 8
)?;
);
} else {
write!(self.stdout, "{0:1$}│", "", 3 * (16 - len))?;
let _ = write!(&mut self.buffer_line, "{0:1$}│", "", 3 * (16 - len));
}
let mut idx = 1;
for &b in self.line.iter() {
write!(self.stdout, "{}", self.byte_char_table[b as usize])?;
for &b in self.raw_line.iter() {
let _ = write!(
&mut self.buffer_line,
"{}",
self.byte_char_table[b as usize]
);
if idx == 8 {
write!(self.stdout, "┊").ok();
let _ = write!(&mut self.buffer_line, "┊");
}
idx += 1;
}
if len < 8 {
writeln!(self.stdout, "{0:1$}┊{0:2$}│ ", "", 8 - len, 8)?;
let _ = writeln!(&mut self.buffer_line, "{0:1$}┊{0:2$}│ ", "", 8 - len, 8);
} else {
writeln!(self.stdout, "{0:1$}│", "", 16 - len)?;
let _ = writeln!(&mut self.buffer_line, "{0:1$}│", "", 16 - len);
}
self.stdout.write_all(&self.buffer_line)?;
self.line.clear();
self.raw_line.clear();
self.buffer_line.clear();
Ok(())
}
}
fn run() -> io::Result<()> {
fn run() -> Result<(), Box<::std::error::Error>> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
@@ -205,10 +232,22 @@ fn run() -> io::Result<()> {
.long("length")
.takes_value(true)
.value_name("N")
.help("read only N bytes from the input"),
.help("Read only N bytes from the input"),
)
.arg(
Arg::with_name("color")
.long("color")
.takes_value(true)
.value_name("when")
.possible_values(&["always", "auto", "never"])
.default_value("always")
.help(
"When to use colors. The auto-mode only displays colors if the output \
goes to an interactive terminal",
),
);
let matches = app.get_matches();
let matches = app.get_matches_safe()?;
let stdin = io::stdin();
@@ -224,23 +263,43 @@ fn run() -> io::Result<()> {
reader = Box::new(reader.take(length));
}
let show_color = match matches.value_of("color") {
Some("never") => false,
Some("auto") => atty::is(Stream::Stdout),
_ => true,
};
// Set up Ctrl-C handler
let cancelled = Arc::new(AtomicBool::new(false));
let c = cancelled.clone();
ctrlc::set_handler(move || {
c.store(true, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
let stdout = io::stdout();
let mut printer = Printer::new(stdout.lock());
let mut printer = Printer::new(stdout.lock(), show_color);
printer.header();
let mut buffer = [0; BUFFER_SIZE];
loop {
'mainloop: loop {
let size = reader.read(&mut buffer)?;
if size == 0 {
break;
}
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
for b in &buffer[..size] {
let res = printer.print_byte(*b);
if res.is_err() {
// Broken pipe
break;
break 'mainloop;
}
}
}
@@ -258,11 +317,21 @@ fn main() {
let _ = ansi_term::enable_ansi_support();
let result = run();
match result {
Err(err) => {
if let Err(err) = result {
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
eprint!("{}", clap_err); // Clap errors already have newlines
match clap_err.kind {
// The exit code should not indicate an error for --help / --version
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
std::process::exit(0)
}
_ => (),
}
} else {
eprintln!("Error: {}", err);
std::process::exit(1);
}
Ok(()) => {}
std::process::exit(1);
}
}