mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
od: proper align different sized output
This commit is contained in:
parent
45895be96d
commit
24fb6d66c4
6 changed files with 161 additions and 44 deletions
|
@ -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,
|
||||
}
|
||||
|
|
70
src/od/od.rs
70
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<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],
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -312,10 +312,10 @@ fn test_suppress_duplicates(){
|
|||
|
||||
let input = [0u8 ; 41];
|
||||
let expected_output = unindent("
|
||||
0000000 000000000000
|
||||
0000000 00000000000
|
||||
0000 0000
|
||||
*
|
||||
0000050 000000000000
|
||||
0000050 00000000000
|
||||
0000
|
||||
0000051
|
||||
");
|
||||
|
@ -332,6 +332,7 @@ 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
|
||||
|
@ -339,9 +340,73 @@ fn test_big_endian() {
|
|||
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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue