@@ -127,10 +127,39 @@ impl FileLoader for RealFileLoader {
127
127
128
128
let mut bytes = Lrc :: new_uninit_slice ( len as usize ) ;
129
129
let mut buf = BorrowedBuf :: from ( Lrc :: get_mut ( & mut bytes) . unwrap ( ) ) ;
130
- file. read_buf_exact ( buf. unfilled ( ) ) ?;
130
+ match file. read_buf_exact ( buf. unfilled ( ) ) {
131
+ Ok ( ( ) ) => { }
132
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: UnexpectedEof => {
133
+ drop ( bytes) ;
134
+ return fs:: read ( path) . map ( Vec :: into) ;
135
+ }
136
+ Err ( e) => return Err ( e) ,
137
+ }
131
138
// SAFETY: If the read_buf_exact call returns Ok(()), then we have
132
139
// read len bytes and initialized the buffer.
133
- Ok ( unsafe { bytes. assume_init ( ) } )
140
+ let bytes = unsafe { bytes. assume_init ( ) } ;
141
+
142
+ // At this point, we've read all the bytes that filesystem metadata reported exist.
143
+ // But we are not guaranteed to be at the end of the file, because we did not attempt to do
144
+ // a read with a non-zero-sized buffer and get Ok(0).
145
+ // So we do small read to a fixed-size buffer. If the read returns no bytes then we're
146
+ // already done, and we just return the Lrc we built above.
147
+ // If the read returns bytes however, we just fall back to reading into a Vec then turning
148
+ // that into an Lrc, losing our nice peak memory behavior. This fallback code path should
149
+ // be rarely exercised.
150
+
151
+ let mut probe = [ 0u8 ; 32 ] ;
152
+ let n = loop {
153
+ match file. read ( & mut probe) {
154
+ Ok ( 0 ) => return Ok ( bytes) ,
155
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: Interrupted => continue ,
156
+ Err ( e) => return Err ( e) ,
157
+ Ok ( n) => break n,
158
+ }
159
+ } ;
160
+ let mut bytes: Vec < u8 > = bytes. iter ( ) . copied ( ) . chain ( probe[ ..n] . iter ( ) . copied ( ) ) . collect ( ) ;
161
+ file. read_to_end ( & mut bytes) ?;
162
+ Ok ( bytes. into ( ) )
134
163
}
135
164
}
136
165
0 commit comments