mirror of
https://github.com/sharkdp/hexyl.git
synced 2026-10-30 09:05:54 +00:00
BREAKING CHANGE: improvement: change byte counts to be a positive i64 (with newtype PositiveI64) instead of a u64 to take advantage of new skip impl for --seek with non-Seek Inputs like STDIN
This is technically a breaking change because it halves the upper range of accepted values for byte counts. I thought that fine under the assumption it will be rare for users to ever want to specify something in the upper half of `u64`'s value range, especially considering that this can pave the way for having negative byte offsets (instead of just counts).
This commit is contained in:
1 parent
6d5c61b671
commit
ddc3a5c04a
1 file changed
+59
-22
+59
-22
@@ -1,6 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, prelude::*, SeekFrom};
|
use std::io::{self, prelude::*, SeekFrom};
|
||||||
|
|
||||||
@@ -103,15 +104,15 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let block_size = matches
|
let block_size = matches
|
||||||
.value_of("block_size")
|
.value_of("block_size")
|
||||||
.and_then(|bs| bs.parse::<u64>().ok())
|
.and_then(|bs| bs.parse().ok().and_then(PositiveI64::new))
|
||||||
.unwrap_or(512);
|
.unwrap_or_else(|| PositiveI64::new(512).unwrap());
|
||||||
|
|
||||||
let skip_arg = matches
|
let skip_arg = matches
|
||||||
.value_of("skip")
|
.value_of("skip")
|
||||||
.and_then(|s| parse_byte_count(s, block_size));
|
.and_then(|s| parse_byte_count(s, block_size));
|
||||||
|
|
||||||
if let Some(skip) = skip_arg {
|
if let Some(skip) = skip_arg.clone() {
|
||||||
reader.seek(SeekFrom::Start(skip))?;
|
reader.seek(SeekFrom::Current(skip.into_inner()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let length_arg = matches
|
let length_arg = matches
|
||||||
@@ -120,7 +121,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let mut reader = if let Some(length) = length_arg.and_then(|s| parse_byte_count(s, block_size))
|
let mut reader = if let Some(length) = length_arg.and_then(|s| parse_byte_count(s, block_size))
|
||||||
{
|
{
|
||||||
Box::new(reader.take(length))
|
Box::new(reader.take(length.into()))
|
||||||
} else {
|
} else {
|
||||||
reader.into_inner()
|
reader.into_inner()
|
||||||
};
|
};
|
||||||
@@ -143,7 +144,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.value_of("display_offset")
|
.value_of("display_offset")
|
||||||
.and_then(|s| parse_byte_count(s, block_size))
|
.and_then(|s| parse_byte_count(s, block_size))
|
||||||
.or(skip_arg)
|
.or(skip_arg)
|
||||||
.unwrap_or(0);
|
.unwrap_or_default()
|
||||||
|
.into();
|
||||||
|
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
let mut stdout_lock = stdout.lock();
|
let mut stdout_lock = stdout.lock();
|
||||||
@@ -180,23 +182,49 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_byte_count(n: &str, block_size: u64) -> Option<u64> {
|
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct PositiveI64(i64);
|
||||||
|
|
||||||
|
impl PositiveI64 {
|
||||||
|
pub fn new(x: i64) -> Option<Self> {
|
||||||
|
if x.is_negative() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self(x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> i64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u64> for PositiveI64 {
|
||||||
|
fn into(self) -> u64 {
|
||||||
|
u64::try_from(self.0)
|
||||||
|
.expect("invariant broken: PositiveI64 should contain a positive i64 value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_byte_count(n: &str, block_size: PositiveI64) -> Option<PositiveI64> {
|
||||||
const HEX_PREFIX: &'static str = "0x";
|
const HEX_PREFIX: &'static str = "0x";
|
||||||
|
|
||||||
let n = {
|
let n = {
|
||||||
let mut chars = n.chars();
|
let mut chars = n.chars();
|
||||||
match chars.next()? {
|
match chars.next()? {
|
||||||
'+' => chars.as_str(),
|
'+' => chars.as_str(),
|
||||||
|
'-' => return None,
|
||||||
_ => n,
|
_ => n,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if n.starts_with(HEX_PREFIX) {
|
if n.starts_with(HEX_PREFIX) {
|
||||||
let n = &n[HEX_PREFIX.len()..];
|
let n = &n[HEX_PREFIX.len()..];
|
||||||
if n.chars().next() == Some('+') {
|
match n.chars().next() {
|
||||||
return None;
|
Some('+') | Some('-') => return None,
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
return u64::from_str_radix(n, 16).ok();
|
return i64::from_str_radix(n, 16).ok().and_then(PositiveI64::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (n, unit_multiplier) = match n.chars().position(|c| !c.is_ascii_digit()) {
|
let (n, unit_multiplier) = match n.chars().position(|c| !c.is_ascii_digit()) {
|
||||||
@@ -207,15 +235,15 @@ fn parse_byte_count(n: &str, block_size: u64) -> Option<u64> {
|
|||||||
n,
|
n,
|
||||||
[
|
[
|
||||||
("b", 1),
|
("b", 1),
|
||||||
("kb", 1000u64.pow(1)),
|
("kb", 1000i64.pow(1)),
|
||||||
("mb", 1000u64.pow(2)),
|
("mb", 1000i64.pow(2)),
|
||||||
("gb", 1000u64.pow(3)),
|
("gb", 1000i64.pow(3)),
|
||||||
("tb", 1000u64.pow(4)),
|
("tb", 1000i64.pow(4)),
|
||||||
("kib", 1024u64.pow(1)),
|
("kib", 1024i64.pow(1)),
|
||||||
("mib", 1024u64.pow(2)),
|
("mib", 1024i64.pow(2)),
|
||||||
("gib", 1024u64.pow(3)),
|
("gib", 1024i64.pow(3)),
|
||||||
("tib", 1024u64.pow(4)),
|
("tib", 1024i64.pow(4)),
|
||||||
("block", block_size),
|
("block", block_size.into_inner()),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
@@ -231,7 +259,10 @@ fn parse_byte_count(n: &str, block_size: u64) -> Option<u64> {
|
|||||||
None => (n, 1),
|
None => (n, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
n.parse::<u64>().ok()?.checked_mul(unit_multiplier)
|
n.parse::<i64>()
|
||||||
|
.ok()?
|
||||||
|
.checked_mul(unit_multiplier)
|
||||||
|
.and_then(PositiveI64::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -241,13 +272,19 @@ fn test_parse_byte_count() {
|
|||||||
success!($input, 512, $expected)
|
success!($input, 512, $expected)
|
||||||
};
|
};
|
||||||
($input: expr, $block_size: expr, $expected: expr) => {
|
($input: expr, $block_size: expr, $expected: expr) => {
|
||||||
assert_eq!(parse_byte_count($input, $block_size), Some($expected));
|
assert_eq!(
|
||||||
|
parse_byte_count($input, PositiveI64::new($block_size).unwrap()),
|
||||||
|
Some(PositiveI64::new($expected).unwrap())
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($input: expr) => {
|
($input: expr) => {
|
||||||
assert_eq!(parse_byte_count($input, 512), None);
|
assert_eq!(
|
||||||
|
parse_byte_count($input, PositiveI64::new(512).unwrap()),
|
||||||
|
None
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user