From 72de226def741fa81172d1b32af014f424569936 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 15:30:58 +1000 Subject: [PATCH] Move `std::io::copy` to `alloc::io` Rely on specialization to allow `std` to provide optimized copy implementations. --- library/alloc/src/io/copy.rs | 79 ++++++ library/alloc/src/io/copy/generic.rs | 236 ++++++++++++++++ library/alloc/src/io/copy/specialization.rs | 75 +++++ library/alloc/src/io/mod.rs | 3 + library/std/src/io/copy.rs | 294 -------------------- library/std/src/io/mod.rs | 20 +- library/std/src/sys/io/kernel_copy/linux.rs | 103 ++++++- library/std/src/sys/io/kernel_copy/mod.rs | 19 +- library/std/src/sys/io/mod.rs | 1 - 9 files changed, 497 insertions(+), 333 deletions(-) create mode 100644 library/alloc/src/io/copy.rs create mode 100644 library/alloc/src/io/copy/generic.rs create mode 100644 library/alloc/src/io/copy/specialization.rs diff --git a/library/alloc/src/io/copy.rs b/library/alloc/src/io/copy.rs new file mode 100644 index 0000000000000..4afcf9ed828ed --- /dev/null +++ b/library/alloc/src/io/copy.rs @@ -0,0 +1,79 @@ +mod generic; +mod specialization; + +use self::generic::generic_copy; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::specialization::SpecCopy; +use self::specialization::specialized_copy; +use crate::io::{Read, Result, Write}; + +#[derive(Debug)] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub enum CopyState { + Ended(u64), + Fallback(u64), +} + +/// 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 want to copy the contents of one file to another and you’re +/// working with filesystem paths, see the [`fs::copy`] function. +/// +// FIXME(#74481): Hard-links required to link from `alloc` to `std` +/// [`fs::copy`]: ../../std/fs/fn.copy.html +/// +/// # 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 +/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted +/// +/// # Examples +/// +/// ``` +/// use std::io; +/// +/// fn main() -> io::Result<()> { +/// let mut reader: &[u8] = b"hello"; +/// let mut writer: Vec = vec![]; +/// +/// io::copy(&mut reader, &mut writer)?; +/// +/// assert_eq!(&b"hello"[..], &writer[..]); +/// Ok(()) +/// } +/// ``` +/// +/// # Platform-specific behavior +/// +/// On Linux (including Android), this function uses `copy_file_range(2)`, +/// `sendfile(2)` or `splice(2)` syscalls to move data directly between file +/// descriptors if possible. +/// +/// Note that platform-specific behavior may change in the future. +#[stable(feature = "rust1", since = "1.0.0")] +pub fn copy(reader: &mut R, writer: &mut W) -> Result +where + R: Read, + W: Write, +{ + match specialized_copy(reader, writer)? { + CopyState::Ended(copied) => Ok(copied), + CopyState::Fallback(copied) => { + generic_copy(reader, writer).map(|additional| copied + additional) + } + } +} diff --git a/library/alloc/src/io/copy/generic.rs b/library/alloc/src/io/copy/generic.rs new file mode 100644 index 0000000000000..7dbc56e464732 --- /dev/null +++ b/library/alloc/src/io/copy/generic.rs @@ -0,0 +1,236 @@ +use core::cmp; +use core::mem::MaybeUninit; + +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::io::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write}; +use crate::vec::Vec; +#[cfg_attr( + no_global_oom_handling, + expect(unused_imports, reason = "only required for VecDeque specialization") +)] +use crate::{alloc::Allocator, io::IoSlice}; + +/// The userspace read-write-loop implementation of `io::copy` that is used when +/// OS-specific specializations for copy offloading are not available or not applicable. +pub(super) fn generic_copy(reader: &mut R, writer: &mut W) -> Result +where + R: Read, + W: Write, +{ + let read_buf = BufferedReaderSpec::buffer_size(reader); + let write_buf = BufferedWriterSpec::buffer_size(writer); + + if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { + return BufferedReaderSpec::copy_to(reader, writer); + } + + BufferedWriterSpec::copy_from(writer, reader) +} + +/// Specialization of the read-write loop that reuses the internal +/// buffer of a BufReader. If there's no buffer then the writer side +/// should be used instead. +trait BufferedReaderSpec { + fn buffer_size(&self) -> usize; + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result; +} + +impl BufferedReaderSpec for T +where + Self: Read, + T: ?Sized, +{ + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { + unreachable!("only called from specializations") + } +} + +impl BufferedReaderSpec for &[u8] { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + to.write_all(self)?; + *self = &self[len..]; + Ok(len as u64) + } +} + +#[cfg(not(no_global_oom_handling))] +impl BufferedReaderSpec for VecDeque { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let len = self.len(); + let (front, back) = self.as_slices(); + let bufs = &mut [IoSlice::new(front), IoSlice::new(back)]; + to.write_all_vectored(bufs)?; + self.clear(); + Ok(len as u64) + } +} + +impl BufferedReaderSpec for BufReader +where + Self: Read, + I: ?Sized, +{ + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let mut len = 0; + + loop { + // Hack: this relies on `impl Read for BufReader` always calling fill_buf + // if the buffer is empty, even for empty slices. + // It can't be called directly here since specialization prevents us + // from adding I: Read + match self.read(&mut []) { + Ok(_) => {} + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + let buf = self.buffer(); + if self.buffer().len() == 0 { + return Ok(len); + } + + // In case the writer side is a BufWriter then its write_all + // implements an optimization that passes through large + // buffers to the underlying writer. That code path is #[cold] + // but we're still avoiding redundant memcopies when doing + // a copy between buffered inputs and outputs. + to.write_all(buf)?; + len += buf.len() as u64; + self.discard_buffer(); + } + } +} + +/// Specialization of the read-write loop that either uses a stack buffer +/// or reuses the internal buffer of a BufWriter +trait BufferedWriterSpec: Write { + fn buffer_size(&self) -> usize; + + fn copy_from(&mut self, reader: &mut R) -> Result; +} + +impl BufferedWriterSpec for W { + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_from(&mut self, reader: &mut R) -> Result { + stack_buffer_copy(reader, self) + } +} + +impl BufferedWriterSpec for BufWriter { + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + if self.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, self); + } + + let mut len = 0; + let mut init = false; + + loop { + let buf = self.buffer_mut(); + let mut read_buf: BorrowedBuf<'_, u8> = buf.spare_capacity_mut().into(); + + if init { + // SAFETY: `init` is only true after `reader` initializes + // `read_buf`. See the comment about `flush_buf` below. + unsafe { read_buf.set_init() }; + } + + if read_buf.capacity() >= DEFAULT_BUF_SIZE { + let mut cursor = read_buf.unfilled(); + match reader.read_buf(cursor.reborrow()) { + Ok(()) => { + let bytes_read = cursor.written(); + + if bytes_read == 0 { + return Ok(len); + } + + init = read_buf.is_init(); + len += bytes_read as u64; + + // SAFETY: BorrowedBuf guarantees all of its filled bytes are init + unsafe { buf.set_len(buf.len() + bytes_read) }; + + // Read again if the buffer still has enough capacity, as BufWriter itself would do + // This will occur if the reader returns short reads + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } else { + // SAFETY: `flush_buf` will not de-initialize any elements of + // the spare capacity so we can remember `init` across this. + self.flush_buf()?; + } + } + } +} + +impl BufferedWriterSpec for Vec { + fn buffer_size(&self) -> usize { + cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64")) + } +} + +fn stack_buffer_copy( + reader: &mut R, + writer: &mut W, +) -> Result { + let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE]; + let mut buf: BorrowedBuf<'_, u8> = buf.into(); + + let mut len = 0; + + loop { + match reader.read_buf(buf.unfilled()) { + Ok(()) => {} + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + }; + + if buf.filled().is_empty() { + break; + } + + len += buf.filled().len() as u64; + writer.write_all(buf.filled())?; + buf.clear(); + } + + Ok(len) +} diff --git a/library/alloc/src/io/copy/specialization.rs b/library/alloc/src/io/copy/specialization.rs new file mode 100644 index 0000000000000..83ad36b2b3b04 --- /dev/null +++ b/library/alloc/src/io/copy/specialization.rs @@ -0,0 +1,75 @@ +//! Provides specialization for `io::copy`. + +use super::CopyState; +use crate::io::{BufReader, Read, Result, Take, Write}; + +/// The implementation of `io::copy` that can rely on platform specific specialization +/// provided by `libstd`. +pub(super) fn specialized_copy( + reader: &mut R, + writer: &mut W, +) -> Result +where + R: Read, + W: Write, +{ + SpecCopyInner::copy((reader, writer)) +} + +trait SpecCopyInner { + fn copy(self) -> Result; +} + +impl SpecCopyInner for (&mut R, &mut W) { + default fn copy(self) -> Result { + Ok(CopyState::Fallback(0)) + } +} + +impl SpecCopyInner for (&mut R, &mut W) { + fn copy(self) -> Result { + ::copy(self.0, self.1) + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[rustc_specialization_trait] +pub trait SpecCopy: Read { + /// Attempt to copy from this reader to the provided writer using a specialized + /// process. + fn copy( + _reader: &mut R, + _writer: &mut W, + ) -> Result; +} + +impl SpecCopy for &mut T +where + T: SpecCopy, +{ + fn copy( + reader: &mut R, + writer: &mut W, + ) -> Result { + ::copy(reader, writer) + } +} + +impl SpecCopy for Take { + fn copy( + reader: &mut R, + writer: &mut W, + ) -> Result { + ::copy(reader, writer) + } +} + +impl SpecCopy for BufReader { + fn copy( + reader: &mut R, + writer: &mut W, + ) -> Result { + ::copy(reader, writer) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index ef09d13cc6247..5c043240daba8 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -2,6 +2,7 @@ mod buf_read; mod buffered; +mod copy; mod cursor; mod error; mod impls; @@ -35,12 +36,14 @@ use self::util::{bytes, lines, split, uninlined_slow_read_byte}; pub use self::{ buf_read::BufRead, buffered::{BufReader, BufWriter, IntoInnerError, LineWriter, WriterPanicked}, + copy::copy, read::{Read, read_to_string}, util::{Bytes, Lines, Split}, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + copy::{CopyState, SpecCopy}, read::{ DEFAULT_BUF_SIZE, default_read_buf, default_read_to_end, default_read_to_string, default_read_vectored, diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 43c6f71357e61..87c2771955a9d 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,296 +1,2 @@ -use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write}; -use crate::alloc::Allocator; -use crate::cmp; -use crate::collections::VecDeque; -use crate::io::IoSlice; -use crate::mem::MaybeUninit; -use crate::sys::io::{CopyState, kernel_copy}; - #[cfg(test)] mod tests; - -/// 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 want 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 -/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// -/// fn main() -> io::Result<()> { -/// let mut reader: &[u8] = b"hello"; -/// let mut writer: Vec = vec![]; -/// -/// io::copy(&mut reader, &mut writer)?; -/// -/// assert_eq!(&b"hello"[..], &writer[..]); -/// Ok(()) -/// } -/// ``` -/// -/// # Platform-specific behavior -/// -/// On Linux (including Android), this function uses `copy_file_range(2)`, -/// `sendfile(2)` or `splice(2)` syscalls to move data directly between file -/// descriptors if possible. -/// -/// Note that platform-specific behavior [may change in the future][changes]. -/// -/// [changes]: crate::io#platform-specific-behavior -#[stable(feature = "rust1", since = "1.0.0")] -pub fn copy(reader: &mut R, writer: &mut W) -> Result -where - R: Read, - W: Write, -{ - match kernel_copy(reader, writer)? { - CopyState::Ended(copied) => Ok(copied), - CopyState::Fallback(copied) => { - generic_copy(reader, writer).map(|additional| copied + additional) - } - } -} - -/// The userspace read-write-loop implementation of `io::copy` that is used when -/// OS-specific specializations for copy offloading are not available or not applicable. -fn generic_copy(reader: &mut R, writer: &mut W) -> Result -where - R: Read, - W: Write, -{ - let read_buf = BufferedReaderSpec::buffer_size(reader); - let write_buf = BufferedWriterSpec::buffer_size(writer); - - if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { - return BufferedReaderSpec::copy_to(reader, writer); - } - - BufferedWriterSpec::copy_from(writer, reader) -} - -/// Specialization of the read-write loop that reuses the internal -/// buffer of a BufReader. If there's no buffer then the writer side -/// should be used instead. -trait BufferedReaderSpec { - fn buffer_size(&self) -> usize; - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result; -} - -impl BufferedReaderSpec for T -where - Self: Read, - T: ?Sized, -{ - #[inline] - default fn buffer_size(&self) -> usize { - 0 - } - - default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { - unreachable!("only called from specializations") - } -} - -impl BufferedReaderSpec for &[u8] { - fn buffer_size(&self) -> usize { - // prefer this specialization since the source "buffer" is all we'll ever need, - // even if it's small - usize::MAX - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let len = self.len(); - to.write_all(self)?; - *self = &self[len..]; - Ok(len as u64) - } -} - -impl BufferedReaderSpec for VecDeque { - fn buffer_size(&self) -> usize { - // prefer this specialization since the source "buffer" is all we'll ever need, - // even if it's small - usize::MAX - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let len = self.len(); - let (front, back) = self.as_slices(); - let bufs = &mut [IoSlice::new(front), IoSlice::new(back)]; - to.write_all_vectored(bufs)?; - self.clear(); - Ok(len as u64) - } -} - -impl BufferedReaderSpec for BufReader -where - Self: Read, - I: ?Sized, -{ - fn buffer_size(&self) -> usize { - self.capacity() - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let mut len = 0; - - loop { - // Hack: this relies on `impl Read for BufReader` always calling fill_buf - // if the buffer is empty, even for empty slices. - // It can't be called directly here since specialization prevents us - // from adding I: Read - match self.read(&mut []) { - Ok(_) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - let buf = self.buffer(); - if self.buffer().len() == 0 { - return Ok(len); - } - - // In case the writer side is a BufWriter then its write_all - // implements an optimization that passes through large - // buffers to the underlying writer. That code path is #[cold] - // but we're still avoiding redundant memcopies when doing - // a copy between buffered inputs and outputs. - to.write_all(buf)?; - len += buf.len() as u64; - self.discard_buffer(); - } - } -} - -/// Specialization of the read-write loop that either uses a stack buffer -/// or reuses the internal buffer of a BufWriter -trait BufferedWriterSpec: Write { - fn buffer_size(&self) -> usize; - - fn copy_from(&mut self, reader: &mut R) -> Result; -} - -impl BufferedWriterSpec for W { - #[inline] - default fn buffer_size(&self) -> usize { - 0 - } - - default fn copy_from(&mut self, reader: &mut R) -> Result { - stack_buffer_copy(reader, self) - } -} - -impl BufferedWriterSpec for BufWriter { - fn buffer_size(&self) -> usize { - self.capacity() - } - - fn copy_from(&mut self, reader: &mut R) -> Result { - if self.capacity() < DEFAULT_BUF_SIZE { - return stack_buffer_copy(reader, self); - } - - let mut len = 0; - let mut init = false; - - loop { - let buf = self.buffer_mut(); - let mut read_buf: BorrowedBuf<'_, u8> = buf.spare_capacity_mut().into(); - - if init { - // SAFETY: `init` is only true after `reader` initializes - // `read_buf`. See the comment about `flush_buf` below. - unsafe { read_buf.set_init() }; - } - - if read_buf.capacity() >= DEFAULT_BUF_SIZE { - let mut cursor = read_buf.unfilled(); - match reader.read_buf(cursor.reborrow()) { - Ok(()) => { - let bytes_read = cursor.written(); - - if bytes_read == 0 { - return Ok(len); - } - - init = read_buf.is_init(); - len += bytes_read as u64; - - // SAFETY: BorrowedBuf guarantees all of its filled bytes are init - unsafe { buf.set_len(buf.len() + bytes_read) }; - - // Read again if the buffer still has enough capacity, as BufWriter itself would do - // This will occur if the reader returns short reads - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } else { - // SAFETY: `flush_buf` will not de-initialize any elements of - // the spare capacity so we can remember `init` across this. - self.flush_buf()?; - } - } - } -} - -impl BufferedWriterSpec for Vec { - fn buffer_size(&self) -> usize { - cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) - } - - fn copy_from(&mut self, reader: &mut R) -> Result { - reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64")) - } -} - -fn stack_buffer_copy( - reader: &mut R, - writer: &mut W, -) -> Result { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE]; - let mut buf: BorrowedBuf<'_, u8> = buf.into(); - - let mut len = 0; - - loop { - match reader.read_buf(buf.unfilled()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - }; - - if buf.filled().is_empty() { - break; - } - - len += buf.filled().len() as u64; - writer.write_all(buf.filled())?; - buf.clear(); - } - - Ok(len) -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 572f0fb3e4611..203572c9067b6 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -314,12 +314,13 @@ pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ BufRead, BufReader, BufWriter, Bytes, Chain, Cursor, Empty, Error, ErrorKind, IntoInnerError, - LineWriter, Lines, Read, Repeat, Result, Seek, SeekFrom, Sink, Split, Take, Write, empty, + LineWriter, Lines, Read, Repeat, Result, Seek, SeekFrom, Sink, Split, Take, Write, copy, empty, repeat, sink, }; #[allow(unused_imports, reason = "only used by certain platforms")] pub(crate) use alloc_crate::io::{ - DEFAULT_BUF_SIZE, default_read_buf, default_read_vectored, default_write_vectored, + CopyState, DEFAULT_BUF_SIZE, SpecCopy, default_read_buf, default_read_vectored, + default_write_vectored, }; pub(crate) use alloc_crate::io::{ IoHandle, SpecReadByte, default_read_to_end, default_read_to_string, stream_len_default, @@ -331,21 +332,20 @@ pub use alloc_crate::io::{IoSlice, IoSliceMut}; pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; -pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "print_internals", issue = "none")] #[doc(hidden)] pub use self::stdio::{_eprint, _print}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::stdio::{ + Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout, +}; +pub(crate) use self::stdio::{attempt_print_to_stderr, cleanup}; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] pub use self::stdio::{set_output_capture, try_set_output_capture}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::{ - copy::copy, - stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, -}; mod buffered; -pub(crate) mod copy; +mod copy; mod cursor; mod error; mod impls; @@ -353,5 +353,3 @@ mod pipe; pub mod prelude; mod stdio; mod util; - -pub(crate) use stdio::cleanup; diff --git a/library/std/src/sys/io/kernel_copy/linux.rs b/library/std/src/sys/io/kernel_copy/linux.rs index 1c00d317f2a52..433fabc3f9733 100644 --- a/library/std/src/sys/io/kernel_copy/linux.rs +++ b/library/std/src/sys/io/kernel_copy/linux.rs @@ -48,12 +48,11 @@ use libc::sendfile as sendfile64; use libc::sendfile64; use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; -use super::CopyState; use crate::cmp::min; use crate::fs::{File, Metadata}; use crate::io::{ - BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock, - StdinLock, StdoutLock, Take, Write, + self, BufRead, BufReader, BufWriter, CopyState, Error, PipeReader, PipeWriter, Read, Result, + StderrLock, StdinLock, StdoutLock, Take, Write, }; use crate::mem::ManuallyDrop; use crate::net::TcpStream; @@ -70,12 +69,98 @@ use crate::sys::weak::syscall; #[cfg(test)] mod tests; -pub fn kernel_copy( - read: &mut R, - write: &mut W, -) -> Result { - let copier = Copier { read, write }; - SpecCopy::copy(copier) +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for File { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for &File { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for TcpStream { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for &TcpStream { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for UnixStream { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for &UnixStream { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for PipeReader { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for &PipeReader { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for ChildStdout { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for ChildStderr { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +#[doc(hidden)] +#[unstable(feature = "io_copy_internals", reason = "implementation detail", issue = "none")] +impl io::SpecCopy for StdinLock<'_> { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } +} + +impl io::SpecCopy for CachedFileMetadata { + fn copy(read: &mut R, write: &mut W) -> Result { + SpecCopy::copy(Copier { read, write }) + } } /// This type represents either the inferred `FileType` of a `RawFd` based on the source diff --git a/library/std/src/sys/io/kernel_copy/mod.rs b/library/std/src/sys/io/kernel_copy/mod.rs index a89279412cf7f..bb71fde631cce 100644 --- a/library/std/src/sys/io/kernel_copy/mod.rs +++ b/library/std/src/sys/io/kernel_copy/mod.rs @@ -1,23 +1,6 @@ -pub enum CopyState { - #[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))] - Ended(u64), - Fallback(u64), -} - cfg_select! { any(target_os = "linux", target_os = "android") => { mod linux; - pub use linux::kernel_copy; - } - _ => { - use crate::io::{Result, Read, Write}; - - pub fn kernel_copy(_reader: &mut R, _writer: &mut W) -> Result - where - R: Read, - W: Write, - { - Ok(CopyState::Fallback(0)) - } } + _ => { } } diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 02a180f4bc295..efca981eb813a 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -45,4 +45,3 @@ pub use error::errno_location; pub use error::set_errno; pub use error::{decode_error_kind, errno, error_string, is_interrupted}; pub use is_terminal::is_terminal; -pub use kernel_copy::{CopyState, kernel_copy};