1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

Merge pull request #2573 from backwaterred/master

dd: Apply OutputTrait to reduce code duplication #2572
This commit is contained in:
Sylvestre Ledru 2021-08-19 10:22:44 +02:00 committed by GitHub
commit b58f4189fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 199 deletions

View file

@ -275,13 +275,19 @@ impl<R: Read> Input<R> {
} }
} }
trait OutputTrait: Sized + Write {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>>;
fn fsync(&mut self) -> io::Result<()>;
fn fdatasync(&mut self) -> io::Result<()>;
}
struct Output<W: Write> { struct Output<W: Write> {
dst: W, dst: W,
obs: usize, obs: usize,
cflags: OConvFlags, cflags: OConvFlags,
} }
impl Output<io::Stdout> { impl OutputTrait for Output<io::Stdout> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> { fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
let obs = parseargs::parse_obs(matches)?; let obs = parseargs::parse_obs(matches)?;
let cflags = parseargs::parse_conv_flag_output(matches)?; let cflags = parseargs::parse_conv_flag_output(matches)?;
@ -300,6 +306,100 @@ impl Output<io::Stdout> {
} }
} }
impl<W: Write> Output<W>
where
Self: OutputTrait,
{
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
let mut writes_complete = 0;
let mut writes_partial = 0;
let mut bytes_total = 0;
for chunk in buf.chunks(self.obs) {
match self.write(chunk)? {
wlen if wlen < chunk.len() => {
writes_partial += 1;
bytes_total += wlen;
}
wlen => {
writes_complete += 1;
bytes_total += wlen;
}
}
}
Ok(WriteStat {
writes_complete,
writes_partial,
bytes_total: bytes_total.try_into().unwrap_or(0u128),
})
}
fn dd_out<R: Read>(mut self, mut i: Input<R>) -> Result<(), Box<dyn Error>> {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
records_truncated: 0,
};
let mut wstat = WriteStat {
writes_complete: 0,
writes_partial: 0,
bytes_total: 0,
};
let start = time::Instant::now();
let bsize = calc_bsize(i.ibs, self.obs);
let prog_tx = {
let (tx, rx) = mpsc::channel();
thread::spawn(gen_prog_updater(rx, i.print_level));
tx
};
while below_count_limit(&i.count, &rstat, &wstat) {
// Read/Write
let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize);
match read_helper(&mut i, loop_bsize)? {
(
ReadStat {
reads_complete: 0,
reads_partial: 0,
..
},
_,
) => break,
(rstat_update, buf) => {
let wstat_update = self.write_blocks(buf)?;
rstat += rstat_update;
wstat += wstat_update;
}
};
// Update Prog
prog_tx.send(ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
})?;
}
if self.cflags.fsync {
self.fsync()?;
} else if self.cflags.fdatasync {
self.fdatasync()?;
}
match i.print_level {
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
_ => print_transfer_stats(&ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
}),
}
Ok(())
}
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> { fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
let mut flag = 0; let mut flag = 0;
@ -340,7 +440,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
} }
} }
impl Output<File> { impl OutputTrait for Output<File> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> { fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> { fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
let mut opts = OpenOptions::new(); let mut opts = OpenOptions::new();
@ -430,62 +530,6 @@ impl Write for Output<io::Stdout> {
} }
} }
impl Output<io::Stdout> {
/// Write all data in the given buffer in writes of size obs.
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
let mut writes_complete = 0;
let mut writes_partial = 0;
let mut bytes_total = 0;
for chunk in buf.chunks(self.obs) {
match self.write(chunk)? {
wlen if wlen < chunk.len() => {
writes_partial += 1;
bytes_total += wlen;
}
wlen => {
writes_complete += 1;
bytes_total += wlen;
}
}
}
Ok(WriteStat {
writes_complete,
writes_partial,
bytes_total: bytes_total.try_into().unwrap_or(0u128),
})
}
}
impl Output<File> {
/// Write all data in the given buffer in writes of size obs.
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<WriteStat> {
let mut writes_complete = 0;
let mut writes_partial = 0;
let mut bytes_total = 0;
for chunk in buf.chunks(self.obs) {
match self.write(chunk)? {
wlen if wlen < chunk.len() => {
writes_partial += 1;
bytes_total += wlen;
}
wlen => {
writes_complete += 1;
bytes_total += wlen;
}
}
}
Ok(WriteStat {
writes_complete,
writes_partial,
bytes_total: bytes_total.try_into().unwrap_or(0u128),
})
}
}
/// Splits the content of buf into cbs-length blocks /// Splits the content of buf into cbs-length blocks
/// Appends padding as specified by conv=block and cbs=N /// Appends padding as specified by conv=block and cbs=N
/// Expects ascii encoded data /// Expects ascii encoded data
@ -827,140 +871,6 @@ fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteS
} }
} }
/// Perform the copy/convert operations. Stdout version
/// Note: The body of this function should be kept identical to dd_fileout. This is definitely a problem from a maintenance perspective
/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout.
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(), Box<dyn Error>> {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
records_truncated: 0,
};
let mut wstat = WriteStat {
writes_complete: 0,
writes_partial: 0,
bytes_total: 0,
};
let start = time::Instant::now();
let bsize = calc_bsize(i.ibs, o.obs);
let prog_tx = {
let (tx, rx) = mpsc::channel();
thread::spawn(gen_prog_updater(rx, i.print_level));
tx
};
while below_count_limit(&i.count, &rstat, &wstat) {
// Read/Write
let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize);
match read_helper(&mut i, loop_bsize)? {
(
ReadStat {
reads_complete: 0,
reads_partial: 0,
..
},
_,
) => break,
(rstat_update, buf) => {
let wstat_update = o.write_blocks(buf)?;
rstat += rstat_update;
wstat += wstat_update;
}
};
// Update Prog
prog_tx.send(ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
})?;
}
if o.cflags.fsync {
o.fsync()?;
} else if o.cflags.fdatasync {
o.fdatasync()?;
}
match i.print_level {
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
_ => print_transfer_stats(&ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
}),
}
Ok(())
}
/// Perform the copy/convert operations. File backed output version
/// Note: The body of this function should be kept identical to dd_stdout. This is definitely a problem from a maintenance perspective
/// and should be addressed (TODO). The problem exists because some of dd's functionality depends on whether the output is a file or stdout.
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<dyn Error>> {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
records_truncated: 0,
};
let mut wstat = WriteStat {
writes_complete: 0,
writes_partial: 0,
bytes_total: 0,
};
let start = time::Instant::now();
let bsize = calc_bsize(i.ibs, o.obs);
let prog_tx = {
let (tx, rx) = mpsc::channel();
thread::spawn(gen_prog_updater(rx, i.print_level));
tx
};
while below_count_limit(&i.count, &rstat, &wstat) {
// Read/Write
let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize);
match read_helper(&mut i, loop_bsize)? {
(
ReadStat {
reads_complete: 0,
reads_partial: 0,
..
},
_,
) => break,
(rstat_update, buf) => {
let wstat_update = o.write_blocks(buf)?;
rstat += rstat_update;
wstat += wstat_update;
}
};
// Update Prog
prog_tx.send(ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
})?;
}
if o.cflags.fsync {
o.fsync()?;
} else if o.cflags.fdatasync {
o.fdatasync()?;
}
match i.print_level {
Some(StatusLevel::Noxfer) | Some(StatusLevel::None) => {}
_ => print_transfer_stats(&ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
}),
}
Ok(())
}
fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> { fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<String> {
if !s.starts_with("--") && !s.starts_with('-') { if !s.starts_with("--") && !s.starts_with('-') {
s.insert_str(0, "--"); s.insert_str(0, "--");
@ -1009,7 +919,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let (i, o) = let (i, o) =
unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches)); unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches));
dd_fileout(i, o) o.dd_out(i)
} }
(false, true) => { (false, true) => {
let (i, o) = unpack_or_rtn!( let (i, o) = unpack_or_rtn!(
@ -1017,7 +927,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Output::<File>::new(&matches) Output::<File>::new(&matches)
); );
dd_fileout(i, o) o.dd_out(i)
} }
(true, false) => { (true, false) => {
let (i, o) = unpack_or_rtn!( let (i, o) = unpack_or_rtn!(
@ -1025,7 +935,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Output::<io::Stdout>::new(&matches) Output::<io::Stdout>::new(&matches)
); );
dd_stdout(i, o) o.dd_out(i)
} }
(false, false) => { (false, false) => {
let (i, o) = unpack_or_rtn!( let (i, o) = unpack_or_rtn!(
@ -1033,7 +943,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Output::<io::Stdout>::new(&matches) Output::<io::Stdout>::new(&matches)
); );
dd_stdout(i, o) o.dd_out(i)
} }
}; };
match result { match result {

View file

@ -153,7 +153,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
cflags: OConvFlags::default(), cflags: OConvFlags::default(),
}; };
dd_fileout(i, o).unwrap(); o.dd_out(i).unwrap();
// EBCDIC->ASCII // EBCDIC->ASCII
let test_name = "all-valid-ebcdic-to-ascii"; let test_name = "all-valid-ebcdic-to-ascii";
@ -175,7 +175,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
cflags: OConvFlags::default(), cflags: OConvFlags::default(),
}; };
dd_fileout(i, o).unwrap(); o.dd_out(i).unwrap();
// Final Comparison // Final Comparison
let res = File::open(&tmp_fname_ea).unwrap(); let res = File::open(&tmp_fname_ea).unwrap();

View file

@ -67,7 +67,7 @@ macro_rules! make_spec_test (
#[test] #[test]
fn $test_id() fn $test_id()
{ {
dd_fileout($i,$o).unwrap(); $o.dd_out($i).unwrap();
let res = File::open($tmp_fname).unwrap(); let res = File::open($tmp_fname).unwrap();
// Check test file isn't empty (unless spec file is too) // Check test file isn't empty (unless spec file is too)