diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 5c043240daba8..44d780292317f 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,4 +1,179 @@ //! 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; @@ -6,6 +181,8 @@ mod copy; mod cursor; mod error; mod impls; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod prelude; mod read; mod util; 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 7ccc8d9c6a115..eb9ea287d950f 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -2,23 +2,31 @@ #![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_cmp)] #![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)] @@ -28,6 +36,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)] @@ -48,6 +59,7 @@ #![feature(vec_deque_retain_range)] #![feature(vec_peek_mut)] #![feature(vec_try_remove)] +#![feature(write_all_vectored)] // tidy-alphabetical-end extern crate alloc; @@ -67,6 +79,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/copy.rs b/library/std/src/io/copy.rs deleted file mode 100644 index 87c2771955a9d..0000000000000 --- a/library/std/src/io/copy.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[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 203572c9067b6..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; @@ -344,12 +341,7 @@ pub(crate) use self::stdio::{attempt_print_to_stderr, cleanup}; #[doc(no_inline, hidden)] pub use self::stdio::{set_output_capture, try_set_output_capture}; -mod buffered; -mod copy; -mod cursor; mod error; -mod impls; mod pipe; pub mod prelude; mod stdio; -mod util; 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;