1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::fmt::Write as FmtWrite;
use std::io::{self, BufRead, Read, Write};
use std::time::{Duration, Instant};

const MAX_LINE: u64 = 1024 * 8;

/// Convert a `time::Duration` to a formatted `String` such as
/// "15h4m5.42s" or "424.2ms", or "" for a zero duration.
pub fn duration_to_human(d: &Duration) -> String {
    let ts = d.as_secs();
    let ns = d.subsec_nanos();

    let mut ret = String::with_capacity(10);

    if ts > 0 {
        let mut s = ts;
        let mut cs = (f64::from(ns) / 10_000_000_f64).round() as u64;
        if cs == 100 {
            // round up to the nearest centisecond
            s += 1;
            cs = 0;
        }

        if ts >= 86400 {
            write!(ret, "{}d", s / 86400).unwrap();
            s %= 86400;
        }

        if ts >= 3600 {
            write!(ret, "{}h", s / 3600).unwrap();
            s %= 3600;
        }

        if ts >= 60 {
            write!(ret, "{}m", s / 60).unwrap();
            s %= 60
        }

        write!(ret, "{}.{:02}s", s, cs).unwrap();
    } else if ns > 100_000 {
        write!(ret, "{:.1}ms", f64::from(ns) / 1_000_000_f64).unwrap();
    } else if ns > 100 {
        write!(ret, "{:.1}μs", f64::from(ns) / 1_000_f64).unwrap();
    }

    ret
}

/// Copy each line from `input` to `output`, prepending the output line with
/// elapsed time since `start` and since the previous line, separated by `separator`
/// until EOF or IO error.
///
/// Returns the number of bytes read from `input` on success.
pub fn line_timing_copy<R: io::Read, W: io::Write>(
    input: &mut R,
    output: &mut W,
    separator: char,
    start: &Instant,
) -> io::Result<u64> {
    let mut input = io::BufReader::new(input);
    let mut output = io::BufWriter::new(output);

    let mut buf = Vec::with_capacity(256);
    let mut last = Instant::now();
    let mut run_on = false;
    let mut n = 0_u64;

    while input.by_ref().take(MAX_LINE).read_until(b'\n', &mut buf)? > 0 {
        n += buf.len() as u64;
        let since_last = last.elapsed();
        let since_start = start.elapsed();
        last = Instant::now();

        if !run_on {
            write!(
                output,
                "{:>8} {:>8} {} ",
                duration_to_human(&since_start),
                duration_to_human(&since_last),
                separator
            )?;
        }

        run_on = buf.last().expect("buf can't be empty") != &b'\n';

        output.write_all(&buf)?;
        output.flush()?;
        buf.clear();
    }

    Ok(n)
}