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/std/src/io/copy.rs b/library/alloc/src/io/copy/generic.rs similarity index 75% rename from library/std/src/io/copy.rs rename to library/alloc/src/io/copy/generic.rs index 43c6f71357e61..7dbc56e464732 100644 --- a/library/std/src/io/copy.rs +++ b/library/alloc/src/io/copy/generic.rs @@ -1,80 +1,19 @@ -use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write}; -use crate::alloc::Allocator; -use crate::cmp; +use core::cmp; +use core::mem::MaybeUninit; + +#[cfg(not(no_global_oom_handling))] 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) - } - } -} +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. -fn generic_copy(reader: &mut R, writer: &mut W) -> Result +pub(super) fn generic_copy(reader: &mut R, writer: &mut W) -> Result where R: Read, W: Write, @@ -128,6 +67,7 @@ impl BufferedReaderSpec for &[u8] { } } +#[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, 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..44d780292317f 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,10 +1,188 @@ //! Traits, helpers, and type definitions for core I/O functionality. +//! +//! The `io` module contains a number of common things you'll need +//! when doing input and output. The most core part of this module is +//! the [`Read`] and [`Write`] traits, which provide the +//! most general interface for reading and writing input and output. +//! +//! ## Read and Write +//! +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number +//! of other types, and you can implement them for your types too. As such, +//! you'll see a few different types of I/O throughout the documentation in +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For +//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on +//! [`File`]s: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! Ok(()) +//! } +//! ``` +//! +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a +//! nickname: readers and writers. So you'll sometimes see 'a reader' instead +//! of 'a type that implements the [`Read`] trait'. Much easier! +//! +//! ## Seek and BufRead +//! +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is +//! coming from: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::SeekFrom; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // skip to the last 10 bytes of the file +//! f.seek(SeekFrom::End(-10))?; +//! +//! // read up to 10 bytes +//! let n = f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", &buffer[..n]); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but +//! to show it off, we'll need to talk about buffers in general. Keep reading! +//! +//! ## BufReader and BufWriter +//! +//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be +//! making near-constant calls to the operating system. To help with this, +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap +//! readers and writers. The wrapper uses a buffer, reducing the number of +//! calls and providing nicer methods for accessing exactly what you want. +//! +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra +//! methods to any reader: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let mut reader = BufReader::new(f); +//! let mut buffer = String::new(); +//! +//! // read a line into buffer +//! reader.read_line(&mut buffer)?; +//! +//! println!("{buffer}"); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write`][`Write::write`]: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufWriter; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::create("foo.txt")?; +//! { +//! let mut writer = BufWriter::new(f); +//! +//! // write a byte to the buffer +//! writer.write(&[42])?; +//! +//! } // the buffer is flushed once writer goes out of scope +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Iterator types +//! +//! A large number of the structures provided by `std::io` are for various +//! ways of iterating over I/O. For example, [`Lines`] is used to split over +//! lines: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let reader = BufReader::new(f); +//! +//! for line in reader.lines() { +//! println!("{}", line?); +//! } +//! Ok(()) +//! } +//! ``` +//! +//! ## io::Result +//! +//! Last, but certainly not least, is [`io::Result`]. This type is used +//! as the return type of many `std::io` functions that can cause an error, and +//! can be returned from your own functions as well. Many of the examples in this +//! module use the [`?` operator]: +//! +//! ```no_run +//! use std::io; +//! +//! # #[allow(dead_code)] +//! fn read_input() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input)?; +//! +//! println!("You typed: {}", input.trim()); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is +//! to read the line and print it, so we use `()`. +//! +//! [`File`]: ../../std/fs/struct.File.html +//! [`TcpStream`]: ../../std/net/struct.TcpStream.html +//! [`Vec`]: crate::vec::Vec +//! [`io::Result`]: self::Result +//! [`?` operator]: ../../book/appendix-02-operators.html mod buf_read; mod buffered; +mod copy; mod cursor; mod error; mod impls; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod prelude; mod read; mod util; @@ -35,12 +213,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/alloc/src/io/prelude.rs b/library/alloc/src/io/prelude.rs new file mode 100644 index 0000000000000..86ae040d1d6f6 --- /dev/null +++ b/library/alloc/src/io/prelude.rs @@ -0,0 +1,12 @@ +//! The I/O Prelude. +//! +//! The purpose of this module is to alleviate imports of many common I/O traits +//! by adding a glob import to the top of I/O heavy modules: +//! +//! ``` +//! # #![allow(unused_imports)] +//! use std::io::prelude::*; +//! ``` + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::io::{BufRead, Read, Seek, Write}; diff --git a/library/std/src/io/impls/tests.rs b/library/alloctests/benches/io.rs similarity index 59% rename from library/std/src/io/impls/tests.rs rename to library/alloctests/benches/io.rs index d1cd84a67ada5..f2e3f2cd3c998 100644 --- a/library/std/src/io/impls/tests.rs +++ b/library/alloctests/benches/io.rs @@ -1,4 +1,4 @@ -use crate::io::prelude::*; +use std::io::prelude::*; #[bench] fn bench_read_slice(b: &mut test::Bencher) { @@ -55,3 +55,26 @@ fn bench_write_vec(b: &mut test::Bencher) { } }) } + +#[bench] +#[cfg(unix)] +#[cfg_attr(target_os = "emscripten", ignore)] // no /dev +fn bench_copy_buf_reader(b: &mut test::Bencher) { + use std::fs::{File, OpenOptions}; + + let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); + // use dyn to avoid specializations unrelated to readbuf + let dyn_in = &mut file_in as &mut dyn Read; + let mut reader = std::io::BufReader::with_capacity(256 * 1024, dyn_in.take(0)); + let mut writer = + OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); + + const BYTES: u64 = 1024 * 1024; + + b.bytes = BYTES; + + b.iter(|| { + reader.get_mut().set_limit(BYTES); + std::io::copy(&mut reader, &mut writer).unwrap() + }); +} diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 4b7139d943593..974b389a765d5 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -12,6 +12,7 @@ extern crate test; mod binary_heap; mod btree; +mod io; mod linked_list; mod slice; mod str; diff --git a/library/std/src/io/buffered/tests.rs b/library/alloctests/tests/io/buffered.rs similarity index 98% rename from library/std/src/io/buffered/tests.rs rename to library/alloctests/tests/io/buffered.rs index ff4585a60cae9..0abaa63870e15 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/alloctests/tests/io/buffered.rs @@ -1,10 +1,14 @@ -use crate::io::prelude::*; -use crate::io::{ - self, BorrowedBuf, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom, +//! Tests for buffering wrappers for I/O traits + +use alloc::io::{ + self, BorrowedBuf, BufRead, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, Read, Seek, + SeekFrom, Write, }; -use crate::mem::MaybeUninit; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::{panic, thread}; +use core::mem::MaybeUninit; +use core::sync::atomic::{AtomicUsize, Ordering}; +use std::{panic, thread}; + +extern crate test; /// A dummy reader intended at testing short-reads propagation. pub struct ShortReader { @@ -488,7 +492,7 @@ fn dont_panic_in_drop_on_panicked_flush() { } #[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_in_write_doesnt_flush_in_drop() { static WRITES: AtomicUsize = AtomicUsize::new(0); @@ -504,12 +508,11 @@ fn panic_in_write_doesnt_flush_in_drop() { } } - thread::spawn(|| { + panic::catch_unwind(panic::AssertUnwindSafe(|| { let mut writer = BufWriter::new(PanicWriter); let _ = writer.write(b"hello world"); let _ = writer.flush(); - }) - .join() + })) .unwrap_err(); assert_eq!(WRITES.load(Ordering::SeqCst), 1); @@ -681,7 +684,7 @@ fn line_vectored() { #[test] fn line_vectored_partial_and_errors() { - use crate::collections::VecDeque; + use alloc::collections::VecDeque; enum Call { Write { inputs: Vec<&'static [u8]>, output: io::Result }, @@ -1150,7 +1153,7 @@ struct WriteRecorder { impl Write for WriteRecorder { fn write(&mut self, buf: &[u8]) -> io::Result { - use crate::str::from_utf8; + use core::str::from_utf8; self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string())); Ok(buf.len()) @@ -1183,7 +1186,7 @@ fn single_formatted_write() { fn bufreader_full_initialize() { struct OneByteReader; impl Read for OneByteReader { - fn read(&mut self, buf: &mut [u8]) -> crate::io::Result { + fn read(&mut self, buf: &mut [u8]) -> alloc::io::Result { if buf.len() > 0 { buf[0] = 0; Ok(1) @@ -1206,7 +1209,7 @@ fn bufreader_full_initialize() { /// This is a regression test for https://github.com/rust-lang/rust/issues/127584. #[test] fn bufwriter_aliasing() { - use crate::io::{BufWriter, Cursor}; + use alloc::io::{BufWriter, Cursor}; let mut v = vec![0; 1024]; let c = Cursor::new(&mut v); let w = BufWriter::new(Box::new(c)); diff --git a/library/std/src/io/copy/tests.rs b/library/alloctests/tests/io/copy.rs similarity index 74% rename from library/std/src/io/copy/tests.rs rename to library/alloctests/tests/io/copy.rs index 7bdba3a04416e..485deeaea80e7 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/alloctests/tests/io/copy.rs @@ -1,7 +1,6 @@ -use crate::cmp::{max, min}; -use crate::collections::VecDeque; -use crate::io; -use crate::io::*; +use alloc::collections::VecDeque; +use alloc::io::{self, *}; +use core::cmp::{max, min}; #[test] fn copy_copies() { @@ -65,7 +64,7 @@ fn copy_specializes_bufreader() { let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); let mut sink = Vec::new(); - assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!(io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); assert_eq!(source.as_slice(), sink.as_slice()); let buf_sz = 71 * 1024; @@ -73,7 +72,7 @@ fn copy_specializes_bufreader() { let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); let mut sink = WriteObserver { observed_buffer: 0 }; - assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!(io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); assert_eq!( sink.observed_buffer, buf_sz, "expected a large buffer to be provided to the writer" @@ -117,32 +116,3 @@ fn copy_specializes_from_slice() { assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); assert_eq!(60 * 1024, sink.observed_buffer); } - -#[cfg(unix)] -mod io_benches { - use test::Bencher; - - use crate::fs::{File, OpenOptions}; - use crate::io::BufReader; - use crate::io::prelude::*; - - #[bench] - #[cfg_attr(target_os = "emscripten", ignore)] // no /dev - fn bench_copy_buf_reader(b: &mut Bencher) { - let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); - // use dyn to avoid specializations unrelated to readbuf - let dyn_in = &mut file_in as &mut dyn Read; - let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0)); - let mut writer = - OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); - - const BYTES: u64 = 1024 * 1024; - - b.bytes = BYTES; - - b.iter(|| { - reader.get_mut().set_limit(BYTES); - crate::io::copy(&mut reader, &mut writer).unwrap() - }); - } -} diff --git a/library/std/src/io/cursor/tests.rs b/library/alloctests/tests/io/cursor.rs similarity index 99% rename from library/std/src/io/cursor/tests.rs rename to library/alloctests/tests/io/cursor.rs index d7c203c297fe6..5e863f29af53c 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/alloctests/tests/io/cursor.rs @@ -1,5 +1,6 @@ -use crate::io::prelude::*; -use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom}; +use alloc::io::{Cursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; + +extern crate test; #[test] fn test_vec_writer() { diff --git a/library/std/src/io/tests.rs b/library/alloctests/tests/io/mod.rs similarity index 98% rename from library/std/src/io/tests.rs rename to library/alloctests/tests/io/mod.rs index 3b4f871268cd1..712e322d79f1c 100644 --- a/library/std/src/io/tests.rs +++ b/library/alloctests/tests/io/mod.rs @@ -1,7 +1,16 @@ -use super::{BorrowedBuf, Cursor, SeekFrom, repeat}; -use crate::cmp::{self, min}; -use crate::io::{self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, Write}; -use crate::mem::MaybeUninit; +mod buffered; +mod copy; +mod cursor; +mod util; + +use alloc::io::{ + self, BorrowedBuf, BufRead, BufReader, Cursor, DEFAULT_BUF_SIZE, IoSlice, Read, Seek, SeekFrom, + Write, repeat, +}; +use core::cmp::{self, min}; +use core::mem::MaybeUninit; + +extern crate test; #[test] fn read_until() { @@ -270,7 +279,7 @@ fn chain_bufread() { #[test] fn chain_splitted_char() { let chain = b"\xc3".chain(b"\xa9".as_slice()); - assert_eq!(crate::io::read_to_string(chain).unwrap(), "é"); + assert_eq!(alloc::io::read_to_string(chain).unwrap(), "é"); let mut chain = b"\xc3".chain(b"\xa9\n".as_slice()); let mut buf = String::new(); @@ -360,7 +369,7 @@ fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { let mut lr = repeat(1).take(10000000); let mut vec = Vec::with_capacity(1024); - super::default_read_to_end(&mut lr, &mut vec, None) + alloc::io::default_read_to_end(&mut lr, &mut vec, None) }); } diff --git a/library/std/src/io/util/tests.rs b/library/alloctests/tests/io/util.rs similarity index 96% rename from library/std/src/io/util/tests.rs rename to library/alloctests/tests/io/util.rs index ed1d6891577da..52e0c3013dde7 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/alloctests/tests/io/util.rs @@ -1,9 +1,9 @@ -use crate::fmt; -use crate::io::prelude::*; -use crate::io::{ - BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink, +use alloc::io::{ + BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, Write, + empty, repeat, sink, }; -use crate::mem::MaybeUninit; +use core::fmt; +use core::mem::MaybeUninit; struct ErrorDisplay; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 96dcde71fc071..2d0846f0b8976 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -2,22 +2,30 @@ #![allow(internal_features)] #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(binary_heap_drain_sorted)] #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_pop_if)] +#![feature(borrowed_buf_init)] +#![feature(buf_read_has_data_left)] +#![feature(can_vector)] #![feature(casefold)] #![feature(const_btree_len)] #![feature(const_heap)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(cow_is_borrowed)] +#![feature(cursor_split)] #![feature(deque_extend_front)] #![feature(downcast_unchecked)] #![feature(drain_keep_rest)] #![feature(exact_size_is_empty)] #![feature(hashmap_internals)] #![feature(inplace_iteration)] +#![feature(io_const_error)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] #![feature(iter_next_chunk)] @@ -27,6 +35,9 @@ #![feature(map_try_insert)] #![feature(pattern)] #![feature(ptr_cast_slice)] +#![feature(read_buf)] +#![feature(seek_io_take_position)] +#![feature(seek_stream_len)] #![feature(slice_partial_sort_unstable)] #![feature(slice_partition_dedup)] #![feature(slice_ptr_get)] @@ -45,6 +56,7 @@ #![feature(vec_deque_retain_range)] #![feature(vec_peek_mut)] #![feature(vec_try_remove)] +#![feature(write_all_vectored)] // tidy-alphabetical-end extern crate alloc; @@ -64,6 +76,7 @@ mod const_fns; mod cow_str; mod fmt; mod heap; +mod io; mod linked_list; mod misc_tests; mod num; diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 0134540ae86c8..a44d271535a9e 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -5,6 +5,8 @@ mod cursor; mod error; mod impls; mod io_slice; +#[unstable(feature = "core_io", issue = "154046")] +pub mod prelude; mod seek; mod size_hint; mod util; diff --git a/library/core/src/io/prelude.rs b/library/core/src/io/prelude.rs new file mode 100644 index 0000000000000..15dacaa3a4fa0 --- /dev/null +++ b/library/core/src/io/prelude.rs @@ -0,0 +1,12 @@ +//! The I/O Prelude. +//! +//! The purpose of this module is to alleviate imports of many common I/O traits +//! by adding a glob import to the top of I/O heavy modules: +//! +//! ``` +//! # #![allow(unused_imports)] +//! use std::io::prelude::*; +//! ``` + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::io::{Seek, Write}; diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs deleted file mode 100644 index 1d09ff7d8dc1c..0000000000000 --- a/library/std/src/io/buffered/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Buffering wrappers for I/O traits - -#[cfg(test)] -mod tests; diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs deleted file mode 100644 index 87c2771955a9d..0000000000000 --- a/library/std/src/io/cursor.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(test)] -mod tests; diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs deleted file mode 100644 index 87c2771955a9d..0000000000000 --- a/library/std/src/io/impls.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(test)] -mod tests; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 572f0fb3e4611..c0ed06d5311bc 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -294,9 +294,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; @@ -314,12 +311,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,27 +329,19 @@ 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 cursor; mod error; -mod impls; mod pipe; pub mod prelude; mod stdio; -mod util; - -pub(crate) use stdio::cleanup; diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs deleted file mode 100644 index 87c2771955a9d..0000000000000 --- a/library/std/src/io/util.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(test)] -mod tests; 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};