From 70816f31f7a9e4d4fdeed043d312a9a62c17bab0 Mon Sep 17 00:00:00 2001 From: Avril Date: Wed, 30 Sep 2020 19:43:07 +0100 Subject: [PATCH] hex view --- src/ext.rs | 158 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/src/ext.rs b/src/ext.rs index 25a1fa4..bb5ede4 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -7,6 +7,9 @@ use std::{ ops::{ Range, }, + marker::{ + PhantomData, + }, }; use tokio::{ io::AsyncRead, @@ -131,12 +134,105 @@ const ASCII_MAP: [char; 256] = [ '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', ]; -pub struct HexStringIter<'a, I>(&'a I, bool); -pub struct HexView<'a, I>(&'a I); +const fn create_hex_map() -> [(u8, u8); 256] +{ + let mut out = [(0, 0); 256]; + const HEX: &[u8; 16] = b"0123456789abcdef"; + let mut i = 0usize; + while i <= 255 + { + out[i] = ( + HEX[i >> 4], + HEX[i & 0xf] + ); + i+=1; + } + out +} +const HEX_MAP: [(u8, u8); 256] = create_hex_map(); + +pub struct HexStringView<'a, I:?Sized>(&'a I, bool); +pub struct HexStringIter<'a, I:?Sized>(std::slice::Iter<'a, u8>, (u8, u8), PhantomData<&'a I>); + +pub struct HexView<'a, I:?Sized>(&'a I); + +pub struct AsciiView<'a, I:?Sized>(&'a I); +pub struct AsciiIter<'a, I:?Sized>(&'a [u8], PhantomData<&'a I>); const SPLIT_EVERY: usize = 16; -impl<'a, I: AsRef<[u8]>> fmt::Display for HexView<'a, I> +impl<'a, I: ?Sized+AsRef<[u8]>> Iterator for AsciiIter<'a, I> +{ + type Item = char; + fn next(&mut self) -> Option + { + match match self.0 { + [] => None, + [chr, ..] => Some(ASCII_MAP[*chr as usize]), + } { + x @ Some(_) => { + self.0 = &self.0[1..]; + x + }, + _ => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.0.len(), Some(self.0.len())) + } +} +impl<'a, I: ?Sized+AsRef<[u8]>> ExactSizeIterator for AsciiIter<'a, I>{} +impl<'a, I: ?Sized+AsRef<[u8]>> std::iter::FusedIterator for AsciiIter<'a, I>{} + +impl<'a, I: ?Sized+AsRef<[u8]>> Iterator for HexStringIter<'a, I> +{ + type Item = char; + fn next(&mut self) -> Option + { + match self.1 { + ref mut buf @ (0, 0) => { + // both are taken + if let Some(&byte) = self.0.next() { + *buf = HEX_MAP[byte as usize]; + } else { + return None; + } + (Some(buf.0 as char),buf.0 = 0).0 + }, + (0, ref mut second) => { + // first is taken + (Some(*second as char),*second = 0).0 + }, + #[cold] (ref mut first, _) => { + // neither are taken, usually shouldn't happen + (Some(*first as char),*first = 0).0 + }, + } + } + + fn size_hint(&self) -> (usize, Option) { + let sz = self.0.size_hint(); + (sz.0 * 2, sz.1.map(|x| x*2)) + } +} +impl<'a, I: ?Sized+AsRef<[u8]>> ExactSizeIterator for HexStringIter<'a, I>{} +impl<'a, I: ?Sized+AsRef<[u8]>> std::iter::FusedIterator for HexStringIter<'a, I>{} + +impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for AsciiView<'a, I> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + for byte in self.0.as_ref().iter().map(|&byte| ASCII_MAP[byte as usize]) + { + use std::fmt::Write; + f.write_char(byte)?; + } + Ok(()) + } +} + +impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for HexView<'a, I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -178,7 +274,7 @@ impl<'a, I: AsRef<[u8]>> fmt::Display for HexView<'a, I> } } -impl<'a, I: AsRef<[u8]>> fmt::Display for HexStringIter<'a, I> +impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for HexStringView<'a, I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -202,10 +298,26 @@ impl<'a, I: AsRef<[u8]>> fmt::Display for HexStringIter<'a, I> } } -pub trait HexStringExt: Sized + AsRef<[u8]> +/// Extensions on byte slices to print them nicely +pub trait HexStringExt: AsRef<[u8]> { + /// An iterator that prints readable ascii of each byte + fn iter_ascii(&self) -> AsciiIter<'_, Self>; + /// A `Display` implementor that prints ascii of each byte + fn fmt_ascii(&self) -> AsciiView<'_, Self>; + + /// A pretty hex view `Display` implementor of the bytes fn fmt_view(&self) -> HexView<'_, Self>; - fn fmt_hex(&self) -> HexStringIter<'_, Self>; + + /// A `Display` implementor that prints the hex of each byte in lowercase + fn fmt_hex(&self) -> HexStringView<'_, Self>; + /// An iterator over `char`s that yields the hex of each byte + /// + /// # Notes + /// This yields each character one at a time, to get the hex of each byte, chunk it with a window of 2. + fn iter_hex(&self) -> HexStringIter<'_, Self>; + + /// Convenience method for creating a hex string. fn to_hex_string(&self) -> String { let mut string = String::with_capacity(self.as_ref().len()*2); @@ -213,9 +325,11 @@ pub trait HexStringExt: Sized + AsRef<[u8]> write!(&mut string, "{}", self.fmt_hex()).unwrap(); string } + + /// Convenience method for creating a hex string with each byte broken by a hyphen. fn to_broken_hex_string(&self) -> String { - let fmt = HexStringIter( + let fmt = HexStringView( self.fmt_hex().0, true ); @@ -224,13 +338,37 @@ pub trait HexStringExt: Sized + AsRef<[u8]> write!(&mut string, "{}", fmt).unwrap(); string } + /// Convenience method for creating a string from `fmt_view()` + #[inline] fn to_view_string(&self) -> String + { + format!("{}", self.fmt_view()) + } + + /// Convenience method for creating a string from `fmt_ascii()` + + #[inline] fn to_ascii_string(&self) -> String + { + self.iter_ascii().collect() + } } -impl> HexStringExt for T +impl+?Sized> HexStringExt for T { - fn fmt_hex(&self) -> HexStringIter<'_, Self> + fn iter_hex(&self) -> HexStringIter<'_, Self> + { + HexStringIter(self.as_ref().iter(), (0,0), PhantomData) + } + fn iter_ascii(&self) -> AsciiIter<'_, Self> + { + AsciiIter(self.as_ref(), PhantomData) + } + fn fmt_ascii(&self) -> AsciiView<'_, Self> + { + AsciiView(&self) + } + fn fmt_hex(&self) -> HexStringView<'_, Self> { - HexStringIter(&self, false) + HexStringView(&self, false) } fn fmt_view(&self) -> HexView<'_, Self>