-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
specialize io::copy to use copy_file_range, splice or sendfile #75272
Changes from all commits
1623647
5eb88fa
67a6059
cd3bddc
0624730
46e7fbe
ad9b07c
7f5d272
18bfe2a
888b103
3dfc377
4854d41
bbfa92c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use crate::io::{self, ErrorKind, Read, Write}; | ||
use crate::mem::MaybeUninit; | ||
|
||
/// Copies the entire contents of a reader into a writer. | ||
/// | ||
/// This function will continuously read data from `reader` and then | ||
/// write it into `writer` in a streaming fashion until `reader` | ||
/// returns EOF. | ||
/// | ||
/// On success, the total number of bytes that were copied from | ||
/// `reader` to `writer` is returned. | ||
/// | ||
/// If you’re wanting to copy the contents of one file to another and you’re | ||
/// working with filesystem paths, see the [`fs::copy`] function. | ||
/// | ||
/// [`fs::copy`]: crate::fs::copy | ||
/// | ||
/// # Errors | ||
/// | ||
/// This function will return an error immediately if any call to [`read`] or | ||
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are | ||
/// handled by this function and the underlying operation is retried. | ||
/// | ||
/// [`read`]: Read::read | ||
/// [`write`]: Write::write | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use std::io; | ||
/// | ||
/// fn main() -> io::Result<()> { | ||
/// let mut reader: &[u8] = b"hello"; | ||
/// let mut writer: Vec<u8> = vec![]; | ||
/// | ||
/// io::copy(&mut reader, &mut writer)?; | ||
/// | ||
/// assert_eq!(&b"hello"[..], &writer[..]); | ||
/// Ok(()) | ||
/// } | ||
/// ``` | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> | ||
where | ||
R: Read, | ||
W: Write, | ||
{ | ||
cfg_if::cfg_if! { | ||
if #[cfg(any(target_os = "linux", target_os = "android"))] { | ||
crate::sys::kernel_copy::copy_spec(reader, writer) | ||
} else { | ||
generic_copy(reader, writer) | ||
} | ||
} | ||
} | ||
|
||
/// The general read-write-loop implementation of | ||
/// `io::copy` that is used when specializations are not available or not applicable. | ||
pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> | ||
where | ||
R: Read, | ||
W: Write, | ||
{ | ||
let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); | ||
// FIXME: #42788 | ||
// | ||
// - This creates a (mut) reference to a slice of | ||
// _uninitialized_ integers, which is **undefined behavior** | ||
// | ||
// - Only the standard library gets to soundly "ignore" this, | ||
// based on its privileged knowledge of unstable rustc | ||
// internals; | ||
unsafe { | ||
reader.initializer().initialize(buf.assume_init_mut()); | ||
} | ||
|
||
let mut written = 0; | ||
loop { | ||
let len = match reader.read(unsafe { buf.assume_init_mut() }) { | ||
Ok(0) => return Ok(written), | ||
Ok(len) => len, | ||
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, | ||
Err(e) => return Err(e), | ||
}; | ||
writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?; | ||
written += len as u64; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -316,6 +316,7 @@ | |
#![feature(toowned_clone_into)] | ||
#![feature(total_cmp)] | ||
#![feature(trace_macros)] | ||
#![feature(try_blocks)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little surprised this wasn't here already... As far as I know the feature is somewhat blocked, but it looks like we do use it elsewhere in the repo so I'm ok with adding it here too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only used it for cleanup in tests. But that was before I learned that other IO tests don't clean up the files they create either. So removing it and adding to the clutter instead would also be an option. |
||
#![feature(try_reserve)] | ||
#![feature(unboxed_closures)] | ||
#![feature(unsafe_block_in_unsafe_fn)] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a more descriptive name for this might be
copy_fallback
since we only use it when specialized implementations aren't available.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if fallback is the right word. From the perspective of the specializations it indeed is the fallback. But the specializations only apply in some narrow scenarios. In the more general case of arbitrary
Read
/Write
pairs this is the default strategy.