linereader 0.4.0

An efficient buffered line reader.
Documentation
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::str;
use std::time::{Duration, Instant};

extern crate linereader;
use linereader::LineReader;
extern crate memchr;

use memchr::Memchr;

const BUFFER_SIZE: usize = 1024 * 64;

struct Report {
    lines: u64,
    bytes: u64,
}

impl Report {
    fn new(filename: &str) -> Self {
        let mut infile = File::open(filename).expect("open");

        let mut lines = 0_u64;
        let mut bytes = 0_u64;
        let mut buf = [0; BUFFER_SIZE];
        while let Ok(r) = infile.read(&mut buf[..]) {
            if r == 0 {
                break;
            }
            bytes += r as u64;
            lines += Memchr::new(b'\n', &buf[..r]).count() as u64;
        }

        println!("File: {}, bytes: {}, lines: {}", filename, bytes, lines);

        println!(
            "| {:16} | {:^7} | {:^11} | {:^13} |",
            "Method", "Time", "Lines/sec", "Bandwidth"
        );
        println!("|------------------|--------:|------------:|--------------:|");
        Self { lines, bytes }
    }

    fn report(&self, name: &str, bytes: Option<u64>, lines: Option<u64>, elapsed: Duration) {
        if let Some(bytes) = bytes {
            if bytes != self.bytes {
                println!("Warning: expected {} bytes, read {}", self.bytes, bytes);
            }
        }

        if let Some(lines) = lines {
            if lines != self.lines {
                println!("Warning: expected {} lines, read {}", self.lines, lines);
            }
        }

        let elapsed =
            (elapsed.as_secs() as f64) + (f64::from(elapsed.subsec_nanos()) / 1_000_000_000.0);
        println!(
            "| {:16} | {: >6.2}s | {:>9.0}/s | {:>8.2} MB/s |",
            name,
            elapsed,
            (self.lines as f64) / elapsed,
            ((self.bytes as f64) / elapsed) / (1024.0 * 1024.0)
        );
    }
}

fn try_baseline(report: &Report, filename: &str) {
    let mut infile = File::open(filename).expect("open");

    let start = Instant::now();
    let mut bytes = 0_u64;

    let mut buf = [0; BUFFER_SIZE];
    while let Ok(r) = infile.read(&mut buf[..]) {
        if r == 0 {
            break;
        }
        bytes += r as u64;
    }

    report.report("read()", Some(bytes), None, start.elapsed());
}

fn try_linereader_batch(report: &Report, filename: &str) {
    let infile = File::open(filename).expect("open");

    let mut reader = LineReader::with_capacity(BUFFER_SIZE, infile);

    let start = Instant::now();
    let mut bytes = 0_u64;
    while let Some(batch) = reader.next_batch() {
        let batch = batch.unwrap();
        bytes += batch.len() as u64;
    }

    report.report("LR::next_batch()", Some(bytes), None, start.elapsed());
}

fn try_linereader(report: &Report, filename: &str) {
    let infile = File::open(filename).expect("open");

    let mut reader = LineReader::with_capacity(BUFFER_SIZE, infile);

    let start = Instant::now();
    let mut lines = 0_u64;
    let mut bytes = 0_u64;
    while let Some(line) = reader.next_line() {
        bytes += line.unwrap().len() as u64;
        lines += 1;
    }

    report.report("LR::next_line()", Some(bytes), Some(lines), start.elapsed());
}

fn try_read_until(report: &Report, filename: &str) {
    let infile = File::open(filename).expect("open");
    let mut infile = BufReader::with_capacity(BUFFER_SIZE, infile);

    let start = Instant::now();
    let mut lines = 0_u64;
    let mut bytes = 0_u64;
    let mut line: Vec<u8> = Vec::with_capacity(128);
    while infile.read_until(b'\n', &mut line).unwrap_or(0) > 0 {
        bytes += line.len() as u64;
        lines += 1;
        line.clear();
    }

    report.report("read_until()", Some(bytes), Some(lines), start.elapsed());
}

fn try_read_line(report: &Report, filename: &str) {
    let infile = File::open(filename).expect("open");
    let mut infile = BufReader::with_capacity(BUFFER_SIZE, infile);

    let start = Instant::now();
    let mut lines = 0_u64;
    let mut bytes = 0_u64;
    let mut line = String::new();
    while infile.read_line(&mut line).unwrap_or(0) > 0 {
        bytes += line.len() as u64;
        lines += 1;
        line.clear();
    }

    report.report("read_line()", Some(bytes), Some(lines), start.elapsed());
}

fn try_lines_iter(report: &Report, filename: &str) {
    let infile = File::open(filename).expect("open");
    let infile = BufReader::with_capacity(BUFFER_SIZE, infile);

    let start = Instant::now();
    let mut lines = 0_u64;
    for _line in infile.lines() {
        lines += 1;
    }

    report.report("lines()", None, Some(lines), start.elapsed());
}

fn main() {
    use std::env;

    for file in env::args().skip(1) {
        let report = Report::new(&file);
        try_baseline(&report, &file);
        try_linereader_batch(&report, &file);
        try_linereader(&report, &file);
        try_read_until(&report, &file);
        try_read_line(&report, &file);
        try_lines_iter(&report, &file);
    }
}