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 LONG_HELP: &str = "";
const DEFAULT_INIT_BYTE: u8 = 0xDD;
const BUF_INIT_BYTE: u8 = 0xDD;
const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1;
@ -175,7 +175,7 @@ impl Input<io::Stdin>
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)?;
}
@ -250,16 +250,16 @@ impl<R: Read> Input<R>
{
/// Fills a given obs-sized buffer.
/// 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;
while base_idx < buf.len()
{
let low_idx = base_idx;
let up_idx = cmp::min(low_idx+self.ibs, buf.len()-1);
let next_blk = cmp::min(base_idx+self.ibs, buf.len());
let rlen = self.read(&mut buf[low_idx..=up_idx])?;
let rlen = self.read(&mut buf[base_idx..next_blk])?;
if rlen > 0
{
base_idx += rlen;
@ -270,29 +270,41 @@ impl<R: Read> Input<R>
}
}
buf.truncate(base_idx);
Ok(base_idx)
}
/// Fills a given obs-sized buffer.
/// 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 obs = buf.len();
let mut bytes_read = 0;
let mut base_idx = 0;
let mut rbytes = 0;
for n in 0..cmp::max(obs/ibs, 1) {
// fill an ibs-len slice from src
let rlen = self.read(&mut buf[n*ibs..(n+1)*ibs])?;
while base_idx < buf.len()
{
let next_blk = cmp::min(base_idx+self.ibs, buf.len());
let plen = next_blk - base_idx;
if rlen != 0 {
bytes_read += rlen;
} else {
let rlen = self.read(&mut buf[base_idx..next_blk])?;
if rlen < plen
{
let padding = vec![pad; plen-rlen];
buf.splice(base_idx+rlen..next_blk, padding.into_iter());
}
if rlen == 0
{
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
@ -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
/// Appends padding as specified by conv=block and cbs=N
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 -----------------------------------------------
#[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;
}
}
// ------------------------------------------------------------------
// ------------------------------------------------------------------
if is_fast_read(&i, &o)
{
// 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
{
// Read
let mut buf = vec![DEFAULT_INIT_BYTE; o.obs];
let rlen = if let Some(ch) = i.cflags.sync
{
i.fill_blocks(&mut buf)?
}
else
{
i.fill_consecutive(&mut buf)?
let mut buf = vec![BUF_INIT_BYTE; bsize];
let rlen = match i.cflags.sync {
Some(ch) =>
i.fill_blocks(&mut buf, o.obs, ch)?,
_ =>
i.fill_consecutive(&mut buf)?,
};
buf.truncate(rlen);
if rlen == 0
{
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);
}
if is_conv(&i) || is_block(&i) || is_unblock(&i)
{
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.
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,
// 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>>
{
let mut bytes_in = 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
{
@ -787,13 +852,13 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(usi
loop
{
match read_write_helper(&mut i, &mut o)?
match read_helper(&mut i, &mut o, buf_size)?
{
(0, _) =>
break,
(rlen, buf) =>
{
let wlen = o.write(&buf)?;
let wlen = o.write_blocks(buf)?;
bytes_in += rlen;
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_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
{
@ -840,7 +907,7 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(usize, u
loop
{
match read_write_helper(&mut i, &mut o)?
match read_helper(&mut i, &mut o, buf_size)?
{
(0, _) =>
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