The LineReader
struct is a byte-delimiter-focused buffered reader meant as a
faster, less error-prone alternative to BufRead::read_until
.
It provides two main functions:
Returns Option<io::Result<&[u8]>>
- None
on end-of-file, an IO error from the
wrapped reader, or an immutable byte slice ending on and including any delimiter.
Line length is limited to the size of the internal buffer.
In contrast with read_until
, detecting end-of-file is more natural with the
use of Option
; line length is naturally limited to some sensible value without
the use of by_ref().take(limit)
; copying is minimised by returning borrowed
slices; you'll never forget to call buf.clear()
.
Behaves identically to next_line()
, except it returns a slice of all the complete
lines in the buffer.
extern crate linereader;
use linereader::LineReader;
let mut file = File::open(myfile).expect("open");
// Defaults to a 1 MiB buffer and b'\n' delimiter; change with one of:
// * LineReader::with_capacity(usize);
// * LineReader::with_delimiter(u8);
// * LineReader::with_delimiter_and_capacity(u8, usize)
let mut reader = LineReader::new(file);
while let Some(line) = reader.next_line() {
let line = line.expect("read error");
// line is a &[u8] owned by reader.
}
Lines can also be read in batches for group processing - e.g. in threads:
while let Some(lines) = reader.next_batch() {
send(&chan, lines.expect("read error").to_vec());
}
This should be more efficient than finding each intermediate delimiter in the main thread, and allocating and sending each individual line. Any line fragments at the end of the internal buffer will be copied to the start in the next call.
Tests performed using 'Dickens_Charles_Pickwick_Papers.xml', concatinated to itself 480 times. The resulting file is 976 MB and 10.3 million lines long.
Buffers in each test are set to 1 MiB.
Method | Time | Lines/sec | Bandwidth |
---|---|---|---|
read() | 1.82s | 5,674,452/s | 535.21 MB/s |
LR::next_batch() | 1.83s | 5,650,387/s | 532.94 MB/s |
LR::next_line() | 3.10s | 3,341,796/s | 315.20 MB/s |
read_until() | 3.62s | 2,861,864/s | 269.93 MB/s |
read_line() | 4.25s | 2,432,505/s | 229.43 MB/s |
lines() | 4.88s | 2,119,837/s | 199.94 MB/s |
Method | Time | Lines/sec | Bandwidth |
---|---|---|---|
read() | 0.26s | 39,253,494/s | 3702.36 MB/s |
LR::next_batch() | 0.26s | 39,477,365/s | 3723.47 MB/s |
LR::next_line() | 0.50s | 20,672,784/s | 1949.84 MB/s |
read_until() | 0.60s | 17,303,147/s | 1632.02 MB/s |
read_line() | 0.84s | 12,293,247/s | 1159.49 MB/s |
lines() | 1.53s | 6,783,849/s | 639.85 MB/s |
It's also surprisingly fast on debug builds (or stdlib is surprisingly slow):
Method | Time | Lines/sec | Bandwidth |
---|---|---|---|
read() | 0.27s | 38,258,105/s | 3608.47 MB/s |
LR::next_batch() | 0.28s | 36,896,353/s | 3480.04 MB/s |
LR::next_line() | 2.99s | 3,463,911/s | 326.71 MB/s |
read_until() | 57.01s | 181,505/s | 17.12 MB/s |
read_line() | 58.36s | 177,322/s | 16.72 MB/s |
lines() | 21.06s | 491,320/s | 46.34 MB/s |