dd_lib/io/
copy.rs

1use super::{ConvertSlice, ErrHandler, Reader};
2
3#[allow(unused_imports)] // for documentatino
4use opts;
5use results::{Result, Success};
6use std::{
7    cmp::Ordering,
8    io::{BufRead, Read, Write},
9    time::SystemTime,
10};
11
12/// Copy bytes from R to W, converting according to C. See
13/// [opts::Mode::Standard]
14pub fn standard<R, W, C, E>(mut r: Reader<R>, w: &mut W, c: C, mut e: ErrHandler<E>) -> Result<Success>
15where
16    R: Read,
17    W: Write,
18    C: ConvertSlice<u8>,
19    E: Write,
20{
21    let start = SystemTime::now();
22    let mut bytes = 0;
23    loop {
24        let converted = match e.handle(r.fill_buf())? {
25            Some(ref buf) if buf.len() == 0 => return Ok(Success::Bytes { bytes, start }),
26            Some(buf) => c.convert_copy(buf),
27            None => continue,
28        };
29
30        e.handle(w.write_all(&converted))?;
31        r.consume(converted.len());
32        bytes += converted.len();
33        e.report_status_standard(bytes, &start)?
34    }
35}
36pub fn bytes<R, W, C, E>(mut r: R, w: &mut W, c: &C, mut e: ErrHandler<E>, max_bytes: usize) -> Result<Success>
37where
38    R: BufRead,
39    W: Write,
40    C: ConvertSlice<u8>,
41    E: Write,
42{
43    let start = SystemTime::now();
44    let mut bytes = 0;
45    loop {
46        let converted = match e.handle(r.fill_buf())? {
47            None => continue,
48            Some(buf) => match usize::min(max_bytes.saturating_sub(bytes), buf.len()) {
49                0 => return Ok(Success::Bytes { bytes, start }),
50                n => c.convert_copy(&buf[..n]),
51            },
52        };
53
54        e.handle(w.write_all(&converted))?;
55        r.consume(converted.len());
56        bytes += converted.len();
57    }
58}
59
60/// Copy a series of newline or EOF-terminated blocks from R to W, converting
61/// according to C. [opts::Mode::Unblock] treats the input as a sequence of '\n'
62/// or EOF-terminated variable length records independent of input and output
63/// block boundaries.  Any trailing '\n' is discarded. Each input record is
64/// converted to a fixed length output record where the length is specified by
65/// the [conversion_block_size][opts] operand.  Input records shorter than the
66/// conversion record size are padded with spaces. Input records longer than the
67/// conversion record size are truncated.  The number of truncated input
68/// records, if any, are reported to the standard error output at the completion
69/// of the copy.
70pub fn block<R, W, C, E>(r: R, w: &mut W, c: C, mut e: ErrHandler<E>, block_size: usize) -> Result<Success>
71where
72    R: BufRead,
73    W: Write,
74    C: ConvertSlice<u8>,
75    E: Write,
76{
77    let (mut lines, mut padded, mut truncated): (usize, usize, usize) = (0, 0, 0);
78    let (mut buf, mut bytes): (Vec<u8>, std::io::Bytes<_>) = (Vec::with_capacity(block_size), r.bytes());
79    let start = SystemTime::now();
80    while {
81        if let Some(b) = skip_trailing_while_match(&mut bytes, &b'\n')? {
82            buf.push(b)
83        };
84        e.handle(fill_buf_until_match(&mut bytes, &mut buf, b'\n'))?;
85        buf.len() != 0
86    } {
87        lines += 1;
88        match buf.len().cmp(&block_size) {
89            Ordering::Greater => truncated += 1,
90            Ordering::Less => padded += 1,
91            Ordering::Equal => {},
92        }
93
94        c.convert_slice(&mut buf);
95        buf.resize(block_size, b' ');
96        w.write_all(&buf)?;
97        e.report_status_block(lines, &start);
98        buf.clear();
99    }
100    Ok(Success::Block {
101        start,
102        lines,
103        truncated,
104        padded,
105        block_size,
106    })
107}
108
109/// Copy a series of fixed-length records from R to W, converting according to
110/// C. [opts::Mode::Unblock] treats the input as a sequence of fixed length
111/// records independent of input and output block boundaries.  The length of the
112/// input records is specified by the [conversion_block_size][opts] option
113/// option. Any trailing space characters are discarded and a newline character
114/// is appended.
115pub fn unblock<R, W, C, E>(r: R, w: &mut W, c: C, mut e: ErrHandler<E>, block_size: usize) -> Result<Success>
116where
117    R: BufRead,
118    W: Write,
119    C: ConvertSlice<u8>,
120    E: Write,
121{
122    let start = SystemTime::now();
123    let mut buf: Vec<u8> = Vec::with_capacity(block_size + 1); // account for newline
124    let mut bytes = r.bytes();
125    let mut blocks = 0;
126    while {
127        if let Some(Some(b)) = e.handle(skip_trailing_while_match(&mut bytes, &b' '))? {
128            buf.push(b)
129        };
130        fill_buf_to(&mut bytes, &mut buf, block_size)?;
131        buf.len() != 0
132    } {
133        buf.push(b'\n');
134
135        c.convert_slice(&mut buf);
136        e.handle(w.write_all(&mut buf))?;
137
138        blocks += 1;
139        e.report_status_unblock(blocks, &start);
140        buf.clear();
141    }
142    Ok(Success::Unblock {
143        blocks,
144        block_size,
145        start,
146    })
147}
148
149/// skip items while they  match `unwanted_byte`, returning the first byte
150/// (if any) that doesn't match
151
152fn skip_trailing_while_match<I, T>(it: &mut I, unwanted: &T) -> std::io::Result<Option<T>>
153where
154    I: Iterator<Item = ::std::io::Result<T>>,
155    T: PartialEq,
156{
157    loop {
158        match it.next() {
159            Some(Ok(ref t)) if t == unwanted => {},
160            Some(t) => return Ok(Some(t?)),
161            None => return Ok(None),
162        }
163    }
164}
165
166/// fill the buffer from `it` until and including the first item that matches
167/// `want`
168/// ```
169/// # use dd_lib::io::copy::fill_buf_until_match;
170/// let mut it = vec![1, 1, 2].into_iter().map(|t| Ok(t));
171/// let mut buf: Vec<u8> = vec![3];
172/// fill_buf_until_match(&mut it, &mut buf, 3).unwrap();
173/// assert_eq!(vec![3, 1, 1, 2], buf);
174/// ```
175pub fn fill_buf_until_match<I, T>(it: &mut I, buf: &mut Vec<T>, want: T) -> std::io::Result<()>
176where
177    I: Iterator<Item = std::io::Result<T>>,
178    T: PartialEq,
179{
180    loop {
181        match it.next() {
182            Some(Ok(ref t)) if t == &want => return Ok(()),
183            Some(Ok(t)) => buf.push(t),
184            Some(Err(err)) => return Err(err),
185            None => return Ok(()),
186        }
187    }
188}
189
190/// fill the buffer with unwrapped elements from `it` until it has len `cap`
191/// ```
192/// # use dd_lib::io::copy::fill_buf_to;
193/// let mut fours = vec![4; 80].into_iter().map(|t| Ok(t));
194/// let mut buf = vec![1_u8];
195/// fill_buf_to(&mut fours, &mut buf, 5).unwrap();
196///
197/// assert_eq!(vec![1, 4, 4, 4, 4], buf);
198/// let remainder: Vec<u8> = fours.map(Result::unwrap).collect();
199/// assert_eq!(vec![4; 80 - 4], remainder)
200/// ```
201pub fn fill_buf_to<I, T>(it: &mut I, buf: &mut Vec<T>, cap: usize) -> std::io::Result<()>
202where
203    I: Iterator<Item = std::io::Result<T>>,
204{
205    for _ in buf.len()..cap {
206        match it.next() {
207            Some(Ok(t)) => buf.push(t),
208            Some(Err(err)) => return Err(err),
209            None => break,
210        };
211    }
212    Ok(())
213}