Skip to content

Commit 205ae16

Browse files
authored
Rollup merge of rust-lang#113493 - the8472:spec-iocopy-slice, r=Mark-Simulacrum
additional io::copy specializations - copying from `&[u8]` and `VecDeque<u8>` - copying to `Vec<u8>` A user on reddit [mentioned they saw a performance drop](https://www.reddit.com/r/rust/comments/14shv9f/comment/jr0bg6j/?context=3) when copying from a slice.
2 parents ec479ba + 6f8ba51 commit 205ae16

File tree

2 files changed

+116
-3
lines changed

2 files changed

+116
-3
lines changed

library/std/src/io/copy.rs

+79-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
2+
use crate::alloc::Allocator;
3+
use crate::cmp;
4+
use crate::collections::VecDeque;
5+
use crate::io::IoSlice;
26
use crate::mem::MaybeUninit;
37

48
#[cfg(test)]
@@ -86,7 +90,7 @@ where
8690

8791
/// Specialization of the read-write loop that reuses the internal
8892
/// buffer of a BufReader. If there's no buffer then the writer side
89-
/// should be used intead.
93+
/// should be used instead.
9094
trait BufferedReaderSpec {
9195
fn buffer_size(&self) -> usize;
9296

@@ -104,7 +108,39 @@ where
104108
}
105109

106110
default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
107-
unimplemented!("only called from specializations");
111+
unreachable!("only called from specializations")
112+
}
113+
}
114+
115+
impl BufferedReaderSpec for &[u8] {
116+
fn buffer_size(&self) -> usize {
117+
// prefer this specialization since the source "buffer" is all we'll ever need,
118+
// even if it's small
119+
usize::MAX
120+
}
121+
122+
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
123+
let len = self.len();
124+
to.write_all(self)?;
125+
*self = &self[len..];
126+
Ok(len as u64)
127+
}
128+
}
129+
130+
impl<A: Allocator> BufferedReaderSpec for VecDeque<u8, A> {
131+
fn buffer_size(&self) -> usize {
132+
// prefer this specialization since the source "buffer" is all we'll ever need,
133+
// even if it's small
134+
usize::MAX
135+
}
136+
137+
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
138+
let len = self.len();
139+
let (front, back) = self.as_slices();
140+
let bufs = &mut [IoSlice::new(front), IoSlice::new(back)];
141+
to.write_all_vectored(bufs)?;
142+
self.clear();
143+
Ok(len as u64)
108144
}
109145
}
110146

@@ -218,6 +254,47 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
218254
}
219255
}
220256

257+
impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> {
258+
fn buffer_size(&self) -> usize {
259+
cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
260+
}
261+
262+
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
263+
let mut bytes = 0;
264+
265+
// avoid allocating before we have determined that there's anything to read
266+
if self.capacity() == 0 {
267+
bytes = stack_buffer_copy(&mut reader.take(DEFAULT_BUF_SIZE as u64), self)?;
268+
if bytes == 0 {
269+
return Ok(0);
270+
}
271+
}
272+
273+
loop {
274+
self.reserve(DEFAULT_BUF_SIZE);
275+
let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into();
276+
match reader.read_buf(buf.unfilled()) {
277+
Ok(()) => {}
278+
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
279+
Err(e) => return Err(e),
280+
};
281+
282+
let read = buf.filled().len();
283+
if read == 0 {
284+
break;
285+
}
286+
287+
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
288+
// and the number of read bytes can't exceed the spare capacity since
289+
// that's what the buffer is borrowing from.
290+
unsafe { self.set_len(self.len() + read) };
291+
bytes += read as u64;
292+
}
293+
294+
Ok(bytes)
295+
}
296+
}
297+
221298
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
222299
reader: &mut R,
223300
writer: &mut W,

library/std/src/io/copy/tests.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::cmp::{max, min};
2+
use crate::collections::VecDeque;
3+
use crate::io;
24
use crate::io::*;
35

46
#[test]
@@ -19,7 +21,7 @@ struct ShortReader {
1921

2022
impl Read for ShortReader {
2123
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
22-
let bytes = min(self.cap, self.read_size);
24+
let bytes = min(self.cap, self.read_size).min(buf.len());
2325
self.cap -= bytes;
2426
self.observed_buffer = max(self.observed_buffer, buf.len());
2527
Ok(bytes)
@@ -78,6 +80,40 @@ fn copy_specializes_bufreader() {
7880
);
7981
}
8082

83+
#[test]
84+
fn copy_specializes_to_vec() {
85+
let cap = 123456;
86+
let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
87+
let mut sink = Vec::new();
88+
assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap());
89+
assert!(
90+
source.observed_buffer > DEFAULT_BUF_SIZE,
91+
"expected a large buffer to be provided to the reader"
92+
);
93+
}
94+
95+
#[test]
96+
fn copy_specializes_from_vecdeque() {
97+
let mut source = VecDeque::with_capacity(100 * 1024);
98+
for _ in 0..20 * 1024 {
99+
source.push_front(0);
100+
}
101+
for _ in 0..20 * 1024 {
102+
source.push_back(0);
103+
}
104+
let mut sink = WriteObserver { observed_buffer: 0 };
105+
assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
106+
assert_eq!(20 * 1024, sink.observed_buffer);
107+
}
108+
109+
#[test]
110+
fn copy_specializes_from_slice() {
111+
let mut source = [1; 60 * 1024].as_slice();
112+
let mut sink = WriteObserver { observed_buffer: 0 };
113+
assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
114+
assert_eq!(60 * 1024, sink.observed_buffer);
115+
}
116+
81117
#[cfg(unix)]
82118
mod io_benches {
83119
use crate::fs::File;

0 commit comments

Comments
 (0)