From c4bbfd446734bd450c42921c1b6c9fb363a418f3 Mon Sep 17 00:00:00 2001 From: Ahmed <> Date: Sat, 21 Sep 2024 23:39:00 +0200 Subject: [PATCH 1/2] Refactor env contains into separate api Signed-off-by: Ahmed <> --- env/src/environment.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/env/src/environment.rs b/env/src/environment.rs index a81f76a..4153fb1 100644 --- a/env/src/environment.rs +++ b/env/src/environment.rs @@ -46,7 +46,10 @@ impl Environment { } true } - + #[must_use] + pub fn contains(&self, key: &str) -> bool { + self.data.contains_key(key) + } pub fn add_str_with_cb( &mut self, key: &str, @@ -55,7 +58,7 @@ impl Environment { data: &mut T, cb: StrFn, ) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvStr { @@ -73,7 +76,7 @@ impl Environment { } pub fn add_str(&mut self, key: &str, val: &str, help: &str) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvStr { @@ -140,7 +143,7 @@ impl Environment { data: &mut T, cb: U64Fn, ) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvU64 { @@ -158,7 +161,7 @@ impl Environment { } pub fn add_u64(&mut self, key: &str, val: u64, help: &str) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvU64 { @@ -225,7 +228,7 @@ impl Environment { data: &mut T, cb: I64Fn, ) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvI64 { @@ -243,7 +246,7 @@ impl Environment { } pub fn add_i64(&mut self, key: &str, val: i64, help: &str) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvI64 { @@ -310,7 +313,7 @@ impl Environment { data: &mut T, cb: BoolFn, ) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvBool { @@ -328,7 +331,7 @@ impl Environment { } pub fn add_bool(&mut self, key: &str, val: bool, help: &str) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvBool { @@ -395,7 +398,7 @@ impl Environment { data: &mut T, cb: ColorFn, ) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvColor { @@ -413,7 +416,7 @@ impl Environment { } pub fn add_color(&mut self, key: &str, val: (u8, u8, u8), help: &str) -> Result<(), EnvErr> { - if self.data.contains_key(key) { + if self.contains(key) { return Err(EnvErr::AlreadyExist); } let meta = EnvColor { From 8a91eaa60095b5518a51e6d2cc234bf51ad6dcdb Mon Sep 17 00:00:00 2001 From: Ahmed <> Date: Fri, 20 Sep 2024 22:19:12 +0200 Subject: [PATCH 2/2] Initial support for hex diff Signed-off-by: Ahmed <> --- core/src/core.rs | 2 + core/src/diff/hexdiff/mod.rs | 171 +++++++++++ core/src/diff/hexdiff/test.rs | 542 ++++++++++++++++++++++++++++++++++ core/src/diff/mod.rs | 8 + core/src/hex/hex_env.rs | 121 ++++++-- core/src/lib.rs | 2 + 6 files changed, 821 insertions(+), 25 deletions(-) create mode 100644 core/src/diff/hexdiff/mod.rs create mode 100644 core/src/diff/hexdiff/test.rs create mode 100644 core/src/diff/mod.rs diff --git a/core/src/core.rs b/core/src/core.rs index b47900d..0c08535 100644 --- a/core/src/core.rs +++ b/core/src/core.rs @@ -5,6 +5,7 @@ use crate::commands::Commands; use crate::helper::{error_msg, AddrMode}; use crate::io::register_io; use crate::loc::register_loc; +use crate::register_diff; use crate::utils::register_utils; use crate::writer::Writer; use alloc::{collections::BTreeMap, sync::Arc}; @@ -60,6 +61,7 @@ impl Core { register_io(self); register_loc(self); register_utils(self); + register_diff(self); } /// Returns list of all available commands in [Core]. pub fn commands(&mut self) -> Arc> { diff --git a/core/src/diff/hexdiff/mod.rs b/core/src/diff/hexdiff/mod.rs new file mode 100644 index 0000000..03bb01b --- /dev/null +++ b/core/src/diff/hexdiff/mod.rs @@ -0,0 +1,171 @@ +use crate::{error_msg, expect_range, hex::HexWithoutEnv, str_to_num, Cmd, Core, Writer}; +use core::cmp::min; +use std::io::Write; + +pub struct HexDiff { + inner: HexWithoutEnv, +} + +impl HexDiff { + pub fn new(core: &mut Core) -> Self { + Self { + inner: HexWithoutEnv::new(core), + } + } + fn parse_args(core: &mut Core, args: &[String]) -> Option<(u64, u64, u64)> { + let args: Vec<_> = args.iter().map(|s| str_to_num(s)).collect(); + if args.len() == 2 { + let addr1 = core.get_loc(); + let addr2 = match &args[0] { + Ok(addr) => *addr, + Err(e) => { + let err_str = format!("{e}"); + error_msg(core, "Failed to parse addr", &err_str); + return None; + } + }; + let size = match &args[1] { + Ok(size) => *size, + Err(e) => { + let err_str = format!("{e}"); + error_msg(core, "Failed to parse size", &err_str); + return None; + } + }; + Some((addr1, addr2, size)) + } else { + let addr1 = match &args[0] { + Ok(addr) => *addr, + Err(e) => { + let err_str = format!("{e}"); + error_msg(core, "Failed to parse addr1", &err_str); + return None; + } + }; + let addr2 = match &args[1] { + Ok(addr) => *addr, + Err(e) => { + let err_str = format!("{e}"); + error_msg(core, "Failed to parse addr2", &err_str); + return None; + } + }; + let size = match &args[2] { + Ok(size) => *size, + Err(e) => { + let err_str = format!("{e}"); + error_msg(core, "Failed to parse size", &err_str); + return None; + } + }; + Some((addr1, addr2, size)) + } + } + + //print enough spaces to padd + pub fn hex_space(counter: usize) -> String { + let mut s = " ".to_owned(); + for i in counter..16 { + s.push_str(" "); + if i % 2 != 0 { + s.push(' '); + } + } + s + } + + pub fn ascii_space(counter: usize) -> String { + let mut s = String::new(); + for _ in counter..16 { + s.push(' '); + } + s + } +} + +impl Cmd for HexDiff { + fn commands(&self) -> &'static [&'static str] { + &["hd", "hexDiff"] + } + + fn help_messages(&self) -> &'static [(&'static str, &'static str)] { + &[ + ( + "[addr] [size]", + "\tPrint binary diff between current location and [addr] for [size] bytes.", + ), + ( + "[addr1] [addr2] [size]", + "Print binary diff between [addr1] and [addr2] for [size] bytes.", + ), + ] + } + + fn run(&mut self, core: &mut Core, args: &[String]) { + if !(2..4).contains(&args.len()) { + expect_range(core, args.len() as u64, 2, 3); + return; + } + let Some((addr1, addr2, size)) = Self::parse_args(core, args) else { + return; + }; + if size == 0 { + return; + } + let data1 = match core.read_sparce(addr1, size) { + Ok(d) => d, + Err(e) => return error_msg(core, "Read Failed", &e.to_string()), + }; + let data2 = match core.read_sparce(addr2, size) { + Ok(d) => d, + Err(e) => return error_msg(core, "Read Failed", &e.to_string()), + }; + let env = self.inner.get_env(core); + env.print_double_banner(&mut core.stdout); + for i in (0..size).step_by(16) { + let mut ascii1 = Writer::new_buf(); + let mut hex1 = Writer::new_buf(); + let mut ascii2 = Writer::new_buf(); + let mut hex2 = Writer::new_buf(); + + for j in i..min(i + 16, size) { + let byte1 = data1.get(&(j + addr1)).copied(); + let byte2 = data2.get(&(j + addr2)).copied(); + env.print_hex_with_highlight(byte1, &mut hex1, j % 2 != 0, byte1 != byte2); + env.print_ascii_with_highlight(byte1, &mut ascii1, byte1 != byte2); + env.print_hex_with_highlight(byte2, &mut hex2, j % 2 != 0, byte1 != byte2); + env.print_ascii_with_highlight(byte2, &mut ascii2, byte1 != byte2); + } + env.print_addr(&mut core.stdout, addr1); + let hex_space = if i + 16 < size || size % 16 == 0 { + " ".to_owned() + } else { + Self::hex_space(size as usize % 16) + }; + let ascii_space = if i + 16 < size || size % 16 == 0 { + String::new() + } else { + Self::ascii_space(size as usize % 16) + }; + write!( + core.stdout, + "{}{hex_space}{}{ascii_space}", + hex1.utf8_string().unwrap(), + ascii1.utf8_string().unwrap(), + ) + .unwrap(); + env.print_separator(&mut core.stdout); + env.print_addr(&mut core.stdout, addr2); + writeln!( + core.stdout, + "{}{hex_space}{}", + hex2.utf8_string().unwrap(), + ascii2.utf8_string().unwrap(), + ) + .unwrap(); + } + } +} + +#[cfg(test)] +mod test; diff --git a/core/src/diff/hexdiff/test.rs b/core/src/diff/hexdiff/test.rs new file mode 100644 index 0000000..f09cb16 --- /dev/null +++ b/core/src/diff/hexdiff/test.rs @@ -0,0 +1,542 @@ +use std::path::Path; + +use rair_io::IoMode; +use test_file::{operate_on_file, DATA}; + +use crate::{diff::hexdiff::HexDiff, CmdOps, Core, Writer}; + +#[test] +fn test_help() { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + let hd = HexDiff::new(&mut core); + hd.help(&mut core); + assert_eq!( + core.stdout.utf8_string().unwrap(), + "Commands: [hexDiff | hd]\n\ + Usage:\n\ + hd [addr] [size]\t\tPrint binary diff between current location and [addr] for [size] bytes.\n\ + hd [addr1] [addr2] [size]\tPrint binary diff between [addr1] and [addr2] for [size] bytes.\n" + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); +} + +#[test] +fn test_hd_0_args() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run("hd", &[]); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Arguments Error: Expected between 2 and 3 arguments, found 0.\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_1_args() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run("hd", &["0x5".to_owned()]); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Arguments Error: Expected between 2 and 3 arguments, found 1.\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_2_args() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run("hd", &["0x5".to_owned(), "0x25".to_owned()]); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ + 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb ││ 0x00000005 0508 0d15 2237 5990 e979 62db 3d18 556d ....\"7Y..yb.=.Um\n\ + 0x00000000 db3d 1855 6dc2 2ff1 2011 3142 73b5 28dd .=.Um./...1Bs.(. ││ 0x00000005 c22f f120 1131 4273 b528 dd05 e2e7 c9b0 ./...1Bs.(......\n\ + 0x00000000 05e2 e7c9 b0 ..... ││ 0x00000005 7929 a2cb 6d y)..m\n"); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_4_args() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &[ + "0x5".to_owned(), + "0x5".to_owned(), + "0x5".to_owned(), + "0x5".to_owned(), + ], + ); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Arguments Error: Expected between 2 and 3 arguments, found 4.\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_2_args_1_bad() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run("hd", &["alex".to_owned(), "0x25".to_owned()]); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Error: Failed to parse addr\ninvalid digit found in string\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_2_args_2_bad() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run("hd", &["0x5".to_owned(), "alex".to_owned()]); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Error: Failed to parse size\ninvalid digit found in string\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_3_args_1_bad() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["alex".to_owned(), "0x25".to_owned(), "0x25".to_owned()], + ); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Error: Failed to parse addr1\ninvalid digit found in string\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_3_args_2_bad() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x5".to_owned(), "alex".to_owned(), "0x25".to_owned()], + ); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Error: Failed to parse addr2\ninvalid digit found in string\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_3_args_3_bad() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x5".to_owned(), "0x25".to_owned(), "alex".to_owned()], + ); + assert_eq!( + core.stderr.utf8_string().unwrap(), + "Error: Failed to parse size\ninvalid digit found in string\n" + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_0() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x0".to_owned()], + ); + assert_eq!(core.stdout.utf8_string().unwrap(), ""); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_1() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x1".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 00 . ││ 0x00000000 00 .\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_2() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x2".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 .. ││ 0x00000000 0001 ..\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_3() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x3".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 01 ... ││ 0x00000000 0001 01 ...\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_4() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x4".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 .... ││ 0x00000000 0001 0102 ....\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_5() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x5".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 03 ..... ││ 0x00000000 0001 0102 03 .....\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_6() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x6".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 ...... ││ 0x00000000 0001 0102 0305 ......\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_7() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x7".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 08 ....... ││ 0x00000000 0001 0102 0305 08 .......\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_8() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x8".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d ........ ││ 0x00000000 0001 0102 0305 080d ........\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_9() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x9".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 15 ......... ││ 0x00000000 0001 0102 0305 080d 15 .........\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_a() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xa".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 1522 .........\" ││ 0x00000000 0001 0102 0305 080d 1522 .........\"\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_b() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xb".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 1522 37 .........\"7 ││ 0x00000000 0001 0102 0305 080d 1522 37 .........\"7\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_c() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xc".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 1522 3759 .........\"7Y ││ 0x00000000 0001 0102 0305 080d 1522 3759 .........\"7Y\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_d() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xd".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 1522 3759 90 .........\"7Y. ││ 0x00000000 0001 0102 0305 080d 1522 3759 90 .........\"7Y.\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_e() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xe".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ +0x00000000 0001 0102 0305 080d 1522 3759 90e9 .........\"7Y.. ││ 0x00000000 0001 0102 0305 080d 1522 3759 90e9 .........\"7Y..\n"); + } + operate_on_file(&test_hd_cb, DATA); +} +#[test] +fn test_hd_f() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0xf".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ + 0x00000000 0001 0102 0305 080d 1522 3759 90e9 79 .........\"7Y..y ││ 0x00000000 0001 0102 0305 080d 1522 3759 90e9 79 .........\"7Y..y\n"); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_10() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x10".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ + 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb ││ 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb\n"); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_11() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "0x11".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ + 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb ││ 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb\n\ + 0x00000000 db . ││ 0x00000000 db .\n"); + } + operate_on_file(&test_hd_cb, DATA); +} + +#[test] +fn test_hd_100() { + fn test_hd_cb(path: &Path) { + let mut core = Core::new_no_colors(); + core.stderr = Writer::new_buf(); + core.stdout = Writer::new_buf(); + core.io.open(&path.to_string_lossy(), IoMode::READ).unwrap(); + core.run( + "hd", + &["0x0".to_owned(), "0x0".to_owned(), "100".to_owned()], + ); + assert_eq!(core.stderr.utf8_string().unwrap(), ""); + assert_eq!(core.stdout.utf8_string().unwrap(), +"- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF ││ - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n\ + 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb ││ 0x00000000 0001 0102 0305 080d 1522 3759 90e9 7962 .........\"7Y..yb\n\ + 0x00000000 db3d 1855 6dc2 2ff1 2011 3142 73b5 28dd .=.Um./...1Bs.(. ││ 0x00000000 db3d 1855 6dc2 2ff1 2011 3142 73b5 28dd .=.Um./...1Bs.(.\n\ + 0x00000000 05e2 e7c9 b079 29a2 cb6d 38a5 dd82 5fe1 .....y)..m8..._. ││ 0x00000000 05e2 e7c9 b079 29a2 cb6d 38a5 dd82 5fe1 .....y)..m8..._.\n\ + 0x00000000 4021 6182 e365 48ad f5a2 9739 d009 d9e2 @!a..eH....9.... ││ 0x00000000 4021 6182 e365 48ad f5a2 9739 d009 d9e2 @!a..eH....9....\n\ + 0x00000000 bb9d 58f5 4d42 8fd1 6031 91c2 5315 687d ..X.MB..`1..S.h} ││ 0x00000000 bb9d 58f5 4d42 8fd1 6031 91c2 5315 687d ..X.MB..`1..S.h}\n\ + 0x00000000 e562 47a9 f099 8922 abcd 7845 bd02 bfc1 .bG....\"..xE.... ││ 0x00000000 e562 47a9 f099 8922 abcd 7845 bd02 bfc1 .bG....\"..xE....\n\ + 0x00000000 8041 c102 .A.. ││ 0x00000000 8041 c102 .A..\n"); + } + operate_on_file(&test_hd_cb, DATA); +} diff --git a/core/src/diff/mod.rs b/core/src/diff/mod.rs new file mode 100644 index 0000000..23a3826 --- /dev/null +++ b/core/src/diff/mod.rs @@ -0,0 +1,8 @@ +mod hexdiff; + +use crate::Core; + +pub fn register_diff(core: &mut Core) { + let hexdiff = hexdiff::HexDiff::new(core); + core.add_command(hexdiff); +} diff --git a/core/src/hex/hex_env.rs b/core/src/hex/hex_env.rs index 639e776..9ee320c 100644 --- a/core/src/hex/hex_env.rs +++ b/core/src/hex/hex_env.rs @@ -10,17 +10,22 @@ pub struct HexEnv { // color for the ascii part that // is not printable pub na: (u8, u8, u8), + // color for highlighting + pub highlight: (u8, u8, u8), // character to print in case of gap pub gap: char, // character to print (with na color) pub noprint: char, + // separator between side by side views + pub separator: String, } impl HexEnv { pub(super) fn new(core: &mut Core) -> Self { - let env = core.env.clone(); - env.write() - .add_str_with_cb( + let env_lock = core.env.clone(); + let mut env = env_lock.write(); + if !env.contains("hex.headerColor") { + env.add_str_with_cb( "hex.headerColor", "color.6", "Color used in the header of when using commands working with hex data", @@ -28,26 +33,27 @@ impl HexEnv { is_color, ) .unwrap(); - env.write() - .add_str_with_cb( + } + if !env.contains("hex.nonPrintColor") { + env.add_str_with_cb( "hex.nonPrintColor", "color.5", "Color used in the Ascii section for non printable ASCII when using commands that work with hex data", core, is_color, - ) - .unwrap(); - env.write() - .add_str_with_cb( + ).unwrap(); + } + if !env.contains("hex.nonPrintReplace") { + env.add_str_with_cb( "hex.nonPrintReplace", ".", "Text used in the Ascii section to replace non printable ASCII when using when using commands that work with hex data", core, one_byte, - ) - .unwrap(); - env.write() - .add_str_with_cb( + ).unwrap(); + } + if !env.contains("hex.gapReplace") { + env.add_str_with_cb( "hex.gapReplace", "#", "Text used to replace gaps when using when using commands that work with hex data", @@ -55,12 +61,32 @@ impl HexEnv { one_byte, ) .unwrap(); - + } + if !env.contains("hex.separator") { + env.add_str( + "hex.separator", + "││", + "Separator between the two side by side views", + ) + .unwrap(); + } + if !env.contains("hex.highlight") { + env.add_str_with_cb( + "hex.highlight", + "color.1", + "Color used to highlight different sections when needed", + core, + is_color, + ) + .unwrap(); + } Self { banner: (0, 0, 0), na: (0, 0, 0), + highlight: (0, 0, 0), gap: char::default(), noprint: char::default(), + separator: String::new(), } } pub(super) fn get_env(&mut self, core: &mut Core) -> &Self { @@ -69,6 +95,8 @@ impl HexEnv { self.banner = env.get_color(color).unwrap(); let color = env.get_str("hex.nonPrintColor").unwrap(); self.na = core.env.read().get_color(color).unwrap(); + let color = env.get_str("hex.highlight").unwrap(); + self.highlight = core.env.read().get_color(color).unwrap(); self.gap = env .get_str("hex.gapReplace") .unwrap() @@ -81,6 +109,9 @@ impl HexEnv { .chars() .next() .unwrap(); + env.get_str("hex.separator") + .unwrap() + .clone_into(&mut self.separator); self } pub fn print_banner_with_newline(&self, writer: &mut Writer, newline: bool) { @@ -99,32 +130,72 @@ impl HexEnv { pub fn print_banner(&self, writer: &mut Writer) { self.print_banner_with_newline(writer, true); } + pub fn print_double_banner(&self, writer: &mut Writer) { + self.print_banner_with_newline(writer, false); + self.print_separator(writer); + self.print_banner_with_newline(writer, true); + } pub fn print_addr(&self, writer: &mut Writer, loc: u64) { let loc = format!("0x{loc:08x}"); let (r, g, b) = self.banner; let loc_colored = loc.rgb(r, g, b); write!(writer, "{loc_colored} ").unwrap(); } - // print hex data all while taking care of extra white space - pub fn print_hex(&self, data: Option, writer: &mut Writer, space_after: bool) { + pub fn print_hex_with_highlight( + &self, + data: Option, + writer: &mut Writer, + space_after: bool, + highlight: bool, + ) { let space = if space_after { " " } else { "" }; - if let Some(c) = data { - write!(writer, "{c:02x}{space}").unwrap(); + let hex: String = if let Some(c) = data { + format!("{c:02x}") + } else { + format!("{}{}", self.gap, self.gap) + }; + if highlight { + let (r, g, b) = self.highlight; + write!(writer, "{}{space}", hex.on_rgb(r, g, b)).unwrap(); } else { - write!(writer, "{}{}{space}", self.gap, self.gap).unwrap(); + write!(writer, "{hex}{space}").unwrap(); } } - // print ascii data while taking care of non printable characters and coloring - pub fn print_ascii(&self, data: Option, writer: &mut Writer) { + + // print hex data all while taking care of extra white space + pub fn print_hex(&self, data: Option, writer: &mut Writer, space_after: bool) { + self.print_hex_with_highlight(data, writer, space_after, false); + } + pub fn print_ascii_with_highlight( + &self, + data: Option, + writer: &mut Writer, + highlight: bool, + ) { let (r, g, b) = self.na; - if let Some(c) = data { + let ascii = if let Some(c) = data { if (0x21..=0x7E).contains(&c) { - write!(writer, "{}", c as char).unwrap(); + format!("{}", c as char) } else { - write!(writer, "{}", self.noprint.rgb(r, g, b)).unwrap(); + format!("{}", self.noprint.rgb(r, g, b)) } } else { - write!(writer, "{}", self.gap.rgb(r, g, b)).unwrap(); + format!("{}", self.gap.rgb(r, g, b)) + }; + if highlight { + let (r, g, b) = self.highlight; + write!(writer, "{}", ascii.on_rgb(r, g, b)).unwrap(); + } else { + write!(writer, "{ascii}").unwrap(); } } + + // print ascii data while taking care of non printable characters and coloring + pub fn print_ascii(&self, data: Option, writer: &mut Writer) { + self.print_ascii_with_highlight(data, writer, false); + } + pub fn print_separator(&self, writer: &mut Writer) { + let (r, g, b) = self.banner; + write!(writer, " {} ", self.separator.rgb(r, g, b)).unwrap(); + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index a0d4c23..6f06624 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,6 +4,7 @@ extern crate alloc; mod cmd; mod commands; mod core; +mod diff; mod helper; mod hex; mod io; @@ -13,6 +14,7 @@ mod writer; pub use self::cmd::*; pub use self::commands::*; pub use self::core::*; +pub use self::diff::*; pub use self::helper::*; pub use self::io::*; pub use self::writer::*;