std\io\buffered\bufreader/buffer.rs
1//! An encapsulation of `BufReader`'s buffer management logic.
2//!
3//! This module factors out the basic functionality of `BufReader` in order to protect two core
4//! invariants:
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.
11
12use crate::cmp;
13use crate::io::{self, BorrowedBuf, ErrorKind, Read};
14use crate::mem::MaybeUninit;
15
16pub struct Buffer {
17 // The buffer.
18 buf: Box<[MaybeUninit<u8>]>,
19 // The current seek offset into `buf`, must always be <= `filled`.
20 pos: usize,
21 // Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
22 // initialized with bytes from a read.
23 filled: usize,
24 // Whether `buf` has been fully initialized. We track this so that we can accurately tell
25 // `read_buf` how many bytes of buf are initialized, to bypass as much of its defensive
26 // initialization as possible. Calls to `fill_buf` are not required to actually fill the buffer,
27 // and omitting this is a huge perf regression for `Read` impls that do not.
28 initialized: bool,
29}
30
31impl Buffer {
32 #[inline]
33 pub fn with_capacity(capacity: usize) -> Self {
34 let buf = Box::new_uninit_slice(capacity);
35 Self { buf, pos: 0, filled: 0, initialized: false }
36 }
37
38 #[inline]
39 pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
40 match Box::try_new_uninit_slice(capacity) {
41 Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: false }),
42 Err(_) => {
43 Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer"))
44 }
45 }
46 }
47
48 #[inline]
49 pub fn buffer(&self) -> &[u8] {
50 // SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
51 // that region is initialized because those are all invariants of this type.
52 unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
53 }
54
55 #[inline]
56 pub fn capacity(&self) -> usize {
57 self.buf.len()
58 }
59
60 #[inline]
61 pub fn filled(&self) -> usize {
62 self.filled
63 }
64
65 #[inline]
66 pub fn pos(&self) -> usize {
67 self.pos
68 }
69
70 // This is only used by a test which asserts that the initialization-tracking is correct.
71 #[cfg(test)]
72 pub fn initialized(&self) -> bool {
73 self.initialized
74 }
75
76 #[inline]
77 pub fn discard_buffer(&mut self) {
78 self.pos = 0;
79 self.filled = 0;
80 }
81
82 #[inline]
83 pub fn consume(&mut self, amt: usize) {
84 self.pos = cmp::min(self.pos + amt, self.filled);
85 }
86
87 /// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
88 /// `visitor` and return true. If there are not enough bytes available, return false.
89 #[inline]
90 pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
91 where
92 V: FnMut(&[u8]),
93 {
94 if let Some(claimed) = self.buffer().get(..amt) {
95 visitor(claimed);
96 // If the indexing into self.buffer() succeeds, amt must be a valid increment.
97 self.pos += amt;
98 true
99 } else {
100 false
101 }
102 }
103
104 #[inline]
105 pub fn unconsume(&mut self, amt: usize) {
106 self.pos = self.pos.saturating_sub(amt);
107 }
108
109 /// Read more bytes into the buffer without discarding any of its contents
110 pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
111 let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
112
113 if self.initialized {
114 // SAFETY: `self.initialized` is only set after `self.buf` was
115 // fully initialized, and once `self.buf` is fully initialized
116 // no part will become uninitialized.
117 unsafe { buf.set_init() };
118 }
119
120 reader.read_buf(buf.unfilled())?;
121 self.filled += buf.len();
122 self.initialized = buf.is_init();
123 Ok(buf.len())
124 }
125
126 /// Remove bytes that have already been read from the buffer.
127 pub fn backshift(&mut self) {
128 self.buf.copy_within(self.pos..self.filled, 0);
129 self.filled -= self.pos;
130 self.pos = 0;
131 }
132
133 #[inline]
134 pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
135 // If we've reached the end of our internal buffer then we need to fetch
136 // some more data from the reader.
137 // Branch using `>=` instead of the more correct `==`
138 // to tell the compiler that the pos..cap slice is always valid.
139 if self.pos >= self.filled {
140 debug_assert!(self.pos == self.filled);
141
142 let mut buf = BorrowedBuf::from(&mut *self.buf);
143
144 if self.initialized {
145 // SAFETY: `self.initialized` is only set after `self.buf` was
146 // fully initialized, and once `self.buf` is fully initialized
147 // no part will become uninitialized.
148 unsafe { buf.set_init() };
149 }
150
151 let result = reader.read_buf(buf.unfilled());
152
153 self.pos = 0;
154 self.filled = buf.len();
155 self.initialized = buf.is_init();
156
157 result?;
158 }
159 Ok(self.buffer())
160 }
161}