Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions library/alloc/src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +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<T>`]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<T>`]: 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;

Expand Down
12 changes: 12 additions & 0 deletions library/alloc/src/io/prelude.rs
Original file line number Diff line number Diff line change
@@ -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};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::io::prelude::*;
use std::io::prelude::*;

#[bench]
fn bench_read_slice(b: &mut test::Bencher) {
Expand Down Expand Up @@ -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()
});
}
1 change: 1 addition & 0 deletions library/alloctests/benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate test;

mod binary_heap;
mod btree;
mod io;
mod linked_list;
mod slice;
mod str;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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")]
Comment thread
bushrat011899 marked this conversation as resolved.
fn panic_in_write_doesnt_flush_in_drop() {
static WRITES: AtomicUsize = AtomicUsize::new(0);

Expand All @@ -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);
Expand Down Expand Up @@ -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<usize> },
Expand Down Expand Up @@ -1150,7 +1153,7 @@ struct WriteRecorder {

impl Write for WriteRecorder {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
use crate::str::from_utf8;
use core::str::from_utf8;

self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string()));
Ok(buf.len())
Expand Down Expand Up @@ -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<usize> {
fn read(&mut self, buf: &mut [u8]) -> alloc::io::Result<usize> {
if buf.len() > 0 {
buf[0] = 0;
Ok(1)
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -65,15 +64,15 @@ 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;
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");

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"
Expand Down Expand Up @@ -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()
});
}
}
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
Loading
Loading