Skip to content

Commit

Permalink
Auto merge of rust-lang#130803 - cuviper:file-buffered, r=joshtriplett
Browse files Browse the repository at this point in the history
Add `File` constructors that return files wrapped with a buffer

In addition to the light convenience, these are intended to raise visibility that buffering is something you should consider when opening a file, since unbuffered I/O is a common performance footgun to Rust newcomers.

ACP: rust-lang/libs-team#446
Tracking Issue: rust-lang#130804
  • Loading branch information
bors committed Sep 25, 2024
2 parents 1b5aa96 + 458537e commit 4c62024
Show file tree
Hide file tree
Showing 33 changed files with 151 additions and 41 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/facts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::error::Error;
use std::fmt::Debug;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::io::Write;
use std::path::Path;

use polonius_engine::{AllFacts as PoloniusFacts, Atom};
Expand Down Expand Up @@ -127,7 +127,7 @@ impl<'w> FactWriter<'w> {
T: FactRow,
{
let file = &self.dir.join(file_name);
let mut file = BufWriter::new(File::create(file)?);
let mut file = File::create_buffered(file)?;
for row in rows {
row.write(&mut file, self.location_table)?;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(file_buffered)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(rustc_attrs)]
Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,8 +808,7 @@ struct ThinLTOKeysMap {
impl ThinLTOKeysMap {
fn save_to_file(&self, path: &Path) -> io::Result<()> {
use std::io::Write;
let file = File::create(path)?;
let mut writer = io::BufWriter::new(file);
let mut writer = File::create_buffered(path)?;
// The entries are loaded back into a hash map in `load_from_file()`, so
// the order in which we write them to file here does not matter.
for (module, key) in &self.keys {
Expand All @@ -821,8 +820,8 @@ impl ThinLTOKeysMap {
fn load_from_file(path: &Path) -> io::Result<Self> {
use std::io::BufRead;
let mut keys = BTreeMap::default();
let file = File::open(path)?;
for line in io::BufReader::new(file).lines() {
let file = File::open_buffered(path)?;
for line in file.lines() {
let line = line?;
let mut split = line.split(' ');
let module = split.next().unwrap();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![feature(assert_matches)]
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(file_buffered)]
#![feature(hash_raw_entry)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::path::{Path, PathBuf};
use std::{env, iter, mem, str};
use std::{env, io, iter, mem, str};

use cc::windows_registry;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
Expand Down Expand Up @@ -754,7 +753,7 @@ impl<'a> Linker for GccLinker<'a> {
if self.sess.target.is_like_osx {
// Write a plain, newline-separated list of symbols
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;
for sym in symbols {
debug!(" _{sym}");
writeln!(f, "_{sym}")?;
Expand All @@ -765,7 +764,7 @@ impl<'a> Linker for GccLinker<'a> {
}
} else if is_windows {
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;

// .def file similar to MSVC one but without LIBRARY section
// because LD doesn't like when it's empty
Expand All @@ -781,7 +780,7 @@ impl<'a> Linker for GccLinker<'a> {
} else {
// Write an LD version script
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;
writeln!(f, "{{")?;
if !symbols.is_empty() {
writeln!(f, " global:")?;
Expand Down Expand Up @@ -1059,7 +1058,7 @@ impl<'a> Linker for MsvcLinker<'a> {

let path = tmpdir.join("lib.def");
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;

// Start off with the standard module name header and then go
// straight to exports.
Expand Down Expand Up @@ -1648,7 +1647,7 @@ impl<'a> Linker for AixLinker<'a> {
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
let path = tmpdir.join("list.exp");
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;
// FIXME: use llvm-nm to generate export list.
for symbol in symbols {
debug!(" _{symbol}");
Expand Down Expand Up @@ -1961,7 +1960,7 @@ impl<'a> Linker for BpfLinker<'a> {
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
let path = tmpdir.join("symbols");
let res: io::Result<()> = try {
let mut f = BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;
for sym in symbols {
writeln!(f, "{sym}")?;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(negative_impls)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#![feature(cfg_match)]
#![feature(core_intrinsics)]
#![feature(extend_one)]
#![feature(file_buffered)]
#![feature(hash_raw_entry)]
#![feature(macro_metavar_expr)]
#![feature(map_try_insert)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::env::var_os;
use std::fs::File;
use std::io::BufWriter;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};

Expand Down Expand Up @@ -33,7 +32,7 @@ impl<O: ForestObligation> ObligationForest<O> {

let file_path = dir.as_ref().join(format!("{counter:010}_{description}.gv"));

let mut gv_file = BufWriter::new(File::create(file_path).unwrap());
let mut gv_file = File::create_buffered(file_path).unwrap();

dot::render(&self, &mut gv_file).unwrap();
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_incremental/src/assert_dep_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

use std::env;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::io::Write;

use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::graph::implementation::{Direction, INCOMING, NodeIndex, OUTGOING};
Expand Down Expand Up @@ -245,7 +245,7 @@ fn dump_graph(query: &DepGraphQuery) {
{
// dump a .txt file with just the edges:
let txt_path = format!("{path}.txt");
let mut file = BufWriter::new(File::create(&txt_path).unwrap());
let mut file = File::create_buffered(&txt_path).unwrap();
for (source, target) in &edges {
write!(file, "{source:?} -> {target:?}\n").unwrap();
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_incremental/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![deny(missing_docs)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(file_buffered)]
#![feature(rustdoc_internals)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// tidy-alphabetical-start
#![feature(decl_macro)]
#![feature(file_buffered)]
#![feature(let_chains)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
write_deps_to_file(&mut file)?;
}
OutFileName::Real(ref path) => {
let mut file = BufWriter::new(fs::File::create(path)?);
let mut file = fs::File::create_buffered(path)?;
write_deps_to_file(&mut file)?;
}
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_metadata/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
}

pub fn copy_to_stdout(from: &Path) -> io::Result<()> {
let file = fs::File::open(from)?;
let mut reader = io::BufReader::new(file);
let mut reader = fs::File::open_buffered(from)?;
let mut stdout = io::stdout();
io::copy(&mut reader, &mut stdout)?;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(extract_if)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(let_chains)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#![feature(discriminant_kind)]
#![feature(extern_types)]
#![feature(extract_if)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(iter_from_coroutine)]
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,9 @@ pub fn create_dump_file<'tcx>(
)
})?;
}
Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| {
Ok(fs::File::create_buffered(&file_path).map_err(|e| {
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
})?))
})?)
}

///////////////////////////////////////////////////////////////////////////
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_mir_dataflow/src/framework/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ where
A::Domain: DebugWithContext<A>,
{
use std::fs;
use std::io::{self, Write};
use std::io::Write;

let def_id = body.source.def_id();
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
Expand All @@ -281,8 +281,7 @@ where
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let f = fs::File::create(&path)?;
io::BufWriter::new(f)
fs::File::create_buffered(&path)?
}

None if dump_enabled(tcx, A::NAME, def_id) => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_dataflow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(exact_size_is_empty)]
#![feature(file_buffered)]
#![feature(let_chains)]
#![feature(try_blocks)]
#![warn(unreachable_pub)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/dump_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> {
write_mir_pretty(tcx, None, &mut f)?;
}
OutFileName::Real(path) => {
let mut f = io::BufWriter::new(File::create(&path)?);
let mut f = File::create_buffered(&path)?;
write_mir_pretty(tcx, None, &mut f)?;
if tcx.sess.opts.json_artifact_notifications {
tcx.dcx().emit_artifact_notification(&path, "mir");
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![feature(box_patterns)]
#![feature(const_type_name)]
#![feature(cow_is_borrowed)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(let_chains)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_monomorphize/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// tidy-alphabetical-start
#![feature(array_windows)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![warn(unreachable_pub)]
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_monomorphize/src/partitioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
use std::cmp;
use std::collections::hash_map::Entry;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::io::Write;
use std::path::{Path, PathBuf};

use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
Expand Down Expand Up @@ -1243,8 +1243,7 @@ fn dump_mono_items_stats<'tcx>(
let ext = format.extension();
let filename = format!("{crate_name}.mono_items.{ext}");
let output_path = output_directory.join(&filename);
let file = File::create(&output_path)?;
let mut file = BufWriter::new(file);
let mut file = File::create_buffered(&output_path)?;

// Gather instantiated mono items grouped by def_id
let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default();
Expand Down
77 changes: 77 additions & 0 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,44 @@ impl File {
OpenOptions::new().read(true).open(path.as_ref())
}

/// Attempts to open a file in read-only mode with buffering.
///
/// See the [`OpenOptions::open`] method, the [`BufReader`][io::BufReader] type,
/// and the [`BufRead`][io::BufRead] trait for more details.
///
/// If you only need to read the entire file contents,
/// consider [`std::fs::read()`][self::read] or
/// [`std::fs::read_to_string()`][self::read_to_string] instead.
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// # Examples
///
/// ```no_run
/// #![feature(file_buffered)]
/// use std::fs::File;
/// use std::io::BufRead;
///
/// fn main() -> std::io::Result<()> {
/// let mut f = File::open_buffered("foo.txt")?;
/// assert!(f.capacity() > 0);
/// for (line, i) in f.lines().zip(1..) {
/// println!("{i:6}: {}", line?);
/// }
/// Ok(())
/// }
/// ```
#[unstable(feature = "file_buffered", issue = "130804")]
pub fn open_buffered<P: AsRef<Path>>(path: P) -> io::Result<io::BufReader<File>> {
// Allocate the buffer *first* so we don't affect the filesystem otherwise.
let buffer = io::BufReader::<Self>::try_new_buffer()?;
let file = File::open(path)?;
Ok(io::BufReader::with_buffer(file, buffer))
}

/// Opens a file in write-only mode.
///
/// This function will create a file if it does not exist,
Expand Down Expand Up @@ -404,6 +442,45 @@ impl File {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}

/// Opens a file in write-only mode with buffering.
///
/// This function will create a file if it does not exist,
/// and will truncate it if it does.
///
/// Depending on the platform, this function may fail if the
/// full directory path does not exist.
///
/// See the [`OpenOptions::open`] method and the
/// [`BufWriter`][io::BufWriter] type for more details.
///
/// See also [`std::fs::write()`][self::write] for a simple function to
/// create a file with some given data.
///
/// # Examples
///
/// ```no_run
/// #![feature(file_buffered)]
/// use std::fs::File;
/// use std::io::Write;
///
/// fn main() -> std::io::Result<()> {
/// let mut f = File::create_buffered("foo.txt")?;
/// assert!(f.capacity() > 0);
/// for i in 0..100 {
/// writeln!(&mut f, "{i}")?;
/// }
/// f.flush()?;
/// Ok(())
/// }
/// ```
#[unstable(feature = "file_buffered", issue = "130804")]
pub fn create_buffered<P: AsRef<Path>>(path: P) -> io::Result<io::BufWriter<File>> {
// Allocate the buffer *first* so we don't affect the filesystem otherwise.
let buffer = io::BufWriter::<Self>::try_new_buffer()?;
let file = File::create(path)?;
Ok(io::BufWriter::with_buffer(file, buffer))
}

/// Creates a new file in read-write mode; error if the file exists.
///
/// This function will create a file if it does not exist, or return an error if it does. This
Expand Down
Loading

0 comments on commit 4c62024

Please sign in to comment.