1 ///! An encapsulation of `BufReader`'s buffer management logic.
3 /// This module factors out the basic functionality of `BufReader` in order to protect two core
5 /// * `filled` bytes of `buf` are always initialized
6 /// * `pos` is always <= `filled`
7 /// Since this module encapsulates the buffer management logic, we can ensure that the range
8 /// `pos..filled` is always a valid index into the initialized region of the buffer. This means
9 /// that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
10 /// without encountering any runtime bounds checks.
12 use crate::io::{self, BorrowedBuf, Read};
13 use crate::mem::MaybeUninit;
17 buf: Box<[MaybeUninit<u8>]>,
18 // The current seek offset into `buf`, must always be <= `filled`.
20 // Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
21 // initialized with bytes from a read.
23 // This is the max number of bytes returned across all `fill_buf` calls. We track this so that we
24 // can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its
25 // defensive initialization as possible. Note that while this often the same as `filled`, it
26 // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
27 // omitting this is a huge perf regression for `Read` impls that do not.
33 pub fn with_capacity(capacity: usize) -> Self {
34 let buf = Box::new_uninit_slice(capacity);
35 Self { buf, pos: 0, filled: 0, initialized: 0 }
39 pub fn buffer(&self) -> &[u8] {
40 // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and
41 // that region is initialized because those are all invariants of this type.
42 unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) }
46 pub fn capacity(&self) -> usize {
51 pub fn filled(&self) -> usize {
56 pub fn pos(&self) -> usize {
60 // This is only used by a test which asserts that the initialization-tracking is correct.
62 pub fn initialized(&self) -> usize {
67 pub fn discard_buffer(&mut self) {
73 pub fn consume(&mut self, amt: usize) {
74 self.pos = cmp::min(self.pos + amt, self.filled);
77 /// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
78 /// `visitor` and return true. If there are not enough bytes available, return false.
80 pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
84 if let Some(claimed) = self.buffer().get(..amt) {
86 // If the indexing into self.buffer() succeeds, amt must be a valid increment.
95 pub fn unconsume(&mut self, amt: usize) {
96 self.pos = self.pos.saturating_sub(amt);
100 pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
101 // If we've reached the end of our internal buffer then we need to fetch
102 // some more data from the reader.
103 // Branch using `>=` instead of the more correct `==`
104 // to tell the compiler that the pos..cap slice is always valid.
105 if self.pos >= self.filled {
106 debug_assert!(self.pos == self.filled);
108 let mut buf = BorrowedBuf::from(&mut *self.buf);
109 // SAFETY: `self.filled` bytes will always have been initialized.
111 buf.set_init(self.initialized);
114 reader.read_buf(buf.unfilled())?;
117 self.filled = buf.len();
118 self.initialized = buf.init_len();