17 Commits
Author SHA1 Message Date
sharkdp c75a882081 Update dependencies 2019-10-08 20:32:56 +02:00
sharkdp 81787d0858 Bump version 2019-10-08 20:31:56 +02:00
sharkdp 8e77507686 Enable unit tests on Travis CI 2019-10-08 20:29:52 +02:00
sharkdp 9c46ced572 Enable LTO 2019-10-08 20:09:10 +02:00
sharkdp 161ddd7ea1 Only print to buffer if line is not squeezed 2019-10-08 20:09:10 +02:00
David Peter 53110bac6f Add sponsor button as an experiment :-) 2019-10-08 18:35:09 +02:00
Tom Milligan d64053771c hexyl: add display offset option 2019-10-07 23:36:53 +02:00
Noah AL-Shihabi 23f199ffbd Remove additional space found on short input 2019-10-01 20:23:57 +02:00
Tom Milligan 175847787d fix crate import for travis tests 2019-07-17 19:31:37 +02:00
Tom Milligan 622a084ea6 run: factor out mainloop and add tests 2019-07-17 19:31:37 +02:00
Tom Milligan 4c48eedb18 printer: accept any Write as output 2019-07-17 19:31:37 +02:00
Tom Milligan ca8970b5cc lib: main and run in bin, everything else in lib 2019-07-17 19:31:37 +02:00
Tom Milligan fb49b633b2 lib: created, moved bin 2019-07-17 19:31:37 +02:00
sharkdp 82c34fe39b Bump version 2019-05-30 15:35:21 +02:00
Armin Widegreen c4028bb213 Consider color when printing squeeze indicator
When color option has been set to never, the squeeze indicator
character asterisk '*' should not be printed in color.

Fixes #63
2019-05-30 15:29:37 +02:00
Armin Widegreen def066ff92 Fixes squeeze state-machine for first/last char
Consider equality for first and last character of a line.

If the first or last character of a line is not equal to previous character,
the state machine should be set back to probe as the line should not be
squeezed.

The unit tests has been expanded for testing the use case described in #62.
Further the body of all unit tests has been simplified.

