pretty-hex-0.4.1/.cargo_vcs_info.json0000644000000001360000000000100131470ustar { "git": { "sha1": "2552b74d95899c93e298e783c3ed351689d1414a" }, "path_in_vcs": "" }pretty-hex-0.4.1/.github/dependabot.yml000064400000000000000000000003171046102023000161300ustar 00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" - package-ecosystem: "cargo" directory: "/" schedule: interval: "weekly" pretty-hex-0.4.1/.github/workflows/rust.yml000064400000000000000000000007751046102023000170650ustar 00000000000000name: Build and test on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build default run: cargo build --verbose - name: Build without alloc run: cargo build --no-default-features --verbose - name: Test default run: cargo test --verbose - name: Test without alloc run: cargo test --no-default-features --verbose pretty-hex-0.4.1/.gitignore000064400000000000000000000000451046102023000137260ustar 00000000000000/target/ **/*.rs.bk Cargo.lock /.ideapretty-hex-0.4.1/Cargo.toml0000644000000020370000000000100111470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "pretty-hex" version = "0.4.1" authors = ["Andrei Volnin "] description = "Pretty hex dump of bytes slice in the common style." homepage = "https://github.com/wolandr/pretty-hex" documentation = "https://docs.rs/pretty-hex" readme = "README.md" keywords = [ "logging", "hex", "dump", "binary", ] categories = [ "development-tools::debugging", "no-std", ] license = "MIT" repository = "https://github.com/wolandr/pretty-hex" [dependencies] [dev-dependencies.heapless] version = "0.8" [features] alloc = [] default = ["alloc"] pretty-hex-0.4.1/Cargo.toml.orig000064400000000000000000000010461046102023000146270ustar 00000000000000[package] name = "pretty-hex" version = "0.4.1" authors = ["Andrei Volnin "] license = "MIT" description = "Pretty hex dump of bytes slice in the common style." readme = "README.md" homepage = "https://github.com/wolandr/pretty-hex" repository = "https://github.com/wolandr/pretty-hex" documentation = "https://docs.rs/pretty-hex" categories = ["development-tools::debugging", "no-std"] keywords = ["logging", "hex", "dump", "binary"] [dependencies] [dev-dependencies] heapless = "0.8" [features] default = ["alloc"] alloc = [] pretty-hex-0.4.1/LICENSE000064400000000000000000000020561046102023000127470ustar 00000000000000MIT License Copyright (c) 2018 Andrei Volnin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pretty-hex-0.4.1/README.md000064400000000000000000000031641046102023000132220ustar 00000000000000# pretty-hex [![crates.io](https://img.shields.io/crates/v/pretty-hex.svg)](https://crates.io/crates/pretty-hex) [![docs.rs](https://docs.rs/pretty-hex/badge.svg)](https://docs.rs/pretty-hex) A Rust library providing pretty hex dump. A `simple_hex()` way renders one-line hex dump, a `pretty_hex()` way renders columned multi-line hex dump with addressing and ASCII representation. A `config_hex()` way renders hex dump in specified format. ## Example of `simple_hex()` ```rust use pretty_hex::*; let v = vec![222, 173, 190, 239, 202, 254, 32, 24]; assert_eq!(simple_hex(&v), format!("{}", v.hex_dump())); println!("{}", v.hex_dump()); ``` Output: ```text de ad be ef ca fe 20 18 ``` ## Example of `pretty_hex()` ```rust use pretty_hex::*; let v: &[u8] = &random::<[u8;30]>(); assert_eq!(pretty_hex(&v), format!("{:?}", v.hex_dump())); println!("{:?}", v.hex_dump()); ``` Output: ```text Length: 30 (0x1e) bytes 0000: 6b 4e 1a c3 af 03 d2 1e 7e 73 ba c8 bd 84 0f 83 kN......~s...... 0010: 89 d5 cf 90 23 67 4b 48 db b1 bc 35 bf ee ....#gKH...5.. ``` ## Example of `config_hex()` ```rust use pretty_hex::*; let cfg = HexConfig {title: false, width: 8, group: 0, ..HexConfig::default() }; let v = &include_bytes!("data"); assert_eq!(config_hex(&v, cfg), format!("{:?}", v.hex_conf(cfg))); println!("{:?}", v.hex_conf(cfg)); ``` Output: ```text 0000: 6b 4e 1a c3 af 03 d2 1e kN...... 0008: 7e 73 ba c8 bd 84 0f 83 ~s...... 0010: 89 d5 cf 90 23 67 4b 48 ....#gKH 0018: db b1 bc 35 bf ee ...5.. ``` --- Inspired by [haskell's pretty-hex](https://hackage.haskell.org/package/pretty-hex-1.0). pretty-hex-0.4.1/src/lib.rs000064400000000000000000000033461046102023000136500ustar 00000000000000#![no_std] //! A Rust library providing pretty hex dump. //! //! A `simple_hex()` way renders one-line hex dump, and a `pretty_hex()` way renders //! columned multi-line hex dump with addressing and ASCII representation. //! A `config_hex()` way renders hex dump in specified format. //! //! ## Example of `simple_hex()` //! ``` //! use pretty_hex::*; //! //! let v = vec![222, 173, 190, 239, 202, 254, 32, 24]; //! # #[cfg(feature = "alloc")] //! assert_eq!(simple_hex(&v), format!("{}", v.hex_dump())); //! //! println!("{}", v.hex_dump()); //! ``` //! Output: //! //! ```text //! de ad be ef ca fe 20 18 //! ``` //! ## Example of `pretty_hex()` //! ``` //! use pretty_hex::*; //! //! let v = &include_bytes!("../tests/data"); //! # #[cfg(feature = "alloc")] //! assert_eq!(pretty_hex(&v), format!("{:?}", v.hex_dump())); //! //! println!("{:?}", v.hex_dump()); //! ``` //! Output: //! //! ```text //! Length: 30 (0x1e) bytes //! 0000: 6b 4e 1a c3 af 03 d2 1e 7e 73 ba c8 bd 84 0f 83 kN......~s...... //! 0010: 89 d5 cf 90 23 67 4b 48 db b1 bc 35 bf ee ....#gKH...5.. //! ``` //! ## Example of `config_hex()` //! ``` //! use pretty_hex::*; //! //! let cfg = HexConfig {title: false, width: 8, group: 0, ..HexConfig::default() }; //! //! let v = &include_bytes!("../tests/data"); //! # #[cfg(feature = "alloc")] //! assert_eq!(config_hex(&v, cfg), format!("{:?}", v.hex_conf(cfg))); //! //! println!("{:?}", v.hex_conf(cfg)); //! ``` //! Output: //! //! ```text //! 0000: 6b 4e 1a c3 af 03 d2 1e kN...... //! 0008: 7e 73 ba c8 bd 84 0f 83 ~s...... //! 0010: 89 d5 cf 90 23 67 4b 48 ....#gKH //! 0018: db b1 bc 35 bf ee ...5.. //! ``` #[cfg(feature = "alloc")] extern crate alloc; mod pretty_hex; pub use pretty_hex::*; pretty-hex-0.4.1/src/pretty_hex.rs000064400000000000000000000153721046102023000152770ustar 00000000000000#[cfg(feature = "alloc")] use alloc::string::String; use core::{default::Default, fmt}; /// Returns a one-line hexdump of `source` grouped in default format without header /// and ASCII column. #[cfg(feature = "alloc")] pub fn simple_hex>(source: &T) -> String { let mut writer = String::new(); hex_write(&mut writer, source, HexConfig::simple()).unwrap_or(()); writer } /// Dump `source` as hex octets in default format without header and ASCII column to the `writer`. pub fn simple_hex_write(writer: &mut W, source: &T) -> fmt::Result where T: AsRef<[u8]>, W: fmt::Write, { hex_write(writer, source, HexConfig::simple()) } /// Return a multi-line hexdump in default format complete with addressing, hex digits, /// and ASCII representation. #[cfg(feature = "alloc")] pub fn pretty_hex>(source: &T) -> String { let mut writer = String::new(); hex_write(&mut writer, source, HexConfig::default()).unwrap_or(()); writer } /// Write multi-line hexdump in default format complete with addressing, hex digits, /// and ASCII representation to the writer. pub fn pretty_hex_write(writer: &mut W, source: &T) -> fmt::Result where T: AsRef<[u8]>, W: fmt::Write, { hex_write(writer, source, HexConfig::default()) } /// Return a hexdump of `source` in specified format. #[cfg(feature = "alloc")] pub fn config_hex>(source: &T, cfg: HexConfig) -> String { let mut writer = String::new(); hex_write(&mut writer, source, cfg).unwrap_or(()); writer } /// Configuration parameters for hexdump. #[derive(Clone, Copy, Debug)] pub struct HexConfig { /// Write first line header with data length. pub title: bool, /// Append ASCII representation column. pub ascii: bool, /// Source bytes per row. 0 for single row without address prefix. pub width: usize, /// Chunks count per group. 0 for single group (column). pub group: usize, /// Source bytes per chunk (word). 0 for single word. pub chunk: usize, /// Maximum bytes to print. pub max_bytes: usize, /// Offset added to displayed address prefix pub display_offset: usize, } /// Default configuration with `title`, `ascii`, 16 source bytes `width` grouped to 4 separate /// hex bytes. Using in `pretty_hex`, `pretty_hex_write` and `fmt::Debug` implementation. impl Default for HexConfig { fn default() -> HexConfig { HexConfig { title: true, ascii: true, width: 16, group: 4, chunk: 1, max_bytes: usize::MAX, display_offset: 0, } } } impl HexConfig { /// Returns configuration for `simple_hex`, `simple_hex_write` and `fmt::Display` implementation. pub fn simple() -> Self { HexConfig::default().to_simple() } fn delimiter(&self, i: usize) -> &'static str { if i > 0 && self.chunk > 0 && i % self.chunk == 0 { if self.group > 0 && i % (self.group * self.chunk) == 0 { " " } else { " " } } else { "" } } fn to_simple(self) -> Self { HexConfig { title: false, ascii: false, width: 0, ..self } } } const NON_ASCII: char = '.'; type AddressWriter = dyn Fn(&mut dyn fmt::Write, usize) -> fmt::Result; fn get_address_writer(max_addr: usize) -> &'static AddressWriter{ match max_addr { 0x0000..=0xffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:04x}: ", a), 0x010000..=0xffffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:06x}: ", a), 0x01000000..=0xffffffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:08x}: ", a), _ => &|w: &mut dyn fmt::Write, a| write!(w, "{:016x}: ", a) } } /// Write hex dump in specified format. pub fn hex_write(writer: &mut W, source: &T, cfg: HexConfig) -> fmt::Result where T: AsRef<[u8]> + ?Sized, W: fmt::Write, { let mut source = source.as_ref(); if cfg.title { writeln!(writer, "Length: {0} (0x{0:x}) bytes", source.len())?; } if source.is_empty() { return Ok(()); } let omitted = source.len().checked_sub(cfg.max_bytes); if omitted.is_some() { source = &source[..cfg.max_bytes]; } let lines = source.chunks(if cfg.width > 0 { cfg.width } else { source.len() }); let lines_len = lines.len(); let max_address = if source.len() <= cfg.width { source.len() + cfg.display_offset } else { source.len() - cfg.width + cfg.display_offset }; let write_address = get_address_writer(max_address); for (i, row) in lines.enumerate() { if cfg.width > 0 { write_address(writer, i * cfg.width + cfg.display_offset)?; } for (i, x) in row.as_ref().iter().enumerate() { write!(writer, "{}{:02x}", cfg.delimiter(i), x)?; } if cfg.ascii { for j in row.len()..cfg.width { write!(writer, "{} ", cfg.delimiter(j))?; } write!(writer, " ")?; for x in row { if x.is_ascii() && !x.is_ascii_control() { writer.write_char((*x).into())?; } else { writer.write_char(NON_ASCII)?; } } } if i + 1 < lines_len { writeln!(writer)?; } } if let Some(o) = omitted { write!(writer, "\n...{0} (0x{0:x}) bytes not shown...", o)?; } Ok(()) } /// Reference wrapper for use in arguments formatting. pub struct Hex<'a, T: 'a + ?Sized>(&'a T, HexConfig); impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Display for Hex<'a, T> { /// Formats the value by `simple_hex_write` using the given formatter. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { hex_write(f, self.0, self.1.to_simple()) } } impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Debug for Hex<'a, T> { /// Formats the value by `pretty_hex_write` using the given formatter. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { hex_write(f, self.0, self.1) } } /// Allows generates hex dumps to a formatter. pub trait PrettyHex { /// Wrap self reference for use in `std::fmt::Display` and `std::fmt::Debug` /// formatting as hex dumps. fn hex_dump(&self) -> Hex; /// Wrap self reference for use in `std::fmt::Display` and `std::fmt::Debug` /// formatting as hex dumps in specified format. fn hex_conf(&self, cfg: HexConfig) -> Hex; } impl PrettyHex for T where T: AsRef<[u8]> + ?Sized, { fn hex_dump(&self) -> Hex { Hex(self, HexConfig::default()) } fn hex_conf(&self, cfg: HexConfig) -> Hex { Hex(self, cfg) } } pretty-hex-0.4.1/tests/256.txt000064400000000000000000000023711046102023000141610ustar 00000000000000Length: 256 (0x100) bytes 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................ 0010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................ 0020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./ 0030: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>? 0040: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 0050: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_ 0060: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno 0070: 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~. 0080: 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f ................ 0090: 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f ................ 00a0: a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af ................ 00b0: b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf ................ 00c0: c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf ................ 00d0: d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df ................ 00e0: e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef ................ 00f0: f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ................pretty-hex-0.4.1/tests/data000064400000000000000000000000361046102023000137340ustar 00000000000000kNï~sȽϐ#gKH۱5pretty-hex-0.4.1/tests/tests.rs000064400000000000000000000145451046102023000146220ustar 00000000000000#![no_std] #[cfg(feature = "alloc")] extern crate alloc; extern crate pretty_hex; #[cfg(feature = "alloc")] use alloc::{format, string::String, vec, vec::Vec}; use pretty_hex::*; #[cfg(feature = "alloc")] #[test] fn test_simple() { let bytes: Vec = (0..16).collect(); let expected = "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"; assert_eq!(expected, simple_hex(&bytes)); assert_eq!(expected, format!("{}", bytes.hex_dump())); assert_eq!(simple_hex(&bytes), config_hex(&bytes, HexConfig::simple())); let mut have = String::new(); simple_hex_write(&mut have, &bytes).unwrap(); assert_eq!(expected, have); let str = "string"; let string: String = String::from("string"); let slice: &[u8] = &[0x73, 0x74, 0x72, 0x69, 0x6e, 0x67]; assert_eq!(simple_hex(&str), "73 74 72 69 6e 67"); assert_eq!(simple_hex(&str), simple_hex(&string)); assert_eq!(simple_hex(&str), simple_hex(&slice)); assert!(simple_hex(&vec![]).is_empty()); } #[cfg(feature = "alloc")] #[test] fn test_pretty() { let bytes: Vec = (0..256).map(|x| x as u8).collect(); let want = include_str!("256.txt"); let mut hex = String::new(); pretty_hex_write(&mut hex, &bytes).unwrap(); assert_eq!(want, hex); assert_eq!(want, format!("{:?}", bytes.hex_dump())); assert_eq!(want, pretty_hex(&bytes)); assert_eq!(want, config_hex(&bytes, HexConfig::default())); assert_eq!("Length: 0 (0x0) bytes\n", pretty_hex(&vec![])); } #[cfg(feature = "alloc")] #[test] fn test_config() { let cfg = HexConfig { title: false, ascii: false, width: 0, group: 0, chunk: 0, max_bytes: usize::MAX, display_offset: 0 }; assert!(config_hex(&vec![], cfg).is_empty()); assert_eq!("2425262728", config_hex(&"$%&'(", cfg)); let v = include_bytes!("data"); let cfg = HexConfig { title: false, group: 8, ..HexConfig::default() }; let hex = "0000: 6b 4e 1a c3 af 03 d2 1e 7e 73 ba c8 bd 84 0f 83 kN......~s......\n\ 0010: 89 d5 cf 90 23 67 4b 48 db b1 bc 35 bf ee ....#gKH...5.."; assert_eq!(hex, config_hex(&v, cfg)); assert_eq!(hex, format!("{:?}", v.hex_conf(cfg))); let mut str = String::new(); hex_write(&mut str, v, cfg).unwrap(); assert_eq!(hex, str); assert_eq!( config_hex(&v, HexConfig{ascii: false, ..cfg}), "0000: 6b 4e 1a c3 af 03 d2 1e 7e 73 ba c8 bd 84 0f 83\n\ 0010: 89 d5 cf 90 23 67 4b 48 db b1 bc 35 bf ee" ); assert_eq!( config_hex(&v, HexConfig { ascii: false, group: 4, chunk: 2, ..cfg } ), "0000: 6b4e 1ac3 af03 d21e 7e73 bac8 bd84 0f83\n\ 0010: 89d5 cf90 2367 4b48 dbb1 bc35 bfee" ); assert_eq!( config_hex(&v, HexConfig { ascii: false, display_offset: 0x200, ..cfg } ), "0200: 6b 4e 1a c3 af 03 d2 1e 7e 73 ba c8 bd 84 0f 83\n\ 0210: 89 d5 cf 90 23 67 4b 48 db b1 bc 35 bf ee" ); let v: Vec = (0..21).collect(); let want = r##"Length: 21 (0x15) bytes 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................ 0010: 10 11 12 13 14 ....."##; assert_eq!(want, pretty_hex(&v)); let v: Vec = (0..13).collect(); assert_eq!( config_hex(&v, HexConfig { title: false, ascii: true, width: 11, group: 2, chunk: 3, max_bytes: usize::MAX, display_offset: 0 }), "0000: 000102 030405 060708 090a ...........\n\ 000b: 0b0c .." ); let v: Vec = (0..19).collect(); assert_eq!( config_hex(&v, HexConfig { title: false, ascii: true, width: 16, group: 3, chunk: 3, max_bytes: usize::MAX, display_offset: 0 } ), "0000: 000102 030405 060708 090a0b 0c0d0e 0f ................\n\ 0010: 101112 ..." ); let cfg = HexConfig { title: false, group: 0, ..HexConfig::default() }; assert_eq!( format!("{:?}", v.hex_conf(cfg)), "0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................\n\ 0010: 10 11 12 ..." ); assert_eq!( format!("{}", v.hex_conf(cfg)), "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12" ); let cfg = HexConfig { max_bytes: 16, ..HexConfig::default() }; assert_eq!( format!("{:?}", v.hex_conf(cfg)), "Length: 19 (0x13) bytes\n\ 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................\n\ ...3 (0x3) bytes not shown..." ); let cfg = HexConfig { max_bytes: 4, ..HexConfig::default() }; assert_eq!( format!("{:?}", v.hex_conf(cfg)), "Length: 19 (0x13) bytes\n\ 0000: 00 01 02 03 ....\n\ ...15 (0xf) bytes not shown..." ); } #[cfg(feature = "alloc")] #[test] fn test_unsized() { struct NewType([u8]); let x: &NewType = unsafe{ &*(&[] as *const [u8] as *const NewType) }; format!("{}", x.0.hex_dump()); format!("{:?}", x.0.hex_dump()); } // This test case checks that hex_write works even without the alloc crate. // Decorators to this function like simple_hex_write or PrettyHex::hex_dump() // will be tested when the alloc feature is selected because it feels quite // cumbersome to set up these tests without the comodity from `alloc`. #[test] fn test_hex_write_with_simple_config() { let config = HexConfig::simple(); let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; let expected = core::str::from_utf8(b"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f").unwrap(); let mut buffer = heapless::Vec::::new(); hex_write(&mut buffer, &bytes, config).unwrap(); let have = core::str::from_utf8(&buffer).unwrap(); assert_eq!(expected, have); }