mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
od: refactor: create InputDecoder to convert input
It reads from the input and provides data conversion functions.
This commit is contained in:
parent
283a29fd2c
commit
83a1ff404f
2 changed files with 215 additions and 55 deletions
182
src/od/inputdecoder.rs
Normal file
182
src/od/inputdecoder.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
use std::io;
|
||||||
|
use byteorder_io::ByteOrder;
|
||||||
|
use multifilereader::HasError;
|
||||||
|
use peekreader::PeekRead;
|
||||||
|
|
||||||
|
/// Processes an input and provides access to the data read in various formats
|
||||||
|
///
|
||||||
|
/// Currently only useful if the input implements `PeekRead`.
|
||||||
|
pub struct InputDecoder<'a, I> where I: 'a {
|
||||||
|
/// The input from which data is read
|
||||||
|
input: &'a mut I,
|
||||||
|
|
||||||
|
/// A memory buffer, it's size is set in `new`.
|
||||||
|
data: Vec<u8>,
|
||||||
|
/// The numer of bytes in the buffer reserved for the peek data from `PeekRead`.
|
||||||
|
reserved_peek_length: usize,
|
||||||
|
|
||||||
|
/// The number of (valid) bytes in the buffer.
|
||||||
|
used_normal_length: usize,
|
||||||
|
/// The number of peek bytes in the buffer.
|
||||||
|
used_peek_length: usize,
|
||||||
|
|
||||||
|
/// Byte order used to read data from the buffer.
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I> InputDecoder<'a, I> {
|
||||||
|
/// Creates a new `InputDecoder` with an allocated buffer of `normal_length`+`peek_length` bytes.
|
||||||
|
/// `byte_order` determines how to read multibyte formats from the buffer.
|
||||||
|
pub fn new(input: &mut I, normal_length: usize, peek_length: usize, byte_order: ByteOrder) -> InputDecoder<I> {
|
||||||
|
|
||||||
|
let mut bytes: Vec<u8> = Vec::with_capacity(normal_length+peek_length);
|
||||||
|
unsafe { bytes.set_len(normal_length+peek_length); } // fast but uninitialized
|
||||||
|
|
||||||
|
InputDecoder {
|
||||||
|
input: input,
|
||||||
|
data: bytes,
|
||||||
|
reserved_peek_length: peek_length,
|
||||||
|
used_normal_length: 0,
|
||||||
|
used_peek_length: 0,
|
||||||
|
byte_order: byte_order,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, I> InputDecoder<'a, I> where I : PeekRead {
|
||||||
|
/// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a
|
||||||
|
/// MemoryDecoder providing access to the result or returns an i/o error.
|
||||||
|
pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> {
|
||||||
|
match self.input.peek_read(self.data.as_mut_slice(), self.reserved_peek_length) {
|
||||||
|
Ok((n, p)) => {
|
||||||
|
self.used_normal_length = n;
|
||||||
|
self.used_peek_length = p;
|
||||||
|
Ok(MemoryDecoder {
|
||||||
|
data: &mut self.data,
|
||||||
|
used_normal_length: self.used_normal_length,
|
||||||
|
used_peek_length: self.used_peek_length,
|
||||||
|
byte_order: self.byte_order,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I> HasError for InputDecoder<'a, I> where I : HasError {
|
||||||
|
/// calls has_error on the internal stream.
|
||||||
|
fn has_error(&self) -> bool {
|
||||||
|
self.input.has_error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides access to the internal data in various formats
|
||||||
|
pub struct MemoryDecoder<'a> {
|
||||||
|
/// A reference to the parents' data
|
||||||
|
data: &'a mut Vec<u8>,
|
||||||
|
/// The number of (valid) bytes in the buffer.
|
||||||
|
used_normal_length: usize,
|
||||||
|
/// The number of peek bytes in the buffer.
|
||||||
|
used_peek_length: usize,
|
||||||
|
/// Byte order used to read data from the buffer.
|
||||||
|
byte_order: ByteOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MemoryDecoder<'a> {
|
||||||
|
/// Set a part of the internal buffer to zero.
|
||||||
|
/// access to the whole buffer is possible, not just to the valid data.
|
||||||
|
pub fn zero_out_buffer(&mut self, start:usize, end:usize) {
|
||||||
|
for i in start..end {
|
||||||
|
self.data[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current length of the buffer. (ie. how much valid data it contains.)
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.used_normal_length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a clone of the internal buffer. The clone only contain the valid data.
|
||||||
|
pub fn clone_buffer(&self, other: &mut Vec<u8>) {
|
||||||
|
other.clone_from(&self.data);
|
||||||
|
other.resize(self.used_normal_length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice to the internal buffer starting at `start`.
|
||||||
|
pub fn get_buffer(&self, start: usize) -> &[u8] {
|
||||||
|
&self.data[start..self.used_normal_length]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice to the internal buffer including the peek data starting at `start`.
|
||||||
|
pub fn get_full_buffer(&self, start: usize) -> &[u8] {
|
||||||
|
&self.data[start..self.used_normal_length+self.used_peek_length]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a u8/u16/u32/u64 from the internal buffer at position `start`.
|
||||||
|
pub fn read_uint(&self, start: usize, byte_size: usize) -> u64 {
|
||||||
|
match byte_size {
|
||||||
|
1 => self.data[start] as u64,
|
||||||
|
2 => self.byte_order.read_u16(&self.data[start..start + 2]) as u64,
|
||||||
|
4 => self.byte_order.read_u32(&self.data[start..start + 4]) as u64,
|
||||||
|
8 => self.byte_order.read_u64(&self.data[start..start + 8]),
|
||||||
|
_ => panic!("Invalid byte_size: {}", byte_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a f32/f64 from the internal buffer at position `start`.
|
||||||
|
pub fn read_float(&self, start: usize, byte_size: usize) -> f64 {
|
||||||
|
match byte_size {
|
||||||
|
4 => self.byte_order.read_f32(&self.data[start..start + 4]) as f64,
|
||||||
|
8 => self.byte_order.read_f64(&self.data[start..start + 8]),
|
||||||
|
_ => panic!("Invalid byte_size: {}", byte_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
use peekreader::PeekReader;
|
||||||
|
use byteorder_io::ByteOrder;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn smoke_test() {
|
||||||
|
let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff];
|
||||||
|
let mut input=PeekReader::new(Cursor::new(&data));
|
||||||
|
let mut sut=InputDecoder::new(&mut input, 8, 2, ByteOrder::Little);
|
||||||
|
|
||||||
|
match sut.peek_read() {
|
||||||
|
Ok(mut mem) => {
|
||||||
|
assert_eq!(8, mem.length());
|
||||||
|
|
||||||
|
assert_eq!(-2.0, mem.read_float(0, 8));
|
||||||
|
assert_eq!(-2.0, mem.read_float(4, 4));
|
||||||
|
assert_eq!(0xc000000000000000, mem.read_uint(0, 8));
|
||||||
|
assert_eq!(0xc0000000, mem.read_uint(4, 4));
|
||||||
|
assert_eq!(0xc000, mem.read_uint(6, 2));
|
||||||
|
assert_eq!(0xc0, mem.read_uint(7, 1));
|
||||||
|
assert_eq!(&[0, 0xc0], mem.get_buffer(6));
|
||||||
|
assert_eq!(&[0, 0xc0, 0xff, 0xff], mem.get_full_buffer(6));
|
||||||
|
|
||||||
|
let mut copy: Vec<u8> = Vec::new();
|
||||||
|
mem.clone_buffer(&mut copy);
|
||||||
|
assert_eq!(vec!{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0}, copy);
|
||||||
|
|
||||||
|
mem.zero_out_buffer(7, 8);
|
||||||
|
assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6));
|
||||||
|
}
|
||||||
|
Err(e) => { assert!(false, e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
match sut.peek_read() {
|
||||||
|
Ok(mem) => {
|
||||||
|
assert_eq!(2, mem.length());
|
||||||
|
assert_eq!(0xffff, mem.read_uint(0, 2));
|
||||||
|
}
|
||||||
|
Err(e) => { assert!(false, e); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
src/od/od.rs
88
src/od/od.rs
|
@ -27,6 +27,7 @@ mod parse_nrofbytes;
|
||||||
mod parse_formats;
|
mod parse_formats;
|
||||||
mod parse_inputs;
|
mod parse_inputs;
|
||||||
mod inputoffset;
|
mod inputoffset;
|
||||||
|
mod inputdecoder;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mockstream;
|
mod mockstream;
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ use parse_formats::{parse_format_flags, ParsedFormatterItemInfo};
|
||||||
use prn_char::format_ascii_dump;
|
use prn_char::format_ascii_dump;
|
||||||
use parse_inputs::{parse_inputs, CommandLineInputs};
|
use parse_inputs::{parse_inputs, CommandLineInputs};
|
||||||
use inputoffset::{InputOffset, Radix};
|
use inputoffset::{InputOffset, Radix};
|
||||||
|
use inputdecoder::{InputDecoder,MemoryDecoder};
|
||||||
|
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
const MAX_BYTES_PER_UNIT: usize = 8;
|
const MAX_BYTES_PER_UNIT: usize = 8;
|
||||||
|
@ -233,25 +235,25 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
|
||||||
let mut input = open_input_peek_reader(&input_strings, skip_bytes, read_bytes);
|
let mut input = open_input_peek_reader(&input_strings, skip_bytes, read_bytes);
|
||||||
|
|
||||||
|
let mut input_decoder = InputDecoder::new(&mut input, line_bytes, PEEK_BUFFER_SIZE, byte_order);
|
||||||
|
|
||||||
let mut input_offset = InputOffset::new(Radix::Octal, skip_bytes, label);
|
let mut input_offset = InputOffset::new(Radix::Octal, skip_bytes, label);
|
||||||
if let Err(e) = input_offset.parse_radix_from_commandline(matches.opt_str("A")) {
|
if let Err(e) = input_offset.parse_radix_from_commandline(matches.opt_str("A")) {
|
||||||
disp_err!("Invalid -A/--address-radix\n{}", e);
|
disp_err!("Invalid -A/--address-radix\n{}", e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
odfunc(&mut input, &mut input_offset, line_bytes, byte_order, &formats[..],
|
odfunc(&mut input_decoder, &mut input_offset, line_bytes, &formats[..],
|
||||||
output_duplicates)
|
output_duplicates)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor, too many arguments
|
// TODO: refactor, too many arguments
|
||||||
fn odfunc<I>(input: &mut I, input_offset: &mut InputOffset, line_bytes: usize, byte_order: ByteOrder,
|
fn odfunc<I>(input_decoder: &mut InputDecoder<I>, input_offset: &mut InputOffset, line_bytes: usize,
|
||||||
formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> i32
|
formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> i32
|
||||||
where I : PeekRead+HasError {
|
where I : PeekRead+HasError {
|
||||||
|
|
||||||
let mut duplicate_line = false;
|
let mut duplicate_line = false;
|
||||||
let mut previous_bytes: Vec<u8> = Vec::new();
|
let mut previous_bytes: Vec<u8> = Vec::new();
|
||||||
let mut bytes: Vec<u8> = Vec::with_capacity(line_bytes + PEEK_BUFFER_SIZE);
|
|
||||||
unsafe { bytes.set_len(line_bytes + PEEK_BUFFER_SIZE); } // fast but uninitialized
|
|
||||||
|
|
||||||
let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size));
|
let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size));
|
||||||
let print_width_block = formats
|
let print_width_block = formats
|
||||||
|
@ -294,29 +296,29 @@ fn odfunc<I>(input: &mut I, input_offset: &mut InputOffset, line_bytes: usize, b
|
||||||
loop {
|
loop {
|
||||||
// print each line data (or multi-format raster of several lines describing the same data).
|
// print each line data (or multi-format raster of several lines describing the same data).
|
||||||
|
|
||||||
match input.peek_read(bytes.as_mut_slice(), PEEK_BUFFER_SIZE) {
|
match input_decoder.peek_read() {
|
||||||
Ok((0, _)) => {
|
Ok(mut memory_decoder) => {
|
||||||
input_offset.print_final_offset();
|
let length=memory_decoder.length();
|
||||||
break;
|
|
||||||
}
|
if length == 0 {
|
||||||
Ok((n, peekbytes)) => {
|
input_offset.print_final_offset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// not enough byte for a whole element, this should only happen on the last line.
|
// not enough byte for a whole element, this should only happen on the last line.
|
||||||
if n != line_bytes {
|
if length != line_bytes {
|
||||||
// set zero bytes in the part of the buffer that will be used, but is not filled.
|
// set zero bytes in the part of the buffer that will be used, but is not filled.
|
||||||
let mut max_used = n + MAX_BYTES_PER_UNIT;
|
let mut max_used = length + MAX_BYTES_PER_UNIT;
|
||||||
if max_used > line_bytes {
|
if max_used > line_bytes {
|
||||||
max_used = line_bytes;
|
max_used = line_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in n..max_used {
|
memory_decoder.zero_out_buffer(length, max_used);
|
||||||
bytes[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !output_duplicates
|
if !output_duplicates
|
||||||
&& n == line_bytes
|
&& length == line_bytes
|
||||||
&& !previous_bytes.is_empty()
|
&& memory_decoder.get_buffer(0) == &previous_bytes[..] {
|
||||||
&& previous_bytes[..line_bytes] == bytes[..line_bytes] {
|
|
||||||
if !duplicate_line {
|
if !duplicate_line {
|
||||||
duplicate_line = true;
|
duplicate_line = true;
|
||||||
println!("*");
|
println!("*");
|
||||||
|
@ -324,17 +326,16 @@ fn odfunc<I>(input: &mut I, input_offset: &mut InputOffset, line_bytes: usize, b
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
duplicate_line = false;
|
duplicate_line = false;
|
||||||
if n == line_bytes {
|
if length == line_bytes {
|
||||||
// save a copy of the input unless it is the last line
|
// save a copy of the input unless it is the last line
|
||||||
previous_bytes.clone_from(&bytes);
|
memory_decoder.clone_buffer(&mut previous_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_bytes(byte_order, &bytes, n, peekbytes,
|
print_bytes(&input_offset.format_byte_offset(), &memory_decoder,
|
||||||
&input_offset.format_byte_offset(),
|
|
||||||
&spaced_formatters, byte_size_block, print_width_line);
|
&spaced_formatters, byte_size_block, print_width_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_offset.increase_position(n);
|
input_offset.increase_position(length);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_error!("{}", e);
|
show_error!("{}", e);
|
||||||
|
@ -344,70 +345,47 @@ fn odfunc<I>(input: &mut I, input_offset: &mut InputOffset, line_bytes: usize, b
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.has_error() {
|
if input_decoder.has_error() {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, peekbytes: usize, prefix: &str,
|
fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder,
|
||||||
formats: &[SpacedFormatterItemInfo], byte_size_block: usize, print_width_line: usize) {
|
formats: &[SpacedFormatterItemInfo], byte_size_block: usize, print_width_line: usize) {
|
||||||
let mut first = true; // First line of a multi-format raster.
|
let mut first = true; // First line of a multi-format raster.
|
||||||
for f in formats {
|
for f in formats {
|
||||||
let mut output_text = String::new();
|
let mut output_text = String::new();
|
||||||
|
|
||||||
let mut b = 0;
|
let mut b = 0;
|
||||||
while b < length {
|
while b < input_decoder.length() {
|
||||||
let nextb = b + f.frm.formatter_item_info.byte_size;
|
|
||||||
|
|
||||||
output_text.push_str(&format!("{:>width$}",
|
output_text.push_str(&format!("{:>width$}",
|
||||||
"",
|
"",
|
||||||
width = f.spacing[b % byte_size_block]));
|
width = f.spacing[b % byte_size_block]));
|
||||||
|
|
||||||
match f.frm.formatter_item_info.formatter {
|
match f.frm.formatter_item_info.formatter {
|
||||||
FormatWriter::IntWriter(func) => {
|
FormatWriter::IntWriter(func) => {
|
||||||
let p: u64 = match f.frm.formatter_item_info.byte_size {
|
let p = input_decoder.read_uint(b, f.frm.formatter_item_info.byte_size);
|
||||||
1 => {
|
|
||||||
bytes[b] as u64
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
byte_order.read_u16(&bytes[b..nextb]) as u64
|
|
||||||
}
|
|
||||||
4 => {
|
|
||||||
byte_order.read_u32(&bytes[b..nextb]) as u64
|
|
||||||
}
|
|
||||||
8 => {
|
|
||||||
byte_order.read_u64(&bytes[b..nextb])
|
|
||||||
}
|
|
||||||
_ => { panic!("Invalid byte_size: {}", f.frm.formatter_item_info.byte_size); }
|
|
||||||
};
|
|
||||||
output_text.push_str(&func(p));
|
output_text.push_str(&func(p));
|
||||||
}
|
}
|
||||||
FormatWriter::FloatWriter(func) => {
|
FormatWriter::FloatWriter(func) => {
|
||||||
let p: f64 = match f.frm.formatter_item_info.byte_size {
|
let p = input_decoder.read_float(b, f.frm.formatter_item_info.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.frm.formatter_item_info.byte_size); }
|
|
||||||
};
|
|
||||||
output_text.push_str(&func(p));
|
output_text.push_str(&func(p));
|
||||||
}
|
}
|
||||||
FormatWriter::MultibyteWriter(func) => {
|
FormatWriter::MultibyteWriter(func) => {
|
||||||
output_text.push_str(&func(&bytes[b..length+peekbytes]));
|
output_text.push_str(&func(input_decoder.get_full_buffer(b)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b = nextb;
|
|
||||||
|
b += f.frm.formatter_item_info.byte_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.frm.add_ascii_dump {
|
if f.frm.add_ascii_dump {
|
||||||
let missing_spacing = print_width_line.saturating_sub(output_text.chars().count());
|
let missing_spacing = print_width_line.saturating_sub(output_text.chars().count());
|
||||||
output_text.push_str(&format!("{:>width$} {}",
|
output_text.push_str(&format!("{:>width$} {}",
|
||||||
"",
|
"",
|
||||||
format_ascii_dump(&bytes[..length]),
|
format_ascii_dump(input_decoder.get_buffer(0)),
|
||||||
width=missing_spacing));
|
width=missing_spacing));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue