diff --git a/src/od/formatteriteminfo.rs b/src/od/formatteriteminfo.rs index c6974a172..a6bd6f5a6 100644 --- a/src/od/formatteriteminfo.rs +++ b/src/od/formatteriteminfo.rs @@ -7,6 +7,6 @@ pub enum FormatWriter { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct FormatterItemInfo { pub byte_size: usize, - pub print_width: usize, + pub print_width: usize, // including a space in front of the text pub formatter: FormatWriter, } diff --git a/src/od/od.rs b/src/od/od.rs index ac7984dde..a7bdec8a6 100644 --- a/src/od/od.rs +++ b/src/od/od.rs @@ -43,6 +43,7 @@ macro_rules! hashmap { } static VERSION: &'static str = env!("CARGO_PKG_VERSION"); +const MAX_BYTES_PER_UNIT: usize = 8; #[derive(Debug)] enum Radix { Decimal, Hexadecimal, Octal, Binary } @@ -65,6 +66,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("b", "", "octal bytes"); opts.optflag("c", "", "ASCII characters or backslash escapes"); opts.optflag("d", "", "unsigned decimal 2-byte units"); + opts.optflag("D", "", "unsigned decimal 4-byte units"); opts.optflag("o", "", "unsigned decimal 2-byte units"); opts.optflag("I", "", "decimal 2-byte units"); @@ -164,6 +166,7 @@ pub fn uumain(args: Vec) -> i32 { "b" => FORMAT_ITEM_OCT8, "c" => FORMAT_ITEM_C, "D" => FORMAT_ITEM_DEC32U, + "d" => FORMAT_ITEM_DEC16U, "e" => FORMAT_ITEM_F64, "F" => FORMAT_ITEM_F64, "f" => FORMAT_ITEM_F32, @@ -225,6 +228,43 @@ fn odfunc(line_bytes: usize, input_offset_base: &Radix, byte_order: ByteOrder, let mut previous_bytes = Vec::::with_capacity(line_bytes); let mut duplicate_line = false; + let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.byte_size)); + let print_width_block = formats + .iter() + .fold(1, |max, next| { + cmp::max(max, next.print_width * (byte_size_block / next.byte_size)) + }); + + if byte_size_block > MAX_BYTES_PER_UNIT { + panic!("{}-bits types are unsupported. Current max={}-bits.", + 8 * byte_size_block, + 8 * MAX_BYTES_PER_UNIT); + } + + let mut spaced_formatters: Vec = formats + .iter() + .map(|f| SpacedFormatterItemInfo { frm: *f, spacing: [0; MAX_BYTES_PER_UNIT] }) + .collect(); + + // calculate proper alignment for each item + for sf in &mut spaced_formatters { + let mut byte_size = sf.frm.byte_size; + let mut items_in_block = byte_size_block / byte_size; + let thisblock_width = sf.frm.print_width * items_in_block; + let mut missing_spacing = print_width_block - thisblock_width; + + while items_in_block > 0 { + let avg_spacing: usize = missing_spacing / items_in_block; + for i in 0..items_in_block { + sf.spacing[i * byte_size] += avg_spacing; + missing_spacing -= avg_spacing; + } + // this assumes the size of all types is a power of 2 (1, 2, 4, 8, 16, ...) + items_in_block /= 2; + byte_size *= 2; + } + } + loop { // print each line data (or multi-format raster of several lines describing the same data). @@ -252,7 +292,8 @@ fn odfunc(line_bytes: usize, input_offset_base: &Radix, byte_order: ByteOrder, duplicate_line = false; previous_bytes.clone_from(&bytes); - print_bytes(byte_order, &bytes, n, &print_with_radix(input_offset_base, addr), formats); + print_bytes(byte_order, &bytes, n, &print_with_radix(input_offset_base, addr), + &spaced_formatters, byte_size_block); } addr += n; @@ -270,17 +311,23 @@ fn odfunc(line_bytes: usize, input_offset_base: &Radix, byte_order: ByteOrder, } } -fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, prefix: &str, formats: &[FormatterItemInfo]) { +fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, prefix: &str, + formats: &[SpacedFormatterItemInfo], byte_size_block: usize) { let mut first = true; // First line of a multi-format raster. for f in formats { let mut output_text = String::new(); let mut b = 0; while b < length { - let nextb = b + f.byte_size; - match f.formatter { + let nextb = b + f.frm.byte_size; + + output_text.push_str(&format!("{:>width$}", + "", + width = f.spacing[b % byte_size_block])); + + match f.frm.formatter { FormatWriter::IntWriter(func) => { - let p: u64 = match f.byte_size { + let p: u64 = match f.frm.byte_size { 1 => { bytes[b] as u64 } @@ -293,19 +340,19 @@ fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, prefix: &str, 8 => { byte_order.read_u64(&bytes[b..nextb]) } - _ => { panic!("Invalid byte_size: {}", f.byte_size); } + _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); } }; - output_text.push_str(&func(p, f.byte_size, f.print_width)); + output_text.push_str(&func(p, f.frm.byte_size, f.frm.print_width)); } FormatWriter::FloatWriter(func) => { - let p: f64 = match f.byte_size { + let p: f64 = match f.frm.byte_size { 4 => { byte_order.read_f32(&bytes[b..nextb]) as f64 } 8 => { byte_order.read_f64(&bytes[b..nextb]) } - _ => { panic!("Invalid byte_size: {}", f.byte_size); } + _ => { panic!("Invalid byte_size: {}", f.frm.byte_size); } }; output_text.push_str(&func(p)); } @@ -360,3 +407,8 @@ fn print_with_radix(r: &Radix, x: usize) -> String{ Radix::Binary => format!("{:07b}", x) } } + +struct SpacedFormatterItemInfo { + frm: FormatterItemInfo, + spacing: [usize; MAX_BYTES_PER_UNIT], +} diff --git a/src/od/prn_char.rs b/src/od/prn_char.rs index a9663c26a..d0811f107 100644 --- a/src/od/prn_char.rs +++ b/src/od/prn_char.rs @@ -2,13 +2,13 @@ use formatteriteminfo::*; pub static FORMAT_ITEM_A: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 3, + print_width: 4, formatter: FormatWriter::IntWriter(format_item_a), }; pub static FORMAT_ITEM_C: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 3, + print_width: 4, formatter: FormatWriter::IntWriter(format_item_c), }; diff --git a/src/od/prn_float.rs b/src/od/prn_float.rs index 4ecc63200..22918ccb4 100644 --- a/src/od/prn_float.rs +++ b/src/od/prn_float.rs @@ -5,13 +5,13 @@ use formatteriteminfo::*; pub static FORMAT_ITEM_F32: FormatterItemInfo = FormatterItemInfo { byte_size: 4, - print_width: 14, + print_width: 15, formatter: FormatWriter::FloatWriter(format_item_flo32), }; pub static FORMAT_ITEM_F64: FormatterItemInfo = FormatterItemInfo { byte_size: 8, - print_width: 24, + print_width: 25, formatter: FormatWriter::FloatWriter(format_item_flo64), }; diff --git a/src/od/prn_int.rs b/src/od/prn_int.rs index 27cdcfdde..7f2b1b58f 100644 --- a/src/od/prn_int.rs +++ b/src/od/prn_int.rs @@ -2,52 +2,52 @@ use formatteriteminfo::*; pub static FORMAT_ITEM_OCT8: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 3, + print_width: 4, // max: 377 formatter: FormatWriter::IntWriter(format_item_oct), }; pub static FORMAT_ITEM_OCT16: FormatterItemInfo = FormatterItemInfo { byte_size: 2, - print_width: 6, + print_width: 7, // max: 177777 formatter: FormatWriter::IntWriter(format_item_oct), }; pub static FORMAT_ITEM_OCT32: FormatterItemInfo = FormatterItemInfo { byte_size: 4, - print_width: 12, + print_width: 12, // max: 37777777777 formatter: FormatWriter::IntWriter(format_item_oct), }; #[allow(dead_code)] pub static FORMAT_ITEM_OCT64: FormatterItemInfo = FormatterItemInfo { byte_size: 8, - print_width: 24, + print_width: 23, // max: 2000000000000000000000 formatter: FormatWriter::IntWriter(format_item_oct), }; #[allow(dead_code)] pub static FORMAT_ITEM_HEX8: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 2, + print_width: 3, // max: ff formatter: FormatWriter::IntWriter(format_item_hex), }; pub static FORMAT_ITEM_HEX16: FormatterItemInfo = FormatterItemInfo { byte_size: 2, - print_width: 4, + print_width: 5, // max: ffff formatter: FormatWriter::IntWriter(format_item_hex), }; pub static FORMAT_ITEM_HEX32: FormatterItemInfo = FormatterItemInfo { byte_size: 4, - print_width: 8, + print_width: 9, // max: ffffffff formatter: FormatWriter::IntWriter(format_item_hex), }; #[allow(dead_code)] pub static FORMAT_ITEM_HEX64: FormatterItemInfo = FormatterItemInfo { byte_size: 8, - print_width: 16, + print_width: 17, // max: ffffffffffffffff formatter: FormatWriter::IntWriter(format_item_hex), }; @@ -55,26 +55,26 @@ pub static FORMAT_ITEM_HEX64: FormatterItemInfo = FormatterItemInfo { #[allow(dead_code)] pub static FORMAT_ITEM_DEC8U: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 3, + print_width: 4, // max: 255 formatter: FormatWriter::IntWriter(format_item_dec_u), }; pub static FORMAT_ITEM_DEC16U: FormatterItemInfo = FormatterItemInfo { byte_size: 2, - print_width: 5, + print_width: 6, // max: 65535 formatter: FormatWriter::IntWriter(format_item_dec_u), }; pub static FORMAT_ITEM_DEC32U: FormatterItemInfo = FormatterItemInfo { byte_size: 4, - print_width: 10, + print_width: 11, // max: 4294967295 formatter: FormatWriter::IntWriter(format_item_dec_u), }; #[allow(dead_code)] pub static FORMAT_ITEM_DEC64U: FormatterItemInfo = FormatterItemInfo { byte_size: 8, - print_width: 19, + print_width: 21, // max: 18446744073709551615 formatter: FormatWriter::IntWriter(format_item_dec_u), }; @@ -82,27 +82,27 @@ pub static FORMAT_ITEM_DEC64U: FormatterItemInfo = FormatterItemInfo { #[allow(dead_code)] pub static FORMAT_ITEM_DEC8S: FormatterItemInfo = FormatterItemInfo { byte_size: 1, - print_width: 4, + print_width: 5, // max: -128 formatter: FormatWriter::IntWriter(format_item_dec_s), }; pub static FORMAT_ITEM_DEC16S: FormatterItemInfo = FormatterItemInfo { byte_size: 2, - print_width: 6, + print_width: 7, // max: -32768 formatter: FormatWriter::IntWriter(format_item_dec_s), }; #[allow(dead_code)] pub static FORMAT_ITEM_DEC32S: FormatterItemInfo = FormatterItemInfo { byte_size: 4, - print_width: 11, + print_width: 12, // max: -2147483648 formatter: FormatWriter::IntWriter(format_item_dec_s), }; #[allow(dead_code)] pub static FORMAT_ITEM_DEC64S: FormatterItemInfo = FormatterItemInfo { byte_size: 8, - print_width: 20, + print_width: 21, // max: -9223372036854775808 formatter: FormatWriter::IntWriter(format_item_dec_s), }; @@ -112,14 +112,14 @@ pub fn format_item_oct(p: u64, _: usize, print_width: usize) -> String { format!(" {:0width$o}", p, - width = print_width) + width = print_width - 1) } pub fn format_item_hex(p: u64, _: usize, print_width: usize) -> String { format!(" {:0width$x}", p, - width = print_width) + width = print_width - 1) } @@ -132,9 +132,9 @@ fn sign_extend(item: u64, itembytes: usize) -> i64{ pub fn format_item_dec_s(p: u64, itembytes: usize, print_width: usize) -> String { // sign extend let s = sign_extend(p, itembytes); - format!(" {:width$}", s, width = print_width) + format!("{:width$}", s, width = print_width) } pub fn format_item_dec_u(p: u64, _: usize, print_width: usize) -> String { - format!(" {:width$}", p, width = print_width) + format!("{:width$}", p, width = print_width) } diff --git a/tests/test_od.rs b/tests/test_od.rs index a7bfc3d5e..1c513e28e 100644 --- a/tests/test_od.rs +++ b/tests/test_od.rs @@ -312,11 +312,11 @@ fn test_suppress_duplicates(){ let input = [0u8 ; 41]; let expected_output = unindent(" - 0000000 000000000000 - 0000 0000 + 0000000 00000000000 + 0000 0000 * - 0000050 000000000000 - 0000 + 0000050 00000000000 + 0000 0000051 "); @@ -332,16 +332,81 @@ fn test_big_endian() { let input : [u8; 8] = [ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];// 0xc000000000000000 -2 + let expected_output = unindent(" - 0000000 -2.0000000000000000 - -2.0000000 0 - c0000000 00000000 - c000 0000 0000 0000 - 0000010 - "); + 0000000 -2.0000000000000000 + -2.0000000 0 + c0000000 00000000 + c000 0000 0000 0000 + 0000010 + "); + let result = new_ucmd!().arg("--endian=big").arg("-F").arg("-f").arg("-X").arg("-x").run_piped_stdin(&input[..]); assert_empty_stderr!(result); assert!(result.success); assert_eq!(result.stdout, expected_output); } + +#[test] +#[allow(non_snake_case)] +fn test_alignment_Xxa() { + + let input : [u8; 8] = [ + 0x0A, 0x0D, 0x65, 0x66, 0x67, 0x00, 0x9e, 0x9f]; + + let expected_output = unindent(" + 0000000 66650d0a 9f9e0067 + 0d0a 6665 0067 9f9e + nl cr e f g nul 9e 9f + 0000010 + "); + + // in this case the width of the -a (8-bit) determines the alignment for the other fields + let result = new_ucmd!().arg("--endian=little").arg("-X").arg("-x").arg("-a").run_piped_stdin(&input[..]); + + assert_empty_stderr!(result); + assert!(result.success); + assert_eq!(result.stdout, expected_output); +} + +#[test] +#[allow(non_snake_case)] +fn test_alignment_Fx() { + + let input : [u8; 8] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0];// 0xc000000000000000 -2 + + let expected_output = unindent(" + 0000000 -2.0000000000000000 + 0000 0000 0000 c000 + 0000010 + "); + + // in this case the width of the -F (64-bit) determines the alignment for the other field + let result = new_ucmd!().arg("--endian=little").arg("-F").arg("-x").run_piped_stdin(&input[..]); + + assert_empty_stderr!(result); + assert!(result.success); + assert_eq!(result.stdout, expected_output); +} + +#[test] +fn test_maxuint(){ + + let input = [0xFFu8 ; 8]; + let expected_output = unindent(" + 0000000 37777777777 37777777777 + 177777 177777 177777 177777 + 377 377 377 377 377 377 377 377 + 4294967295 4294967295 + 65535 65535 65535 65535 + 0000010 + "); + + let result = new_ucmd!().arg("-O").arg("-o").arg("-b").arg("-D").arg("-d").run_piped_stdin(&input[..]); + + assert_empty_stderr!(result); + assert!(result.success); + assert_eq!(result.stdout, expected_output); +}