From de89e4c8f747cfc4818c8ea5cf0c574a3e76167b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 8 Nov 2018 17:04:47 +0000 Subject: [PATCH 1/2] hex: allow specifying a width when printing hashes --- src/hex.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/src/hex.rs b/src/hex.rs index 375d3ab..b014ad3 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -135,26 +135,43 @@ impl<'a> ExactSizeIterator for HexIterator<'a> { /// Output hex into an object implementing `fmt::Write`, which is usually more /// efficient than going through a `String` using `ToHex`. -pub fn format_hex(data: &[u8], mut fmt: T) -> fmt::Result { - for ch in data { - write!(fmt, "{:02x}", *ch)?; +pub fn format_hex(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + let width = f.width().unwrap_or(2 * data.len()); + for _ in (2 * data.len())..width { + f.write_str("0")?; + } + for ch in data.into_iter().take(width / 2) { + write!(f, "{:02x}", *ch)?; + } + if width < 2 * data.len() && width % 2 == 1 { + write!(f, "{:x}", data[width / 2] / 16)?; } Ok(()) } /// Output hex in reverse order; used for Sha256dHash whose standard hex encoding /// has the bytes reversed. -pub fn format_hex_reverse(data: &[u8], mut fmt: T) -> fmt::Result { - for ch in data.iter().rev() { - write!(fmt, "{:02x}", *ch)?; +pub fn format_hex_reverse(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + let width = f.width().unwrap_or(2 * data.len()); + for _ in (2 * data.len())..width { + f.write_str("0")?; + } + for ch in data.iter().rev().take(width / 2) { + write!(f, "{:02x}", *ch)?; + } + if width < 2 * data.len() && width % 2 == 1 { + write!(f, "{:x}", data[data.len() - 1 - width / 2] / 16)?; } Ok(()) } impl ToHex for [u8] { fn to_hex(&self) -> String { + use std::fmt::Write; let mut ret = String::with_capacity(2 * self.len()); - format_hex(self, &mut ret).expect("format to string"); + for ch in self { + write!(ret, "{:02x}", ch).expect("writing to string"); + } ret } } @@ -213,7 +230,9 @@ impl_fromhex_array!(512); #[cfg(test)] mod tests { - use super::{ToHex, FromHex}; + use std::fmt; + + use super::{format_hex, format_hex_reverse, ToHex, FromHex}; use Error; #[test] @@ -237,6 +256,72 @@ mod tests { assert_eq!(ser, expected); } + #[test] + fn hex_truncate() { + struct HexBytes(Vec); + impl fmt::LowerHex for HexBytes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + format_hex(&self.0, f) + } + } + + let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + assert_eq!( + format!("{:x}", bytes), + "0102030405060708090a" + ); + + for i in 0..20 { + assert_eq!( + format!("{:width$x}", bytes, width = i), + &"0102030405060708090a"[0..i] + ); + } + + assert_eq!( + format!("{:25x}", bytes), + "000000102030405060708090a" + ); + assert_eq!( + format!("{:26x}", bytes), + "0000000102030405060708090a" + ); + } + + #[test] + fn hex_truncate_rev() { + struct HexBytes(Vec); + impl fmt::LowerHex for HexBytes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + format_hex_reverse(&self.0, f) + } + } + + let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + assert_eq!( + format!("{:x}", bytes), + "0a090807060504030201" + ); + + for i in 0..20 { + assert_eq!( + format!("{:width$x}", bytes, width = i), + &"0a090807060504030201"[0..i] + ); + } + + assert_eq!( + format!("{:25x}", bytes), + "000000a090807060504030201" + ); + assert_eq!( + format!("{:26x}", bytes), + "0000000a090807060504030201" + ); + } + #[test] fn hex_error() { let oddlen = "0123456789abcdef0"; From 012d775edceb1520c6fc605fec2cf7fdc9cc0e6d Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 1 May 2019 16:23:29 +0000 Subject: [PATCH 2/2] hex: use `precision` to set maximum width; use `width` for minimum --- src/hex.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hex.rs b/src/hex.rs index b014ad3..28d07d1 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -136,15 +136,16 @@ impl<'a> ExactSizeIterator for HexIterator<'a> { /// Output hex into an object implementing `fmt::Write`, which is usually more /// efficient than going through a `String` using `ToHex`. pub fn format_hex(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + let prec = f.precision().unwrap_or(2 * data.len()); let width = f.width().unwrap_or(2 * data.len()); for _ in (2 * data.len())..width { f.write_str("0")?; } - for ch in data.into_iter().take(width / 2) { + for ch in data.into_iter().take(prec / 2) { write!(f, "{:02x}", *ch)?; } - if width < 2 * data.len() && width % 2 == 1 { - write!(f, "{:x}", data[width / 2] / 16)?; + if prec < 2 * data.len() && prec % 2 == 1 { + write!(f, "{:x}", data[prec / 2] / 16)?; } Ok(()) } @@ -152,15 +153,16 @@ pub fn format_hex(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { /// Output hex in reverse order; used for Sha256dHash whose standard hex encoding /// has the bytes reversed. pub fn format_hex_reverse(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + let prec = f.precision().unwrap_or(2 * data.len()); let width = f.width().unwrap_or(2 * data.len()); for _ in (2 * data.len())..width { f.write_str("0")?; } - for ch in data.iter().rev().take(width / 2) { + for ch in data.iter().rev().take(prec / 2) { write!(f, "{:02x}", *ch)?; } - if width < 2 * data.len() && width % 2 == 1 { - write!(f, "{:x}", data[data.len() - 1 - width / 2] / 16)?; + if prec < 2 * data.len() && prec % 2 == 1 { + write!(f, "{:x}", data[data.len() - 1 - prec / 2] / 16)?; } Ok(()) } @@ -274,7 +276,7 @@ mod tests { for i in 0..20 { assert_eq!( - format!("{:width$x}", bytes, width = i), + format!("{:.prec$x}", bytes, prec = i), &"0102030405060708090a"[0..i] ); } @@ -307,7 +309,7 @@ mod tests { for i in 0..20 { assert_eq!( - format!("{:width$x}", bytes, width = i), + format!("{:.prec$x}", bytes, prec = i), &"0a090807060504030201"[0..i] ); }