Fixes #62
2019-05-30 15:29:37 +02:00
sharkdp 17c2c61bad Update dependencies, closes #61 2019-05-28 21:37:33 +02:00
8 changed files with 538 additions and 264 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
github: sharkdp
+1 -1
View File
@@ -43,7 +43,7 @@ install:
script: script:
# Incorporate TARGET env var to the build and test process # Incorporate TARGET env var to the build and test process
- cargo build --target $TARGET --verbose - cargo build --target $TARGET --verbose
#- cargo test --target $TARGET --verbose - cargo test --target $TARGET --verbose
before_deploy: before_deploy:
- bash ci/before_deploy.bash - bash ci/before_deploy.bash
Generated
+45 -63
View File
@@ -5,32 +5,39 @@ name = "ansi_term"
version = "0.11.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.11" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.0.4" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.35" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.7" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -39,32 +46,32 @@ version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ctrlc" name = "ctrlc"
version = "3.1.1" version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "hexyl" name = "hexyl"
version = "0.5.0" version = "0.6.0"
dependencies = [ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -78,34 +85,21 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.51" version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.11.0" version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (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.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.8.0"
@@ -117,32 +111,22 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -162,7 +146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.7" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -186,26 +170,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "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 ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" "checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" "checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" "checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "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 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.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+6 -2
View File
@@ -7,14 +7,18 @@ license = "MIT/Apache-2.0"
name = "hexyl" name = "hexyl"
readme = "README.md" readme = "README.md"
repository = "https://github.com/sharkdp/hexyl" repository = "https://github.com/sharkdp/hexyl"
version = "0.5.0" version = "0.6.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
ansi_term = "0.11" ansi_term = "0.12"
atty = "0.2" atty = "0.2"
ctrlc = "3.1" ctrlc = "3.1"
[dependencies.clap] [dependencies.clap]
version = "2" version = "2"
features = ["suggestions", "color", "wrap_help"] features = ["suggestions", "color", "wrap_help"]
[profile.release]
lto = true
codegen-units = 1
+2 -2
View File
@@ -22,8 +22,8 @@ of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, o
### On Debian-based systems ### On Debian-based systems
``` bash ``` bash
wget "https://github.com/sharkdp/hexyl/releases/download/v0.5.0/hexyl_0.5.0_amd64.deb" wget "https://github.com/sharkdp/hexyl/releases/download/v0.6.0/hexyl_0.6.0_amd64.deb"
sudo dpkg -i hexyl_0.5.0_amd64.deb sudo dpkg -i hexyl_0.6.0_amd64.deb
``` ```
### On Arch Linux ### On Arch Linux
+166
View File
@@ -0,0 +1,166 @@
#[macro_use]
extern crate clap;
use atty;
use ctrlc;
use std::fs::File;
use std::io::{self, prelude::*};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use clap::{App, AppSettings, Arg};
use atty::Stream;
use hexyl::{BorderStyle, Printer};
fn run() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::UnifiedHelpMessage)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("length")
.short("n")
.long("length")
.takes_value(true)
.value_name("N")
.help("Read only N bytes from the input"),
)
.arg(
Arg::with_name("bytes")
.short("c")
.long("bytes")
.takes_value(true)
.value_name("N")
.help("An alias for -n/--length"),
)
.arg(
Arg::with_name("nosqueezing")
.short("v")
.long("no-squeezing")
.help(
"Displays all input data. Otherwise any number of groups of output \
lines which would be identical to the preceding group of lines, are \
replaced with a line comprised of a single asterisk.",
),
)
.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",
),
)
.arg(
Arg::with_name("border")
.long("border")
.takes_value(true)
.possible_values(&["unicode", "ascii", "none"])
.default_value("unicode")
.help("Whether to draw a border with unicode or ASCII characters, or none at all"),
)
.arg(
Arg::with_name("display_offset")
.short("o")
.long("display-offset")
.takes_value(true)
.value_name("OFFSET")
.help("Add OFFSET to the displayed file position."),
);
let matches = app.get_matches_safe()?;
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
};
let length_arg = matches.value_of("length").or(matches.value_of("bytes"));
if let Some(length) = length_arg.and_then(parse_hex_or_int) {
reader = Box::new(reader.take(length));
}
let show_color = match matches.value_of("color") {
Some("never") => false,
Some("auto") => atty::is(Stream::Stdout),
_ => true,
};
let border_style = match matches.value_of("border") {
Some("unicode") => BorderStyle::Unicode,
Some("ascii") => BorderStyle::Ascii,
_ => BorderStyle::None,
};
let squeeze = !matches.is_present("nosqueezing");
let display_offset = matches
.value_of("display_offset")
.and_then(parse_hex_or_int)
.unwrap_or(0);
// 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 stdout_lock = stdout.lock();
let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
printer.display_offset(display_offset as usize);
printer.print_all(&mut reader, Some(cancelled))?;
Ok(())
}
fn main() {
// Enable ANSI support for Windows
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
let result = run();
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);
}
}
fn parse_hex_or_int(n: &str) -> Option<u64> {
if n.starts_with("0x") {
u64::from_str_radix(n.trim_start_matches("0x"), 16).ok()
} else {
n.parse::<u64>().ok()
}
}
+115 -157
View File
@@ -1,24 +1,16 @@
#[macro_use] pub mod squeezer;
extern crate clap;
use atty; use std::io::{self, Read, Write};
use ctrlc;
mod squeezer;
use std::fs::File;
use std::io::{self, prelude::*, StdoutLock};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use clap::{App, AppSettings, Arg};
use ansi_term::Color; use ansi_term::Color;
use ansi_term::Color::Fixed; use ansi_term::Color::Fixed;
use atty::Stream;
use crate::squeezer::{SqueezeAction, Squeezer}; use crate::squeezer::{SqueezeAction, Squeezer};
type Cancelled = Arc<AtomicBool>;
const BUFFER_SIZE: usize = 256; const BUFFER_SIZE: usize = 256;
const COLOR_NULL: Color = Fixed(242); // grey const COLOR_NULL: Color = Fixed(242); // grey
@@ -28,7 +20,7 @@ const COLOR_ASCII_WHITESPACE: Color = Color::Green;
const COLOR_ASCII_OTHER: Color = Color::Purple; const COLOR_ASCII_OTHER: Color = Color::Purple;
const COLOR_NONASCII: Color = Color::Yellow; const COLOR_NONASCII: Color = Color::Yellow;
enum ByteCategory { pub enum ByteCategory {
Null, Null,
AsciiPrintable, AsciiPrintable,
AsciiWhitespace, AsciiWhitespace,
@@ -90,7 +82,7 @@ struct BorderElements {
right_corner: char, right_corner: char,
} }
enum BorderStyle { pub enum BorderStyle {
Unicode, Unicode,
Ascii, Ascii,
None, None,
@@ -150,33 +142,34 @@ impl BorderStyle {
} }
} }
struct Printer<'a> { pub struct Printer<'a, Writer: Write> {
idx: usize, idx: usize,
/// The raw bytes used as input for the current line. /// The raw bytes used as input for the current line.
raw_line: Vec<u8>, raw_line: Vec<u8>,
/// The buffered line built with each byte, ready to print to stdout. /// The buffered line built with each byte, ready to print to writer.
buffer_line: Vec<u8>, buffer_line: Vec<u8>,
stdout: StdoutLock<'a>, writer: &'a mut Writer,
show_color: bool, show_color: bool,
border_style: BorderStyle, border_style: BorderStyle,
header_was_printed: bool, header_was_printed: bool,
byte_hex_table: Vec<String>, byte_hex_table: Vec<String>,
byte_char_table: Vec<String>, byte_char_table: Vec<String>,
squeezer: Squeezer, squeezer: Squeezer,
display_offset: usize,
} }
impl<'a> Printer<'a> { impl<'a, Writer: Write> Printer<'a, Writer> {
fn new( pub fn new(
stdout: StdoutLock<'_>, writer: &'a mut Writer,
show_color: bool, show_color: bool,
border_style: BorderStyle, border_style: BorderStyle,
use_squeeze: bool, use_squeeze: bool,
) -> Printer<'_> { ) -> Printer<'a, Writer> {
Printer { Printer {
idx: 1, idx: 1,
raw_line: vec![], raw_line: vec![],
buffer_line: vec![], buffer_line: vec![],
stdout, writer,
show_color, show_color,
border_style, border_style,
header_was_printed: false, header_was_printed: false,
@@ -201,17 +194,23 @@ impl<'a> Printer<'a> {
}) })
.collect(), .collect(),
squeezer: Squeezer::new(use_squeeze), squeezer: Squeezer::new(use_squeeze),
display_offset: 0,
} }
} }
fn header(&mut self) { pub fn display_offset(&mut self, display_offset: usize) -> &mut Self {
self.display_offset = display_offset;
self
}
pub fn header(&mut self) {
if let Some(border_elements) = self.border_style.header_elems() { if let Some(border_elements) = self.border_style.header_elems() {
let h = border_elements.horizontal_line; let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8); let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25); let h25 = h.to_string().repeat(25);
writeln!( writeln!(
self.stdout, self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner, l = border_elements.left_corner,
c = border_elements.column_separator, c = border_elements.column_separator,
@@ -223,14 +222,14 @@ impl<'a> Printer<'a> {
} }
} }
fn footer(&mut self) { pub fn footer(&mut self) {
if let Some(border_elements) = self.border_style.footer_elems() { if let Some(border_elements) = self.border_style.footer_elems() {
let h = border_elements.horizontal_line; let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8); let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25); let h25 = h.to_string().repeat(25);
writeln!( writeln!(
self.stdout, self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner, l = border_elements.left_corner,
c = border_elements.column_separator, c = border_elements.column_separator,
@@ -249,7 +248,7 @@ impl<'a> Printer<'a> {
} }
let style = COLOR_OFFSET.normal(); let style = COLOR_OFFSET.normal();
let byte_index = format!("{:08x}", self.idx - 1); let byte_index = format!("{:08x}", (self.idx - 1) + self.display_offset);
let formatted_string = if self.show_color { let formatted_string = if self.show_color {
format!("{}", style.paint(byte_index)) format!("{}", style.paint(byte_index))
} else { } else {
@@ -264,7 +263,7 @@ impl<'a> Printer<'a> {
); );
} }
fn print_byte(&mut self, b: u8) -> io::Result<()> { pub fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % 16 == 1 { if self.idx % 16 == 1 {
self.print_position_indicator(); self.print_position_indicator();
} }
@@ -289,7 +288,7 @@ impl<'a> Printer<'a> {
Ok(()) Ok(())
} }
fn print_textline(&mut self) -> io::Result<()> { pub fn print_textline(&mut self) -> io::Result<()> {
let len = self.raw_line.len(); let len = self.raw_line.len();
if len == 0 { if len == 0 {
@@ -305,11 +304,14 @@ impl<'a> Printer<'a> {
self.border_style.inner_sep(), self.border_style.inner_sep(),
self.border_style.outer_sep(), self.border_style.outer_sep(),
); );
self.stdout.write_all(&self.buffer_line)?; self.writer.write_all(&self.buffer_line)?;
} }
return Ok(()); return Ok(());
} }
let squeeze_action = self.squeezer.action();
if squeeze_action != SqueezeAction::Delete {
if len < 8 { if len < 8 {
let _ = write!( let _ = write!(
&mut self.buffer_line, &mut self.buffer_line,
@@ -348,7 +350,7 @@ impl<'a> Printer<'a> {
if len < 8 { if len < 8 {
let _ = writeln!( let _ = writeln!(
&mut self.buffer_line, &mut self.buffer_line,
"{0:1$}{3}{0:2$}{4} ", "{0:1$}{3}{0:2$}{4}",
"", "",
8 - len, 8 - len,
8, 8,
@@ -364,15 +366,21 @@ impl<'a> Printer<'a> {
self.border_style.outer_sep() self.border_style.outer_sep()
); );
} }
}
match self.squeezer.action() { match squeeze_action {
SqueezeAction::Print => { SqueezeAction::Print => {
self.buffer_line.clear(); self.buffer_line.clear();
let style = COLOR_OFFSET.normal(); let style = COLOR_OFFSET.normal();
let asterisk = if self.show_color {
format!("{}", style.paint("*"))
} else {
String::from("*")
};
let _ = writeln!( let _ = writeln!(
&mut self.buffer_line, &mut self.buffer_line,
"{5}{0}{1:2$}{5}{1:3$}{6}{1:3$}{5}{1:4$}{6}{1:4$}{5}", "{5}{0}{1:2$}{5}{1:3$}{6}{1:3$}{5}{1:4$}{6}{1:4$}{5}",
style.paint("*"), asterisk,
"", "",
7, 7,
25, 25,
@@ -385,118 +393,27 @@ impl<'a> Printer<'a> {
SqueezeAction::Ignore => (), SqueezeAction::Ignore => (),
} }
self.stdout.write_all(&self.buffer_line)?; self.writer.write_all(&self.buffer_line)?;
self.raw_line.clear(); self.raw_line.clear();
self.buffer_line.clear(); self.buffer_line.clear();
self.squeezer.advance();
Ok(()) Ok(())
} }
}
fn run() -> Result<(), Box<dyn std::error::Error>> { pub fn header_was_printed(&self) -> bool {
let app = App::new(crate_name!()) self.header_was_printed
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::UnifiedHelpMessage)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("length")
.short("n")
.long("length")
.takes_value(true)
.value_name("N")
.help("Read only N bytes from the input"),
)
.arg(
Arg::with_name("bytes")
.short("c")
.long("bytes")
.takes_value(true)
.value_name("N")
.help("An alias for -n/--length"),
)
.arg(
Arg::with_name("nosqueezing")
.short("v")
.long("no-squeezing")
.help(
"Displays all input data. Otherwise any number of groups of output \
lines which would be identical to the preceding group of lines, are \
replaced with a line comprised of a single asterisk.",
),
)
.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",
),
)
.arg(
Arg::with_name("border")
.long("border")
.takes_value(true)
.possible_values(&["unicode", "ascii", "none"])
.default_value("unicode")
.help("Whether to draw a border with unicode or ASCII characters, or none at all"),
);
let matches = app.get_matches_safe()?;
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
};
let length_arg = matches.value_of("length").or(matches.value_of("bytes"));
if let Some(length) = length_arg.and_then(|n| {
if n.starts_with("0x") {
u64::from_str_radix(n.trim_start_matches("0x"), 16).ok()
} else {
n.parse::<u64>().ok()
}
}) {
reader = Box::new(reader.take(length));
} }
let show_color = match matches.value_of("color") { /// Loop through the given `Reader`, printing until the `Reader` buffer
Some("never") => false, /// is exhausted, or the optional `cancelled` bool is set to true.
Some("auto") => atty::is(Stream::Stdout), pub fn print_all<Reader: Read>(
_ => true, &mut self,
}; mut reader: Reader,
canceller: Option<Cancelled>,
let border_style = match matches.value_of("border") { ) -> Result<(), Box<dyn std::error::Error>> {
Some("unicode") => BorderStyle::Unicode,
Some("ascii") => BorderStyle::Ascii,
_ => BorderStyle::None,
};
let squeeze = !matches.is_present("nosqueezing");
// 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(), show_color, border_style, squeeze);
let mut buffer = [0; BUFFER_SIZE]; let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop { 'mainloop: loop {
let size = reader.read(&mut buffer)?; let size = reader.read(&mut buffer)?;
@@ -504,13 +421,15 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
break; break;
} }
if let Some(cancelled) = &canceller {
if cancelled.load(Ordering::SeqCst) { if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled."); eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT std::process::exit(130); // Set exit code to 128 + SIGINT
} }
}
for b in &buffer[..size] { for b in &buffer[..size] {
let res = printer.print_byte(*b); let res = self.print_byte(*b);
if res.is_err() { if res.is_err() {
// Broken pipe // Broken pipe
@@ -520,36 +439,75 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
} }
// Finish last line // Finish last line
printer.print_textline().ok(); self.print_textline().ok();
if !printer.header_was_printed { if !self.header_was_printed() {
printer.header(); self.header();
} }
printer.footer(); self.footer();
Ok(()) Ok(())
}
} }
#[cfg(test)]
mod tests {
use std::io;
use std::str;
fn main() { use super::*;
// Enable ANSI support for Windows
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
let result = run(); fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) -> () {
let mut output = vec![];
let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true);
if let Err(err) = result { printer.print_all(input, None).unwrap();
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
eprint!("{}", clap_err); // Clap errors already have newlines
match clap_err.kind { let actual_string: &str = str::from_utf8(&output).unwrap();
// The exit code should not indicate an error for --help / --version assert_eq!(actual_string, expected_string)
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
std::process::exit(0)
} }
_ => (),
#[test]
fn empty_file_passes() {
let input = io::empty();
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
assert_print_all_output(input, expected_string);
} }
} else {
eprintln!("Error: {}", err); #[test]
fn short_input_passes() {
let input = io::Cursor::new(b"spam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
assert_print_all_output(input, expected_string);
} }
std::process::exit(1);
#[test]
fn display_offset() {
let input = io::Cursor::new(b"spamspamspamspamspam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│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 ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
let mut output = vec![];
let mut printer: Printer<Vec<u8>> =
Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.display_offset(0xdeadbeef);
printer.print_all(input, None).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
} }
} }
+202 -39
View File
@@ -54,6 +54,9 @@ impl Squeezer {
let eq = b == self.byte; let eq = b == self.byte;
if i % LSIZE == 0 { if i % LSIZE == 0 {
if !eq {
self.state = Probe;
} else {
self.state = match self.state { self.state = match self.state {
NoSqueeze => Probe, NoSqueeze => Probe,
Probe => SqueezeActiveFirstLine, Probe => SqueezeActiveFirstLine,
@@ -63,8 +66,11 @@ impl Squeezer {
Squeeze => SqueezeActive, Squeeze => SqueezeActive,
Disabled => Disabled, Disabled => Disabled,
}; };
}
} else if !eq { } else if !eq {
if (i % LSIZE == 1 && self.state != Probe) || i % LSIZE != 1 { if i % LSIZE == 1 {
self.state = Probe;
} else if i % LSIZE != 1 {
self.state = NoSqueeze; self.state = NoSqueeze;
} }
} }
@@ -80,17 +86,23 @@ impl Squeezer {
} }
} }
pub fn action(&mut self) -> SqueezeAction { pub fn action(&self) -> SqueezeAction {
match self.state {
SqueezeState::SqueezeFirstLine => SqueezeAction::Print,
SqueezeState::Squeeze => SqueezeAction::Delete,
_ => SqueezeAction::Ignore,
}
}
pub fn advance(&mut self) {
match self.state { match self.state {
SqueezeState::SqueezeFirstLine => { SqueezeState::SqueezeFirstLine => {
self.state = SqueezeState::SqueezeActive; self.state = SqueezeState::SqueezeActive;
SqueezeAction::Print
} }
SqueezeState::Squeeze => { SqueezeState::Squeeze => {
self.state = SqueezeState::SqueezeActive; self.state = SqueezeState::SqueezeActive;
SqueezeAction::Delete
} }
_ => SqueezeAction::Ignore, _ => {}
} }
} }
} }
@@ -106,6 +118,7 @@ mod tests {
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
@@ -113,22 +126,28 @@ mod tests {
SqueezeAction::Delete, // delete reoccurring line SqueezeAction::Delete, // delete reoccurring line
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
} }
} }
#[test] #[test]
fn incomplete_while_squeeze() { fn incomplete_while_squeeze() {
const LINES: usize = 3; // fourth line only has 12 bytes and should be printed
// third line only has 12 bytes and should be printed let v = vec![0u8; 3 * LSIZE + 12];
let v = vec![0u8; LINES * LSIZE + 12];
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
@@ -137,23 +156,22 @@ mod tests {
SqueezeAction::Ignore, // last line only 12 bytes, print it SqueezeAction::Ignore, // last line only 12 bytes, print it
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
} }
for i in LSIZE * LINES..LINES * LSIZE + 12 {
s.process(v[i], i + 1);
}
assert_eq!(s.action(), exp[3]);
} }
#[test] #[test]
/// all three lines are different, print all /// all three lines are different, print all
fn three_different_lines() { fn three_different_lines() {
const LINES: usize = 3;
let mut v: Vec<u8> = vec![]; let mut v: Vec<u8> = vec![];
v.extend(vec![0u8; 16]); v.extend(vec![0u8; 16]);
v.extend(vec![1u8; 16]); v.extend(vec![1u8; 16]);
@@ -162,6 +180,7 @@ mod tests {
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
@@ -169,11 +188,17 @@ mod tests {
SqueezeAction::Ignore, // different SqueezeAction::Ignore, // different
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); let action = s.action();
assert_eq!(action, exp[line]);
s.advance();
line += 1;
} }
} }
@@ -188,6 +213,7 @@ mod tests {
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
@@ -195,11 +221,17 @@ mod tests {
SqueezeAction::Ignore, // different lines, print again SqueezeAction::Ignore, // different lines, print again
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
} }
} }
@@ -214,24 +246,30 @@ mod tests {
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Ignore, // print squeeze symbol SqueezeAction::Ignore, // print squeeze symbol
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
} }
} }
#[test] #[test]
/// all three lines never become squeeze candidate (diff within line) /// all three lines never become squeeze candidate (diff within line)
fn never_squeeze_candidate() { fn never_squeeze_candidate() {
const LINES: usize = 3;
let mut v = vec![]; let mut v = vec![];
v.extend(vec![0u8; 8]); v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]); v.extend(vec![1u8; 8]);
@@ -243,6 +281,7 @@ mod tests {
let mut s = Squeezer::new(true); let mut s = Squeezer::new(true);
// just initialized // just initialized
assert_eq!(s.action(), SqueezeAction::Ignore); assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![ let exp = vec![
SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // first line, print as is
@@ -250,11 +289,135 @@ mod tests {
SqueezeAction::Ignore, // print squeeze symbol SqueezeAction::Ignore, // print squeeze symbol
]; ];
for z in 0..LINES { let mut line = 0;
for i in z * LSIZE..(z + 1) * LSIZE { let mut idx = 1;
s.process(v[i], i + 1); for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
} }
assert_eq!(s.action(), exp[z]); let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn mix_everything() {
let mut v = vec![];
v.extend(vec![10u8; 16]); // print
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 16]); // print
v.extend(vec![0u8; 16]); // *
v.extend(vec![10u8; 16]); // print
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 16]); // print
v.extend(vec![0u8; 16]); // *
v.extend(vec![0u8; 16]); // delete
v.extend(vec![0u8; 16]); // delete*
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 12]); // print, only 12 bytes
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Print,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Print,
SqueezeAction::Delete,
SqueezeAction::Delete,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn last_char_diff() {
// see issue #62
let mut v = vec![];
v.extend(vec![20u8; 16]);
v.extend(vec![20u8; 15]);
v.push(61);
v.extend(vec![20u8; 16]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Print, // print '*' char
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
}
}
#[test]
fn first_char_diff() {
// see issue #62
let mut v = vec![];
v.extend(vec![20u8; 16]);
v.push(61);
v.extend(vec![20u8; 15]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
} }
} }
} }