Skip to content

Commit 954ecf5

Browse files
committed
avoid excessive initialization when copying to a Vec
It now keeps track of initialized bytes to avoid reinitialization. It also keeps track of read sizes to avoid initializing more bytes than the reader needs. This is important when passing a huge vector to a Read that only has a few bytes to offer and doesn't implement read_buf().
1 parent 9c20ddd commit 954ecf5

File tree

1 file changed

+44
-17
lines changed

1 file changed

+44
-17
lines changed

library/std/src/io/copy.rs

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -271,28 +271,55 @@ impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> {
271271
}
272272
}
273273

274+
// don't immediately offer the vec's whole spare capacity, otherwise
275+
// we might have to fully initialize it if the reader doesn't have a custom read_buf() impl
276+
let mut max_read_size = DEFAULT_BUF_SIZE;
277+
274278
loop {
275279
self.reserve(DEFAULT_BUF_SIZE);
276-
let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into();
277-
match reader.read_buf(buf.unfilled()) {
278-
Ok(()) => {}
279-
Err(e) if e.is_interrupted() => continue,
280-
Err(e) => return Err(e),
281-
};
280+
let mut initialized_spare_capacity = 0;
282281

283-
let read = buf.filled().len();
284-
if read == 0 {
285-
break;
286-
}
282+
loop {
283+
let buf = self.spare_capacity_mut();
284+
let read_size = min(max_read_size, buf.len());
285+
let mut buf = BorrowedBuf::from(&mut buf[..read_size]);
286+
// SAFETY: init is either 0 or the init_len from the previous iteration.
287+
unsafe { buf.set_init(initialized_spare_capacity); }
288+
match reader.read_buf(buf.unfilled()) {
289+
Ok(()) => {
290+
let bytes_read = buf.len();
287291

288-
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
289-
// and the number of read bytes can't exceed the spare capacity since
290-
// that's what the buffer is borrowing from.
291-
unsafe { self.set_len(self.len() + read) };
292-
bytes += read as u64;
293-
}
292+
// EOF
293+
if bytes_read == 0 {
294+
return Ok(bytes);
295+
}
296+
297+
// the reader is returning short reads but it doesn't call ensure_init()
298+
if buf.init_len() < buf.capacity() {
299+
max_read_size = usize::MAX;
300+
}
301+
// the reader hasn't returned short reads so far
302+
if bytes_read == buf.capacity() {
303+
max_read_size *= 2;
304+
}
294305

295-
Ok(bytes)
306+
initialized_spare_capacity = buf.init_len() - bytes_read;
307+
bytes += bytes_read as u64;
308+
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
309+
// and the number of read bytes can't exceed the spare capacity since
310+
// that's what the buffer is borrowing from.
311+
unsafe { self.set_len(self.len() + bytes_read) };
312+
313+
// spare capacity full, reserve more
314+
if self.len() == self.capacity() {
315+
break;
316+
}
317+
}
318+
Err(e) if e.is_interrupted() => continue,
319+
Err(e) => return Err(e),
320+
}
321+
}
322+
}
296323
}
297324
}
298325

0 commit comments

Comments
 (0)