1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

od: proper align different sized output

This commit is contained in:
Wim Hueskes 2016-07-31 16:51:20 +02:00
parent 45895be96d
commit 24fb6d66c4
6 changed files with 161 additions and 44 deletions

View file

@ -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,
}

View file

@ -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<String>) -> 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<String>) -> 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::<u8>::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<SpacedFormatterItemInfo> = 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],
}

View file

@ -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),
};

View file

@ -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),
};

View file

@ -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)
}

View file

@ -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);
}