Impl conv=sync

- Adds tests where ibs causes extention
- Impl conv=sync. All tests passing.
This commit is contained in:
Tyler 2021-06-07 16:13:46 -07:00
parent f7eaf96eda
commit 841faebdac
8 changed files with 200 additions and 34 deletions

View file

@ -36,7 +36,7 @@ const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
const SUMMARY: &str = "convert, and optionally copy, a file"; const SUMMARY: &str = "convert, and optionally copy, a file";
const LONG_HELP: &str = ""; const LONG_HELP: &str = "";
const DEFAULT_INIT_BYTE: u8 = 0xDD; const BUF_INIT_BYTE: u8 = 0xDD;
const RTN_SUCCESS: i32 = 0; const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1; const RTN_FAILURE: i32 = 1;
@ -175,7 +175,7 @@ impl Input<io::Stdin>
if let Some(amt) = skip if let Some(amt) = skip
{ {
let mut buf = vec![DEFAULT_INIT_BYTE; amt]; let mut buf = vec![BUF_INIT_BYTE; amt];
i.force_fill(&mut buf, amt)?; i.force_fill(&mut buf, amt)?;
} }
@ -250,16 +250,16 @@ impl<R: Read> Input<R>
{ {
/// Fills a given obs-sized buffer. /// Fills a given obs-sized buffer.
/// Reads in increments of 'self.ibs'. /// Reads in increments of 'self.ibs'.
fn fill_consecutive(&mut self, buf: &mut [u8]) -> Result<usize, Box<dyn Error>> /// The start of each ibs-sized read follows the previous one.
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> Result<usize, Box<dyn Error>>
{ {
let mut base_idx = 0; let mut base_idx = 0;
while base_idx < buf.len() while base_idx < buf.len()
{ {
let low_idx = base_idx; let next_blk = cmp::min(base_idx+self.ibs, buf.len());
let up_idx = cmp::min(low_idx+self.ibs, buf.len()-1);
let rlen = self.read(&mut buf[low_idx..=up_idx])?; let rlen = self.read(&mut buf[base_idx..next_blk])?;
if rlen > 0 if rlen > 0
{ {
base_idx += rlen; base_idx += rlen;
@ -270,29 +270,41 @@ impl<R: Read> Input<R>
} }
} }
buf.truncate(base_idx);
Ok(base_idx) Ok(base_idx)
} }
/// Fills a given obs-sized buffer. /// Fills a given obs-sized buffer.
/// Reads in increments of 'self.ibs'. /// Reads in increments of 'self.ibs'.
fn fill_blocks(&mut self, buf: &mut [u8]) -> Result<usize, Box<dyn Error>> /// The start of each ibs-sized read is aligned to multiples of ibs; remaing space is filled with the 'pad' byte.
fn fill_blocks(&mut self, buf: &mut Vec<u8>, obs: usize, pad: u8) -> Result<usize, Box<dyn Error>>
{ {
let ibs = self.ibs; let mut base_idx = 0;
let obs = buf.len(); let mut rbytes = 0;
let mut bytes_read = 0;
for n in 0..cmp::max(obs/ibs, 1) { while base_idx < buf.len()
// fill an ibs-len slice from src {
let rlen = self.read(&mut buf[n*ibs..(n+1)*ibs])?; let next_blk = cmp::min(base_idx+self.ibs, buf.len());
let plen = next_blk - base_idx;
if rlen != 0 { let rlen = self.read(&mut buf[base_idx..next_blk])?;
bytes_read += rlen;
} else { if rlen < plen
{
let padding = vec![pad; plen-rlen];
buf.splice(base_idx+rlen..next_blk, padding.into_iter());
}
if rlen == 0
{
break; break;
} }
rbytes += rlen;
base_idx += self.ibs;
} }
Ok(bytes_read) buf.truncate(base_idx);
Ok(rbytes)
} }
/// Force-fills a buffer, ignoring zero-length reads which would otherwise be /// Force-fills a buffer, ignoring zero-length reads which would otherwise be
@ -453,6 +465,40 @@ impl Write for Output<io::Stdout>
} }
} }
impl Output<io::Stdout>
{
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<usize>
{
let mut base_idx = 0;
while base_idx < buf.len()
{
let next_blk = cmp::min(base_idx+self.obs, buf.len());
let wlen = self.write(&buf[base_idx..next_blk])?;
base_idx += wlen;
}
Ok(base_idx)
}
}
impl Output<File>
{
fn write_blocks(&mut self, buf: Vec<u8>) -> io::Result<usize>
{
let mut base_idx = 0;
while base_idx < buf.len()
{
let next_blk = cmp::min(base_idx+self.obs, buf.len());
let wlen = self.write(&buf[base_idx..next_blk])?;
base_idx += wlen;
}
Ok(base_idx)
}
}
/// 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
fn block(buf: Vec<u8>, cbs: usize) -> Vec<Vec<u8>> fn block(buf: Vec<u8>, cbs: usize) -> Vec<Vec<u8>>
@ -655,7 +701,7 @@ fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<
} }
} }
fn read_write_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) -> Result<(usize, Vec<u8>), Box<dyn Error>> fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: usize) -> Result<(usize, Vec<u8>), Box<dyn Error>>
{ {
// Local Predicate Fns ----------------------------------------------- // Local Predicate Fns -----------------------------------------------
#[inline] #[inline]
@ -697,7 +743,7 @@ fn read_write_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) ->
buf[base-1] = tmp; buf[base-1] = tmp;
} }
} }
// ------------------------------------------------------------------ // ------------------------------------------------------------------
if is_fast_read(&i, &o) if is_fast_read(&i, &o)
{ {
// TODO: fast reads are copies performed // TODO: fast reads are copies performed
@ -708,17 +754,13 @@ fn read_write_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) ->
else else
{ {
// Read // Read
let mut buf = vec![DEFAULT_INIT_BYTE; o.obs]; let mut buf = vec![BUF_INIT_BYTE; bsize];
let rlen = if let Some(ch) = i.cflags.sync let rlen = match i.cflags.sync {
{ Some(ch) =>
i.fill_blocks(&mut buf)? i.fill_blocks(&mut buf, o.obs, ch)?,
} _ =>
else i.fill_consecutive(&mut buf)?,
{
i.fill_consecutive(&mut buf)?
}; };
buf.truncate(rlen);
if rlen == 0 if rlen == 0
{ {
return Ok((0,buf)); return Ok((0,buf));
@ -729,7 +771,6 @@ fn read_write_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) ->
{ {
perform_swab(&mut buf); perform_swab(&mut buf);
} }
if is_conv(&i) || is_block(&i) || is_unblock(&i) if is_conv(&i) || is_block(&i) || is_unblock(&i)
{ {
let buf = conv_block_unblock_helper(buf, i, o)?; let buf = conv_block_unblock_helper(buf, i, o)?;
@ -742,6 +783,21 @@ fn read_write_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>) ->
} }
} }
/// Write obs-size blocks
// fn write_helper<W: Write>(o: &mut Output<W>, buf: Vec<u8>) -> Result<usize, Box<dyn Error>>
// {
// let mut base_idx = 0;
//
// while base_idx < buf.len()
// {
// let width = cmp::min(base_idx+o.obs, buf.len());
// let wlen = o.write(&mut buf[base_idx..width])?;
// base_idx += wlen;
// }
//
// Ok(base_idx)
// }
/// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals. /// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals.
fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> () fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
{ {
@ -766,13 +822,22 @@ fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
} }
} }
/// Perform the copy/convert opertaions. Non file backed output version /// Find the greatest common factor for the pair of integers.
fn gcf(u: usize, v: usize) -> usize
{
// TODO: 1 is not the gcf of all pairs of integers...
1
}
/// Perform the copy/convert opertaions. Stdout version
// Note: Some of dd's functionality depends on whether the output is actually a file. This breaks the Output<Write> abstraction, // Note: Some of dd's functionality depends on whether the output is actually a file. This breaks the Output<Write> abstraction,
// and should be fixed in the future. // and should be fixed in the future.
fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usize, usize), Box<dyn Error>> fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usize, usize), Box<dyn Error>>
{ {
let mut bytes_in = 0; let mut bytes_in = 0;
let mut bytes_out = 0; let mut bytes_out = 0;
let gcf = gcf(i.ibs, o.obs);
let buf_size = (i.ibs/gcf)*(o.obs/gcf);
let prog_tx = if i.xfer_stats == StatusLevel::Progress let prog_tx = if i.xfer_stats == StatusLevel::Progress
{ {
@ -787,13 +852,13 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usi
loop loop
{ {
match read_write_helper(&mut i, &mut o)? match read_helper(&mut i, &mut o, buf_size)?
{ {
(0, _) => (0, _) =>
break, break,
(rlen, buf) => (rlen, buf) =>
{ {
let wlen = o.write(&buf)?; let wlen = o.write_blocks(buf)?;
bytes_in += rlen; bytes_in += rlen;
bytes_out += wlen; bytes_out += wlen;
@ -826,6 +891,8 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(usize, u
{ {
let mut bytes_in = 0; let mut bytes_in = 0;
let mut bytes_out = 0; let mut bytes_out = 0;
let gcf = gcf(i.ibs, o.obs);
let buf_size = (i.ibs/gcf)*(o.obs/gcf);
let prog_tx = if i.xfer_stats == StatusLevel::Progress let prog_tx = if i.xfer_stats == StatusLevel::Progress
{ {
@ -840,7 +907,7 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(usize, u
loop loop
{ {
match read_write_helper(&mut i, &mut o)? match read_helper(&mut i, &mut o, buf_size)?
{ {
(0, _) => (0, _) =>
break, break,

View file

@ -0,0 +1,97 @@
use super::*;
macro_rules! make_sync_test (
( $test_id:ident, $test_name:expr, $src:expr, $sync:expr, $ibs:expr, $obs:expr, $spec:expr ) =>
{
make_spec_test!($test_id,
$test_name,
Input {
src: $src,
non_ascii: false,
ibs: $ibs,
xfer_stats: StatusLevel::None,
cflags: IConvFlags {
ctable: None,
block: None,
unblock: None,
swab: false,
sync: $sync,
noerror: false,
},
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(),
obs: $obs,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
$spec,
format!("./test-resources/FAILED-{}.test", $test_name)
);
};
);
// Zeros
make_sync_test!(
zeros_4k_conv_sync_obs_gt_ibs,
"zeros_4k_conv_sync_obs_gt_ibs",
File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(),
Some(0u8),
521,
1031,
File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-zeros.spec").unwrap()
);
make_sync_test!(
zeros_4k_conv_sync_ibs_gt_obs,
"zeros_4k_conv_sync_ibs_gt_obs",
File::open("./test-resources/zeros-620f0b67a91f7f74151bc5be745b7110.test").unwrap(),
Some(0u8),
1031,
521,
File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-zeros.spec").unwrap()
);
// Deadbeef
make_sync_test!(
deadbeef_32k_conv_sync_obs_gt_ibs,
"deadbeef_32k_conv_sync_obs_gt_ibs",
File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
Some(0u8),
521,
1031,
File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-deadbeef.spec").unwrap()
);
make_sync_test!(
deadbeef_32k_conv_sync_ibs_gt_obs,
"deadbeef_32k_conv_sync_ibs_gt_obs",
File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
Some(0u8),
1031,
521,
File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-deadbeef.spec").unwrap()
);
// Random
make_sync_test!(
random_73k_test_bs_prime_obs_gt_ibs_sync,
"random-73k-test-bs-prime-obs-gt-ibs-sync",
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
Some(0u8),
521,
1031,
File::open("./test-resources/gnudd-conv-sync-ibs-521-obs-1031-random.spec").unwrap()
);
make_sync_test!(
random_73k_test_bs_prime_ibs_gt_obs_sync,
"random-73k-test-bs-prime-ibs-gt-obs-sync",
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
Some(0u8),
1031,
521,
File::open("./test-resources/gnudd-conv-sync-ibs-1031-obs-521-random.spec").unwrap()
);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long