use super::{super::results::elapsed, conv::ConvertSlice, read::Reader};
use opts::number::units;
use results::{Result, Success};
use std::{
cmp::Ordering,
io::{BufRead, Read, Write},
time::SystemTime,
};
const BYTE_THRESHOLD_FOR_REPORT: usize = 5 * units::MB;
const BLOCK_THRESHOLD_FOR_REPORT: usize = 25;
const LINE_THRESHOLD_FOR_REPORT: usize = 20;
pub fn standard<R, W, C, E>(mut r: Reader<R>, w: &mut W, c: C, err: &mut Option<E>) -> Result<Success>
where
R: Read,
W: Write,
C: ConvertSlice<u8>,
E: Write,
{
let start = SystemTime::now();
let mut converted: Vec<u8>;
let mut bytes = 0;
let mut prev_report = 0;
while {
converted = c.convert_copy(&mut r.fill_buf()?);
converted.len() != 0
} {
w.write_all(&converted)?;
r.consume(converted.len());
bytes += converted.len();
if let Some(err) = err {
if bytes.saturating_sub(prev_report) > BYTE_THRESHOLD_FOR_REPORT {
prev_report = bytes;
writeln!(
err,
"dd in progress: elapsed: {}: wrote {} bytes",
elapsed(&start),
bytes
);
};
};
}
Ok(Success::Bytes { bytes, start })
}
pub fn bytes<R, W, C, E>(mut r: R, w: &mut W, c: &C, err: &mut Option<E>, max_bytes: usize) -> Result<Success>
where
R: BufRead,
W: Write,
C: ConvertSlice<u8>,
E: Write,
{
let start = SystemTime::now();
let mut converted: Vec<u8>;
let mut bytes: usize = 0;
let mut read;
let mut prev_report = 0;
while {
let slice = r.fill_buf()?;
read = usize::min(max_bytes - bytes, slice.len());
converted = c.convert_copy(&slice[..read]);
read != 0
} {
w.write_all(&converted)?;
r.consume(read);
bytes += read;
if let Some(err) = err {
if bytes.saturating_sub(prev_report) > BYTE_THRESHOLD_FOR_REPORT {
prev_report = bytes;
writeln!(
err,
"dd in progress: elapsed: {}: wrote {} bytes",
elapsed(&start),
bytes
);
};
};
}
Ok(Success::Bytes { bytes, start })
}
pub fn block<R, W, C, E>(r: R, w: &mut W, c: C, err: &mut Option<E>, block_size: usize) -> Result<Success>
where
R: BufRead,
W: Write,
C: ConvertSlice<u8>,
E: Write,
{
let (mut lines, mut padded, mut truncated): (usize, usize, usize) = (0, 0, 0);
let (mut buf, mut bytes): (Vec<u8>, std::io::Bytes<_>) = (Vec::with_capacity(block_size), r.bytes());
let start = SystemTime::now();
while {
if let Some(b) = skip_trailing_while_match(&mut bytes, b'\n')? {
buf.push(b)
};
buf = fill_buf_until_match(&mut bytes, buf, b'\n')?;
buf.len() != 0
} {
lines += 1;
match buf.len().cmp(&block_size) {
Ordering::Greater => truncated += 1,
Ordering::Less => padded += 1,
Ordering::Equal => {},
}
c.convert_slice(&mut buf);
buf.resize(block_size, b' ');
w.write_all(&buf)?;
if let Some(err) = err {
if lines % LINE_THRESHOLD_FOR_REPORT == 0 {
writeln!(
err,
"dd in progress: unblock: elapsed: {}, wrote {} lines",
elapsed(&start),
lines
);
}
}
buf.clear();
}
Ok(Success::Block {
start,
lines,
truncated,
padded,
block_size,
})
}
pub fn unblock<R, W, C, E>(r: R, w: &mut W, c: C, err: &mut Option<E>, block_size: usize) -> Result<Success>
where
R: BufRead,
W: Write,
C: ConvertSlice<u8>,
E: Write,
{
let start = SystemTime::now();
let mut buf: Vec<u8> = Vec::with_capacity(block_size + 1); let mut bytes = r.bytes();
let mut blocks = 0;
while {
if let Some(b) = skip_trailing_while_match(&mut bytes, b' ')? {
buf.push(b)
};
buf = fill_buf_to(&mut bytes, buf, block_size)?;
buf.len() != 0
} {
buf.push(b'\n');
c.convert_slice(&mut buf);
w.write_all(&mut buf)?;
blocks += 1;
if let Some(err) = err {
if blocks % BLOCK_THRESHOLD_FOR_REPORT == 0 {
writeln!(
err,
"dd in progress: unblock: elapsed: {}, wrote {} fixed-length blocks",
elapsed(&start),
blocks
);
}
}
buf.clear();
}
Ok(Success::Unblock {
blocks,
block_size,
start,
})
}
#[inline]
fn skip_trailing_while_match<I, T>(it: &mut I, unwanted: T) -> Result<Option<T>>
where
I: Iterator<Item = ::std::io::Result<T>>,
T: PartialEq,
{
while let Some(res) = it.next() {
let t = res?;
if t != unwanted {
return Ok(Some(t));
}
}
return Ok(None);
}
#[inline]
fn fill_buf_until_match<I, T>(it: &mut I, mut buf: Vec<T>, want: T) -> Result<Vec<T>>
where
I: Iterator<Item = ::std::io::Result<T>>,
T: PartialEq,
{
while let Some(res) = it.next() {
let t = res?;
if t == want {
break;
}
buf.push(t)
}
Ok(buf)
}
#[inline]
fn fill_buf_to<I, T>(it: &mut I, mut buf: Vec<T>, cap: usize) -> Result<Vec<T>>
where
I: Iterator<Item = ::std::io::Result<T>>,
{
while buf.len() < cap {
if let Some(res) = it.next() {
buf.push(res?);
} else {
break;
}
}
Ok(buf)
}