Implements count=N

- Adds tests for count=READS and count=BYTES.
- Implements count logic for read count and bytes count limits.
This commit is contained in:
Tyler 2021-06-15 11:06:36 -07:00
parent fc110bb656
commit 8141919064
10 changed files with 341 additions and 98 deletions

View file

@ -66,6 +66,17 @@ struct ReadStat
reads_partial: u64,
records_truncated: u32,
}
impl std::ops::AddAssign for ReadStat
{
fn add_assign(&mut self, other: Self)
{
*self = Self {
reads_complete: self.reads_complete + other.reads_complete,
reads_partial: self.reads_partial + other.reads_partial,
records_truncated: self.records_truncated + other.records_truncated,
}
}
}
struct WriteStat
{
@ -73,6 +84,17 @@ struct WriteStat
writes_partial: u64,
bytes_total: u128,
}
impl std::ops::AddAssign for WriteStat
{
fn add_assign(&mut self, other: Self)
{
*self = Self {
writes_complete: self.writes_complete + other.writes_complete,
writes_partial: self.writes_partial + other.writes_partial,
bytes_total: self.bytes_total + other.bytes_total,
}
}
}
type Cbs = usize;
@ -150,6 +172,16 @@ pub enum StatusLevel
None,
}
/// The value of count=N
/// Defaults to Reads(N)
/// if iflag=count_bytes
/// then becomes Bytes(N)
pub enum CountType
{
Reads(usize),
Bytes(usize),
}
#[derive(Debug)]
enum InternalError
{
@ -180,6 +212,7 @@ struct Input<R: Read>
non_ascii: bool,
ibs: usize,
xfer_stats: Option<StatusLevel>,
count: Option<CountType>,
cflags: IConvFlags,
iflags: IFlags,
}
@ -194,12 +227,14 @@ impl Input<io::Stdin>
let cflags = parseargs::parse_conv_flag_input(matches)?;
let iflags = parseargs::parse_iflags(matches)?;
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
let count = parseargs::parse_count(&iflags, matches)?;
let mut i = Input {
src: io::stdin(),
non_ascii,
ibs,
xfer_stats,
count,
cflags,
iflags,
};
@ -225,6 +260,7 @@ impl Input<File>
let cflags = parseargs::parse_conv_flag_input(matches)?;
let iflags = parseargs::parse_iflags(matches)?;
let skip = parseargs::parse_skip_amt(&ibs, &iflags, matches)?;
let count = parseargs::parse_count(&iflags, matches)?;
if let Some(fname) = matches.opt_str("if")
{
@ -241,6 +277,7 @@ impl Input<File>
non_ascii,
ibs,
xfer_stats,
count,
cflags,
iflags,
};
@ -279,7 +316,7 @@ impl<R: Read> Read for Input<R>
impl<R: Read> Input<R>
{
/// Fills a given obs-sized buffer.
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read follows the previous one.
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> Result<ReadStat, Box<dyn Error>>
@ -317,7 +354,7 @@ impl<R: Read> Input<R>
})
}
/// Fills a given obs-sized buffer.
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// 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<ReadStat, Box<dyn Error>>
@ -375,23 +412,19 @@ impl<R: Read> Input<R>
}
/// Force-fills a buffer, ignoring zero-length reads which would otherwise be
/// interpreted as EOF. Does not continue after errors.
/// Note: This may never return.
fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result<(), Box<dyn Error>>
/// interpreted as EOF.
/// Note: This will not return unless the source (eventually) produces
/// enough bytes to meet target_len.
fn force_fill(&mut self, mut buf: &mut [u8], target_len: usize) -> Result<usize, Box<dyn Error>>
{
let mut total_len = 0;
loop
let mut base_idx = 0;
while base_idx < target_len
{
total_len += self.read(&mut buf)?;
if total_len == target_len
{
return Ok(());
}
}
base_idx += self.read(&mut buf[base_idx..target_len])?;
}
Ok(base_idx)
}
}
struct Output<W: Write>
@ -602,7 +635,7 @@ impl Output<File>
/// 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, rstats: &mut ReadStat) -> Vec<Vec<u8>>
fn block(buf: Vec<u8>, cbs: usize, rstat: &mut ReadStat) -> Vec<Vec<u8>>
{
let mut blocks = buf.split(| &e | e == '\n' as u8)
.fold(Vec::new(), | mut blocks, split |
@ -610,7 +643,7 @@ fn block(buf: Vec<u8>, cbs: usize, rstats: &mut ReadStat) -> Vec<Vec<u8>>
let mut split = split.to_vec();
if split.len() > cbs
{
rstats.records_truncated += 1;
rstat.records_truncated += 1;
}
split.resize(cbs, ' ' as u8);
blocks.push(split);
@ -683,7 +716,7 @@ fn unblock(buf: Vec<u8>, cbs: usize) -> Vec<u8>
.collect()
}
fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<R>, o: &Output<W>, rstats: &mut ReadStat) -> Result<Vec<u8>, Box<dyn Error>>
fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<R>, o: &Output<W>, rstat: &mut ReadStat) -> Result<Vec<u8>, Box<dyn Error>>
{
// Local Predicate Fns -------------------------------------------------
#[inline]
@ -737,7 +770,7 @@ fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<
{ // ascii input so perform the block first
let cbs = i.cflags.block.unwrap();
let mut blocks = block(buf, cbs, rstats);
let mut blocks = block(buf, cbs, rstat);
if let Some(ct) = i.cflags.ctable
{
@ -762,7 +795,7 @@ fn conv_block_unblock_helper<R: Read, W: Write>(mut buf: Vec<u8>, i: &mut Input<
apply_ct(&mut buf, &ct);
}
let blocks = block(buf, cbs, rstats)
let blocks = block(buf, cbs, rstat)
.into_iter()
.flatten()
.collect();
@ -860,7 +893,7 @@ fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: us
{
// Read
let mut buf = vec![BUF_INIT_BYTE; bsize];
let mut rstats = match i.cflags.sync
let mut rstat = match i.cflags.sync
{
Some(ch) =>
i.fill_blocks(&mut buf, o.obs, ch)?,
@ -868,9 +901,9 @@ fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: us
i.fill_consecutive(&mut buf)?,
};
// Return early if no data
if rstats.reads_complete == 0 && rstats.reads_partial == 0
if rstat.reads_complete == 0 && rstat.reads_partial == 0
{
return Ok((rstats,buf));
return Ok((rstat,buf));
}
// Perform any conv=x[,x...] options
@ -880,12 +913,12 @@ fn read_helper<R: Read, W: Write>(i: &mut Input<R>, o: &mut Output<W>, bsize: us
}
if is_conv(&i) || is_block(&i) || is_unblock(&i)
{
let buf = conv_block_unblock_helper(buf, i, o, &mut rstats)?;
Ok((rstats, buf))
let buf = conv_block_unblock_helper(buf, i, o, &mut rstat)?;
Ok((rstat, buf))
}
else
{
Ok((rstats, buf))
Ok((rstat, buf))
}
}
}
@ -935,13 +968,13 @@ fn print_xfer_stats(update: &ProgUpdate)
}
/// Generate a progress updater that tracks progress, receives updates, and TODO: responds to signals.
/// Generate a progress updater that tracks progress, receives updates, and responds to signals.
fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLevel>) -> impl Fn() -> ()
{
// --------------------------------------------------------------
fn posixly_correct() -> bool
{
!env::var("POSIXLY_CORRECT").is_err()
env::var("POSIXLY_CORRECT").is_ok()
}
// --------------------------------------------------------------
move || {
@ -951,7 +984,7 @@ fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLev
// TODO: SIGINFO seems to only exist for BSD (and therefore MACOS)
// I will probably want put this behind a feature-gate and may need to pass the value to handle as my own constant.
// This may involve some finagling with the library.
// This may involve some finagling with the signals library.
// see -> https://unix.stackexchange.com/questions/179481/siginfo-on-gnu-linux-arch-linux-missing
// if let Err(e) = signal_hook::flag::register_usize(signal::SIGINFO, sigval.clone(), signal::SIGINFO as usize)
// {
@ -980,12 +1013,9 @@ fn gen_prog_updater(rx: mpsc::Receiver<ProgUpdate>, xfer_stats: Option<StatusLev
{
update
},
(Err(e), _) =>
{
debug_println!("Internal dd Warning: Error in progress update thread\n\t{}", e);
continue;
},
(Err(_), _) =>
// recv only fails permenantly
break,
};
// Handle signals
match sigval.load(Ordering::Relaxed)
@ -1014,17 +1044,64 @@ fn calc_bsize(ibs: usize, obs: usize) -> usize
lcm
}
/// Calculate the buffer size appropriate for this loop iteration, respecting
/// a count=N if present.
fn calc_loop_bsize(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteStat, ibs: usize, ideal_bsize: usize) -> usize
{
match count
{
Some(CountType::Reads(rmax)) =>
{
let rmax: u64 = (*rmax).try_into().unwrap();
let rsofar = rstat.reads_complete + rstat.reads_partial;
let rremain: usize = (rmax - rsofar).try_into().unwrap();
cmp::min(ideal_bsize, rremain*ibs)
},
Some(CountType::Bytes(bmax)) =>
{
let bmax: u128 = (*bmax).try_into().unwrap();
let bremain: usize = (bmax - wstat.bytes_total).try_into().unwrap();
cmp::min(ideal_bsize, bremain)
},
None =>
ideal_bsize,
}
}
/// Decide if the current progress is below a count=N limit or return
/// true if no such limit is set.
fn below_count_limit(count: &Option<CountType>, rstat: &ReadStat, wstat: &WriteStat) -> bool
{
match count
{
Some(CountType::Reads(n)) =>
{
let n = (*n).try_into().unwrap();
// debug_assert!(rstat.reads_complete + rstat.reads_partial >= n);
rstat.reads_complete + rstat.reads_partial <= n
},
Some(CountType::Bytes(n)) =>
{
let n = (*n).try_into().unwrap();
// debug_assert!(wstat.bytes_total >= n);
wstat.bytes_total <= n
},
None =>
true,
}
}
/// 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<(), Box<dyn Error>>
{
let mut rstats = ReadStat {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
records_truncated: 0,
};
let mut wstats = WriteStat {
let mut wstat = WriteStat {
writes_complete: 0,
writes_partial: 0,
bytes_total: 0,
@ -1038,37 +1115,30 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
tx
};
loop
while below_count_limit(&i.count, &rstat, &wstat)
{
// Read/Write
match read_helper(&mut i, &mut o, bsize)?
let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize);
match read_helper(&mut i, &mut o, loop_bsize)?
{
(ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) =>
break,
(rstat_update, buf) =>
{
let wstats_update = o.write_blocks(buf)?;
let wstat_update = o.write_blocks(buf)?;
rstats = ReadStat {
reads_complete: rstats.reads_complete + rstat_update.reads_complete,
reads_partial: rstats.reads_partial + rstat_update.reads_partial,
records_truncated: rstats.records_truncated + rstat_update.records_truncated,
};
wstats = WriteStat {
writes_complete: wstats.writes_complete + wstats_update.writes_complete,
writes_partial: wstats.writes_partial + wstats_update.writes_partial,
bytes_total: wstats.bytes_total + wstats_update.bytes_total,
};
rstat += rstat_update;
wstat += wstat_update;
},
};
// Update Prog
prog_tx.send(ProgUpdate {
reads_complete: rstats.reads_complete,
reads_partial: rstats.reads_partial,
writes_complete: wstats.writes_complete,
writes_partial: wstats.writes_partial,
bytes_total: wstats.bytes_total,
records_truncated: rstats.records_truncated,
reads_complete: rstat.reads_complete,
reads_partial: rstat.reads_partial,
writes_complete: wstat.writes_complete,
writes_partial: wstat.writes_partial,
bytes_total: wstat.bytes_total,
records_truncated: rstat.records_truncated,
duration: start.elapsed(),
})?;
}
@ -1088,12 +1158,12 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
Some(StatusLevel::None) => {},
_ =>
print_xfer_stats(&ProgUpdate {
reads_complete: rstats.reads_complete,
reads_partial: rstats.reads_partial,
writes_complete: wstats.writes_complete,
writes_partial: wstats.writes_partial,
bytes_total: wstats.bytes_total,
records_truncated: rstats.records_truncated,
reads_complete: rstat.reads_complete,
reads_partial: rstat.reads_partial,
writes_complete: wstat.writes_complete,
writes_partial: wstat.writes_partial,
bytes_total: wstat.bytes_total,
records_truncated: rstat.records_truncated,
duration: start.elapsed(),
}),
}
@ -1105,12 +1175,12 @@ fn dd_stdout<R: Read>(mut i: Input<R>, mut o: Output<io::Stdout>) -> Result<(),
// and should be fixed in the future.
fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<dyn Error>>
{
let mut rstats = ReadStat {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
records_truncated: 0,
};
let mut wstats = WriteStat {
let mut wstat = WriteStat {
writes_complete: 0,
writes_partial: 0,
bytes_total: 0,
@ -1124,37 +1194,30 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
tx
};
loop
while below_count_limit(&i.count, &rstat, &wstat)
{
// Read/Write
match read_helper(&mut i, &mut o, bsize)?
let loop_bsize = calc_loop_bsize(&i.count, &rstat, &wstat, i.ibs, bsize);
match read_helper(&mut i, &mut o, loop_bsize)?
{
(ReadStat { reads_complete: 0, reads_partial: 0, .. }, _) =>
break,
(rstat_update, buf) =>
{
let wstats_update = o.write_blocks(buf)?;
let wstat_update = o.write_blocks(buf)?;
rstats = ReadStat {
reads_complete: rstats.reads_complete + rstat_update.reads_complete,
reads_partial: rstats.reads_partial + rstat_update.reads_partial,
records_truncated: rstats.records_truncated + rstat_update.records_truncated,
};
wstats = WriteStat {
writes_complete: wstats.writes_complete + wstats_update.writes_complete,
writes_partial: wstats.writes_partial + wstats_update.writes_partial,
bytes_total: wstats.bytes_total + wstats_update.bytes_total,
};
rstat += rstat_update;
wstat += wstat_update;
},
};
// Update Prog
prog_tx.send(ProgUpdate {
reads_complete: rstats.reads_complete,
reads_partial: rstats.reads_partial,
writes_complete: wstats.writes_complete,
writes_partial: wstats.writes_partial,
bytes_total: wstats.bytes_total,
records_truncated: rstats.records_truncated,
reads_complete: rstat.reads_complete,
reads_partial: rstat.reads_partial,
writes_complete: wstat.writes_complete,
writes_partial: wstat.writes_partial,
bytes_total: wstat.bytes_total,
records_truncated: rstat.records_truncated,
duration: start.elapsed(),
})?;
}
@ -1174,12 +1237,12 @@ fn dd_fileout<R: Read>(mut i: Input<R>, mut o: Output<File>) -> Result<(), Box<d
Some(StatusLevel::None) => {},
_ =>
print_xfer_stats(&ProgUpdate {
reads_complete: rstats.reads_complete,
reads_partial: rstats.reads_partial,
writes_complete: wstats.writes_complete,
writes_partial: wstats.writes_partial,
bytes_total: wstats.bytes_total,
records_truncated: rstats.records_truncated,
reads_complete: rstat.reads_complete,
reads_partial: rstat.reads_partial,
writes_complete: wstat.writes_complete,
writes_partial: wstat.writes_partial,
bytes_total: wstat.bytes_total,
records_truncated: rstat.records_truncated,
duration: start.elapsed(),
}),
}

View file

@ -24,6 +24,7 @@ macro_rules! make_block_test (
non_ascii: false,
ibs: 512,
xfer_stats: None,
count: None,
cflags: IConvFlags {
ctable: None,
block: $block,
@ -56,6 +57,7 @@ macro_rules! make_unblock_test (
non_ascii: false,
ibs: 512,
xfer_stats: None,
count: None,
cflags: IConvFlags {
ctable: None,
block: None,

View file

@ -24,6 +24,7 @@ macro_rules! make_sync_test (
non_ascii: false,
ibs: $ibs,
xfer_stats: None,
count: None,
cflags: IConvFlags {
ctable: None,
block: None,

View file

@ -10,6 +10,7 @@ macro_rules! make_conv_test (
non_ascii: false,
ibs: 512,
xfer_stats: None,
count: None,
cflags: icf!($ctable),
iflags: DEFAULT_IFLAGS,
},
@ -35,6 +36,7 @@ macro_rules! make_icf_test (
non_ascii: false,
ibs: 512,
xfer_stats: None,
count: None,
cflags: $icf,
iflags: DEFAULT_IFLAGS,
},
@ -138,6 +140,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test()
non_ascii: false,
ibs: 128,
xfer_stats: None,
count: None,
cflags: icf!(Some(&ASCII_TO_EBCDIC)),
iflags: DEFAULT_IFLAGS,
};
@ -160,6 +163,7 @@ fn all_valid_ascii_ebcdic_ascii_roundtrip_conv_test()
non_ascii: false,
ibs: 256,
xfer_stats: None,
count: None,
cflags: icf!(Some(&EBCDIC_TO_ASCII)),
iflags: DEFAULT_IFLAGS,
};

View file

@ -95,6 +95,7 @@ macro_rules! make_spec_test (
non_ascii: false,
ibs: 512,
xfer_stats: None,
count: None,
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
@ -116,11 +117,13 @@ macro_rules! make_spec_test (
dd_fileout($i,$o).unwrap();
let res = File::open($tmp_fname).unwrap();
// Check test file isn't empty (unless spec file is too)
assert_eq!(res.metadata().unwrap().len(), $spec.metadata().unwrap().len());
let spec = BufReader::new($spec);
let res = BufReader::new(res);
// Check all bytes match
for (b_res, b_spec) in res.bytes().zip(spec.bytes())
{
assert_eq!(b_res.unwrap(),

View file

@ -1,5 +1,25 @@
use super::*;
const DST_PLACEHOLDER: Vec<u8> = Vec::new();
macro_rules! make_io_test (
( $test_id:ident, $test_name:expr, $i:expr, $o:expr, $spec:expr ) =>
{
make_spec_test!($test_id,
$test_name,
$i,
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", $test_name)).unwrap(),
obs: $o.obs,
cflags: $o.cflags,
oflags: $o.oflags,
},
$spec,
format!("./test-resources/FAILED-{}.test", $test_name)
);
};
);
make_spec_test!(
zeros_4k_test,
"zeros-4k",
@ -24,7 +44,7 @@ make_spec_test!(
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap()
);
make_spec_test!(
make_io_test!(
random_73k_test_not_a_multiple_obs_gt_ibs,
"random-73k-not-a-multiple-obs-gt-ibs",
Input {
@ -32,20 +52,20 @@ make_spec_test!(
non_ascii: false,
ibs: 521,
xfer_stats: None,
count: None,
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs")).unwrap(),
dst: DST_PLACEHOLDER,
obs: 1031,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
format!("./test-resources/FAILED-{}.test", "random-73k-not-a-multiple-obs-gt-ibs")
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap()
);
make_spec_test!(
make_io_test!(
random_73k_test_obs_lt_not_a_multiple_ibs,
"random-73k-obs-lt-not-a-multiple-ibs",
Input {
@ -53,17 +73,143 @@ make_spec_test!(
non_ascii: false,
ibs: 1031,
xfer_stats: None,
count: None,
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: File::create(format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs")).unwrap(),
dst: DST_PLACEHOLDER,
obs: 521,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
format!("./test-resources/FAILED-{}.test", "random-73k-obs-lt-not-a-multiple-ibs")
File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap()
);
make_io_test!(
deadbeef_all_32k_test_count_reads,
"deadbeef_all_32k_test_count_reads",
Input {
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
non_ascii: false,
ibs: 1024,
xfer_stats: None,
count: Some(CountType::Reads(32)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1024,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap()
);
make_io_test!(
deadbeef_all_32k_test_count_bytes,
"deadbeef_all_32k_test_count_bytes",
Input {
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
non_ascii: false,
ibs: 531,
xfer_stats: None,
count: Some(CountType::Bytes(32*1024)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1031,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap()
);
make_io_test!(
deadbeef_32k_to_16k_test_count_reads,
"deadbeef_32k_test_count_reads",
Input {
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
non_ascii: false,
ibs: 1024,
xfer_stats: None,
count: Some(CountType::Reads(16)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1031,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/gnudd-deadbeef-first-16k.spec").unwrap()
);
make_io_test!(
deadbeef_32k_to_12345_test_count_bytes,
"deadbeef_32k_to_12345_test_count_bytes",
Input {
src: File::open("./test-resources/deadbeef-18d99661a1de1fc9af21b0ec2cd67ba3.test").unwrap(),
non_ascii: false,
ibs: 531,
xfer_stats: None,
count: Some(CountType::Bytes(12345)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1031,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/gnudd-deadbeef-first-12345.spec").unwrap()
);
make_io_test!(
random_73k_test_count_reads,
"random-73k-test-count-reads",
Input {
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
non_ascii: false,
ibs: 1024,
xfer_stats: None,
count: Some(CountType::Reads(32)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1024,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/gnudd-random-first-32k.spec").unwrap()
);
make_io_test!(
random_73k_test_count_bytes,
"random-73k-test-count-bytes",
Input {
src: File::open("./test-resources/random-5828891cb1230748e146f34223bbd3b5.test").unwrap(),
non_ascii: false,
ibs: 521,
xfer_stats: None,
count: Some(CountType::Bytes(32*1024)),
cflags: icf!(),
iflags: DEFAULT_IFLAGS,
},
Output {
dst: DST_PLACEHOLDER,
obs: 1031,
cflags: DEFAULT_CFO,
oflags: DEFAULT_OFLAGS,
},
File::open("./test-resources/gnudd-random-first-32k.spec").unwrap()
);
// Test internal buffer size fn

View file

@ -3,6 +3,7 @@ mod unit_tests;
use crate::conversion_tables::*;
use crate::{
CountType,
IConvFlags, OConvFlags,
StatusLevel,
};
@ -759,6 +760,27 @@ pub fn parse_seek_amt(obs: &usize, oflags: &OFlags, matches: &getopts::Matches)
}
}
/// Parse the value of count=N and the type of N implied by iflags
pub fn parse_count(iflags: &IFlags, matches: &getopts::Matches) -> Result<Option<CountType>, ParseError>
{
if let Some(amt) = matches.opt_str("count")
{
let n = parse_bytes_with_opt_multiplier(amt)?;
if iflags.count_bytes
{
Ok(Some(CountType::Bytes(n)))
}
else
{
Ok(Some(CountType::Reads(n)))
}
}
else
{
Ok(None)
}
}
/// Parse whether the args indicate the input is not ascii
pub fn parse_input_non_ascii(matches: &getopts::Matches) -> Result<bool, ParseError>
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.