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.
1
11
use crate :: cmp;
2
12
use crate :: io:: { self , Read , ReadBuf } ;
3
13
use crate :: mem:: MaybeUninit ;
4
14
5
15
pub struct Buffer {
16
+ // The buffer.
6
17
buf : Box < [ MaybeUninit < u8 > ] > ,
18
+ // The current seek offset into `buf`, must always be <= `filled`.
7
19
pos : usize ,
8
- cap : usize ,
9
- init : usize ,
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.
22
+ filled : usize ,
10
23
}
11
24
12
25
impl Buffer {
13
26
#[ inline]
14
27
pub fn with_capacity ( capacity : usize ) -> Self {
15
28
let buf = Box :: new_uninit_slice ( capacity) ;
16
- Self { buf, pos : 0 , cap : 0 , init : 0 }
29
+ Self { buf, pos : 0 , filled : 0 }
17
30
}
18
31
19
32
#[ inline]
20
33
pub fn buffer ( & self ) -> & [ u8 ] {
21
- // SAFETY: self.cap is always <= self.init, so self.buf[self.pos..self.cap] is always init
22
- // Additionally, both self.pos and self.cap are valid and and self.cap => self.pos, and
34
+ // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and
23
35
// that region is initialized because those are all invariants of this type.
24
- unsafe { MaybeUninit :: slice_assume_init_ref ( & self . buf . get_unchecked ( self . pos ..self . cap ) ) }
36
+ unsafe { MaybeUninit :: slice_assume_init_ref ( self . buf . get_unchecked ( self . pos ..self . filled ) ) }
25
37
}
26
38
27
39
#[ inline]
@@ -30,8 +42,8 @@ impl Buffer {
30
42
}
31
43
32
44
#[ inline]
33
- pub fn cap ( & self ) -> usize {
34
- self . cap
45
+ pub fn filled ( & self ) -> usize {
46
+ self . filled
35
47
}
36
48
37
49
#[ inline]
@@ -42,12 +54,12 @@ impl Buffer {
42
54
#[ inline]
43
55
pub fn discard_buffer ( & mut self ) {
44
56
self . pos = 0 ;
45
- self . cap = 0 ;
57
+ self . filled = 0 ;
46
58
}
47
59
48
60
#[ inline]
49
61
pub fn consume ( & mut self , amt : usize ) {
50
- self . pos = cmp:: min ( self . pos + amt, self . cap ) ;
62
+ self . pos = cmp:: min ( self . pos + amt, self . filled ) ;
51
63
}
52
64
53
65
#[ inline]
@@ -58,25 +70,17 @@ impl Buffer {
58
70
#[ inline]
59
71
pub fn fill_buf ( & mut self , mut reader : impl Read ) -> io:: Result < & [ u8 ] > {
60
72
// If we've reached the end of our internal buffer then we need to fetch
61
- // some more data from the underlying reader.
73
+ // some more data from the reader.
62
74
// Branch using `>=` instead of the more correct `==`
63
75
// to tell the compiler that the pos..cap slice is always valid.
64
- if self . pos >= self . cap {
65
- debug_assert ! ( self . pos == self . cap ) ;
76
+ if self . pos >= self . filled {
77
+ debug_assert ! ( self . pos == self . filled ) ;
66
78
67
79
let mut readbuf = ReadBuf :: uninit ( & mut self . buf ) ;
68
80
69
- // SAFETY: `self.init` is either 0 or set to `readbuf.initialized_len()`
70
- // from the last time this function was called
71
- unsafe {
72
- readbuf. assume_init ( self . init ) ;
73
- }
74
-
75
81
reader. read_buf ( & mut readbuf) ?;
76
82
77
- self . cap = readbuf. filled_len ( ) ;
78
- self . init = readbuf. initialized_len ( ) ;
79
-
83
+ self . filled = readbuf. filled_len ( ) ;
80
84
self . pos = 0 ;
81
85
}
82
86
Ok ( self . buffer ( ) )
0 commit comments