forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend io::copy buffer reuse to BufReader too
previously it was only able to use BufWriter. This was due to a limitation in the BufReader generics that prevented specialization. This change works around the issue by using `where Self: Read` instead of `where I: Read`. This limits our options, e.g. we can't access BufRead methods, but it happens to work out if we rely on some implementation details.
- Loading branch information
Showing
4 changed files
with
207 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
use crate::cmp::{max, min}; | ||
use crate::io::*; | ||
|
||
#[test] | ||
fn copy_copies() { | ||
let mut r = repeat(0).take(4); | ||
let mut w = sink(); | ||
assert_eq!(copy(&mut r, &mut w).unwrap(), 4); | ||
|
||
let mut r = repeat(0).take(1 << 17); | ||
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); | ||
} | ||
|
||
struct ShortReader { | ||
cap: usize, | ||
read_size: usize, | ||
observed_buffer: usize, | ||
} | ||
|
||
impl Read for ShortReader { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
let bytes = min(self.cap, self.read_size); | ||
self.cap -= bytes; | ||
self.observed_buffer = max(self.observed_buffer, buf.len()); | ||
Ok(bytes) | ||
} | ||
} | ||
|
||
struct WriteObserver { | ||
observed_buffer: usize, | ||
} | ||
|
||
impl Write for WriteObserver { | ||
fn write(&mut self, buf: &[u8]) -> Result<usize> { | ||
self.observed_buffer = max(self.observed_buffer, buf.len()); | ||
Ok(buf.len()) | ||
} | ||
|
||
fn flush(&mut self) -> Result<()> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[test] | ||
fn copy_specializes_bufwriter() { | ||
let cap = 117 * 1024; | ||
let buf_sz = 16 * 1024; | ||
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; | ||
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); | ||
assert_eq!( | ||
copy(&mut r, &mut w).unwrap(), | ||
cap as u64, | ||
"expected the whole capacity to be copied" | ||
); | ||
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); | ||
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); | ||
} | ||
|
||
#[test] | ||
fn copy_specializes_bufreader() { | ||
let mut source = vec![0; 768 * 1024]; | ||
source[1] = 42; | ||
let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); | ||
|
||
let mut sink = Vec::new(); | ||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); | ||
assert_eq!(source.as_slice(), sink.as_slice()); | ||
|
||
let buf_sz = 71 * 1024; | ||
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition"); | ||
|
||
let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); | ||
let mut sink = WriteObserver { observed_buffer: 0 }; | ||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); | ||
assert_eq!( | ||
sink.observed_buffer, buf_sz, | ||
"expected a large buffer to be provided to the writer" | ||
); | ||
} | ||
|
||
#[cfg(unix)] | ||
mod io_benches { | ||
use crate::fs::File; | ||
use crate::fs::OpenOptions; | ||
use crate::io::prelude::*; | ||
use crate::io::BufReader; | ||
|
||
use test::Bencher; | ||
|
||
#[bench] | ||
fn bench_copy_buf_reader(b: &mut Bencher) { | ||
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); | ||
// use dyn to avoid specializations unrelated to readbuf | ||
let dyn_in = &mut file_in as &mut dyn Read; | ||
let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0)); | ||
let mut writer = | ||
OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); | ||
|
||
const BYTES: u64 = 1024 * 1024; | ||
|
||
b.bytes = BYTES; | ||
|
||
b.iter(|| { | ||
reader.get_mut().set_limit(BYTES); | ||
crate::io::copy(&mut reader, &mut writer).unwrap() | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters