abonander / buf_redux

A drop-in replacement for Rust's std::io::BufReader, with extra features
Apache License 2.0
38 stars 16 forks source link

buf_re(a)dux

Travis Crates.io Crates.io Crates.io

Drop-in replacements for buffered I/O types in std::io.

These replacements retain the method names/signatures and implemented traits of their stdlib counterparts, making replacement as simple as swapping the import of the type.

More Direct Control

All replacement types provide methods to:

BufReader provides methods to:

BufWriter and LineWriter provide methods to:

More Sensible and Customizable Buffering Behavior

Tune the behavior of the buffer to your specific use-case using the types in the policy module:

Usage

Documentation

Cargo.toml:

[dependencies]
buf_redux = "0.2"

lib.rs or main.rs:

extern crate buf_redux;

And then simply swap the import of the types you want to replace:

BufReader:

- use std::io::BufReader;
+ use buf_redux::BufReader;

BufWriter:

- use std::io::BufWriter;
+ use buf_redux::BufWriter;

LineWriter:

- use std::io::LineWriter;
+ use buf_redux::LineWriter;

Using MinBuffered

The new policy::MinBuffered reader-policy can be used to ensure that BufReader always has at least a certain number of bytes in its buffer. This can be useful for parsing applications that require a certain amount of lookahead.

use buf_redux::BufReader;
use buf_redux::policy::MinBuffered;
use std::io::{BufRead, Cursor};

let data = (1 .. 16).collect::<Vec<u8>>();

// normally you should use `BufReader::new()` or give a capacity of several KiB or more
let mut reader = BufReader::with_capacity(8, Cursor::new(data))
    // always at least 4 bytes in the buffer (or until the source is empty)
    .set_policy(MinBuffered(4)); // always at least 4 bytes in the buffer

// first buffer fill, same as `std::io::BufReader`
assert_eq!(reader.fill_buf().unwrap(), &[1, 2, 3, 4, 5, 6, 7, 8]);
reader.consume(3);

// enough data in the buffer, another read isn't done yet
assert_eq!(reader.fill_buf().unwrap(), &[4, 5, 6, 7, 8]);
reader.consume(4);

// `std::io::BufReader` would return `&[8]`
assert_eq!(reader.fill_buf().unwrap(), &[8, 9, 10, 11, 12, 13, 14, 15]);
reader.consume(5);

// no data left in the reader
assert_eq!(reader.fill_buf().unwrap(), &[13, 14, 15]);

Note: Making Room / Ringbuffers / slice-deque Feature

With policies like MinBuffered, that will read into the buffer and consume bytes from it without completely emptying it, normal buffer handling can run out of room to read/write into as all the free space is at the head of the buffer. If the amount of data in the buffer is small, you can call .make_room() on the buffered type to make more room for reading. MinBuffered will do this automatically.

Instead of this, with the slice-deque feature, you can instead have your buffered type allocate a ringbuffer, simply by using the ::new_ringbuf() or ::with_capacity_ringbuf() constructors instead of ::new() or with_capacity(), respectively. With a ringbuffer, consuming/flushing bytes from a buffer instantly makes room for more reading/writing at the end. However, this has some caveats:

It is up to you to decide if the benefits outweigh the costs. With a policy like MinBuffered, it could significantly improve performance.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.