Remove some redundant checks from BufReader
The implementation of BufReader contains a lot of redundant checks. While any one of these checks is not particularly expensive to execute, especially when taken together they dramatically inhibit LLVM's ability to make subsequent optimizations.
This commit is contained in:
parent
b4151a41a0
commit
761ddf3e7f
3 changed files with 106 additions and 53 deletions
75
library/std/src/io/buffered/bufreader/buffer.rs
Normal file
75
library/std/src/io/buffered/bufreader/buffer.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use crate::cmp;
|
||||
use crate::io::{self, Read, ReadBuf};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub struct Buffer {
|
||||
buf: Box<[MaybeUninit<u8>]>,
|
||||
pos: usize,
|
||||
cap: usize,
|
||||
init: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let buf = Box::new_uninit_slice(capacity);
|
||||
Self { buf, pos: 0, cap: 0, init: 0 }
|
||||
}
|
||||
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
// SAFETY: self.cap is always <= self.init, so self.buf[self.pos..self.cap] is always init
|
||||
// Additionally, both self.pos and self.cap are valid and and self.cap => self.pos, and
|
||||
// that region is initialized because those are all invariants of this type.
|
||||
unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.get_unchecked(self.pos..self.cap)) }
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
pub fn cap(&self) -> usize {
|
||||
self.cap
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
pub fn discard_buffer(&mut self) {
|
||||
self.pos = 0;
|
||||
self.cap = 0;
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.pos = cmp::min(self.pos + amt, self.cap);
|
||||
}
|
||||
|
||||
pub fn unconsume(&mut self, amt: usize) {
|
||||
self.pos = self.pos.saturating_sub(amt);
|
||||
}
|
||||
|
||||
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the underlying reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.cap {
|
||||
debug_assert!(self.pos == self.cap);
|
||||
|
||||
let mut readbuf = ReadBuf::uninit(&mut self.buf);
|
||||
|
||||
// SAFETY: `self.init` is either 0 or set to `readbuf.initialized_len()`
|
||||
// from the last time this function was called
|
||||
unsafe {
|
||||
readbuf.assume_init(self.init);
|
||||
}
|
||||
|
||||
reader.read_buf(&mut readbuf)?;
|
||||
|
||||
self.cap = readbuf.filled_len();
|
||||
self.init = readbuf.initialized_len();
|
||||
|
||||
self.pos = 0;
|
||||
}
|
||||
Ok(self.buffer())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue